From d964eae817b538c532996af0b41993d128fa5a5c Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Thu, 24 Apr 2008 17:49:03 +0000 Subject: QPID-832 : Fix eol-style git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@651325 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/exchange/FanoutExchange.java | 480 +-- .../qpid/server/handler/AccessRequestHandler.java | 130 +- .../qpid/server/handler/BasicGetMethodHandler.java | 202 +- .../handler/BasicRecoverSyncMethodHandler.java | 150 +- .../qpid/server/handler/QueuePurgeHandler.java | 242 +- .../server/handler/ServerMethodDispatcherImpl.java | 1132 +++---- .../handler/ServerMethodDispatcherImpl_0_9.java | 328 +- .../handler/ServerMethodDispatcherImpl_8_0.java | 172 +- .../server/handler/UnexpectedMethodException.java | 66 +- .../server/output/ProtocolOutputConverter.java | 114 +- .../output/ProtocolOutputConverterRegistry.java | 122 +- .../amqp0_8/ProtocolOutputConverterImpl.java | 570 ++-- .../amqp0_9/ProtocolOutputConverterImpl.java | 794 ++--- .../protocol/AMQNoMethodHandlerException.java | 92 +- .../protocol/UnknnownMessageTypeException.java | 92 +- .../qpid/server/queue/NotificationCheck.java | 276 +- .../server/queue/QueueNotificationListener.java | 54 +- .../server/virtualhost/ManagedVirtualHost.java | 88 +- .../server/virtualhost/VirtualHostRegistry.java | 140 +- .../org/apache/qpid/sasl/ClientFactoryImpl.java | 686 ++-- .../java/org/apache/qpid/sasl/CramMD5Client.java | 694 ++-- .../java/org/apache/qpid/sasl/PlainClient.java | 550 ++-- .../main/java/org/apache/qpid/sasl/Provider.java | 122 +- .../test/integration/client/ConnectionTest.java | 132 +- .../org/apache/qpid/client/AMQSessionAdapter.java | 52 +- .../qpid/client/AMQUndefinedDestination.java | 80 +- .../org/apache/qpid/client/CustomJMSXProperty.java | 132 +- .../apache/qpid/client/TemporaryDestination.java | 76 +- .../qpid/client/failover/FailoverNoopSupport.java | 150 +- .../failover/FailoverProtectedOperation.java | 98 +- .../qpid/client/failover/FailoverRetrySupport.java | 270 +- .../handler/AccessRequestOkMethodHandler.java | 72 +- .../client/handler/ClientMethodDispatcherImpl.java | 1056 +++--- .../handler/ClientMethodDispatcherImpl_0_9.java | 310 +- .../handler/ClientMethodDispatcherImpl_8_0.java | 170 +- .../client/message/AbstractBytesTypedMessage.java | 1604 ++++----- .../qpid/client/message/JMSHeaderAdapter.java | 1104 +++---- .../state/AMQMethodNotImplementedException.java | 64 +- .../src/main/java/org/apache/qpid/jms/Message.java | 56 +- .../test/unit/basic/InvalidDestinationTest.java | 208 +- .../client/temporaryqueue/TemporaryQueueTest.java | 442 +-- .../qpid/test/unit/close/CloseBeforeAckTest.java | 288 +- .../qpid/test/unit/message/JMSDestinationTest.java | 178 +- .../org/apache/qpid/AMQUnknownExchangeType.java | 86 +- .../org/apache/qpid/framing/AMQMethodBodyImpl.java | 192 +- .../qpid/framing/AMQMethodBodyInstanceFactory.java | 60 +- .../org/apache/qpid/framing/AMQMethodFactory.java | 180 +- .../main/java/org/apache/qpid/framing/AMQType.java | 1590 ++++----- .../java/org/apache/qpid/framing/AMQTypeMap.java | 96 +- .../org/apache/qpid/framing/AMQTypedValue.java | 192 +- .../framing/CommonContentHeaderProperties.java | 162 +- .../qpid/framing/SmallCompositeAMQDataBlock.java | 196 +- .../qpid/framing/VersionSpecificRegistry.java | 396 +-- .../abstraction/AbstractMethodConverter.java | 94 +- .../qpid/framing/abstraction/ContentChunk.java | 64 +- .../framing/abstraction/MessagePublishInfo.java | 76 +- .../abstraction/MessagePublishInfoConverter.java | 64 +- .../ProtocolVersionMethodConverter.java | 64 +- .../qpid/framing/amqp_0_9/AMQMethodBody_0_9.java | 418 +-- .../qpid/framing/amqp_0_9/MethodConverter_0_9.java | 344 +- .../qpid/framing/amqp_8_0/AMQMethodBody_8_0.java | 418 +-- .../qpid/framing/amqp_8_0/MethodConverter_8_0.java | 302 +- .../protocol/AMQVersionAwareProtocolSession.java | 114 +- .../apache/qpid/protocol/ProtocolVersionAware.java | 106 +- .../org/apache/qpid/util/CommandLineParser.java | 1378 ++++---- .../main/java/org/apache/qpid/util/FileUtils.java | 390 +-- .../org/apache/qpid/util/PrettyPrintingUtils.java | 150 +- .../java/org/apache/qpid/util/PropertiesUtils.java | 400 +-- .../java/org/apache/qpid/util/ReflectionUtils.java | 456 +-- .../apache/qpid/util/ReflectionUtilsException.java | 88 +- .../util/concurrent/AlreadyUnblockedException.java | 26 +- .../qpid/util/concurrent/BatchSynchQueue.java | 202 +- .../qpid/util/concurrent/BatchSynchQueueBase.java | 1626 ++++----- .../apache/qpid/util/concurrent/BooleanLatch.java | 214 +- .../org/apache/qpid/util/concurrent/Capacity.java | 28 +- .../apache/qpid/util/concurrent/SynchBuffer.java | 58 +- .../qpid/util/concurrent/SynchException.java | 62 +- .../apache/qpid/util/concurrent/SynchQueue.java | 54 +- .../apache/qpid/util/concurrent/SynchRecord.java | 106 +- .../org/apache/qpid/util/concurrent/SynchRef.java | 60 +- .../apache/qpid/util/CommandLineParserTest.java | 1108 +++---- .../interop/clienttestcases/TestCase1DummyRun.java | 270 +- .../interop/clienttestcases/TestCase2BasicP2P.java | 418 +-- .../clienttestcases/TestCase3BasicPubSub.java | 478 +-- .../clienttestcases/TestCase4P2PMessageSize.java | 428 +-- .../TestCase5PubSubMessageSize.java | 486 +-- .../testcases/InteropTestCase1DummyRun.java | 168 +- .../testcases/InteropTestCase2BasicP2P.java | 180 +- .../testcases/InteropTestCase3BasicPubSub.java | 176 +- .../testcases/InteropTestCase4P2PMessageSize.java | 386 +-- .../InteropTestCase5PubSubMessageSize.java | 386 +-- .../qpid/sustained/SustainedClientTestCase.java | 1812 +++++------ .../apache/qpid/sustained/SustainedTestCase.java | 252 +- .../qpid/junit/maven/IsolatedClassLoader.java | 226 +- .../apache/qpid/junit/maven/TKTestRunnerMojo.java | 548 ++-- .../qpid/junit/maven/TKTestScriptGenMojo.java | 296 +- .../junit/concurrency/DefaultThreadFactory.java | 96 +- .../concurrency/PossibleDeadlockException.java | 92 +- .../qpid/junit/concurrency/TestRunnable.java | 478 +-- .../junit/concurrency/ThreadTestCoordinator.java | 970 +++--- .../qpid/junit/concurrency/ThreadTestExample.java | 290 +- .../qpid/junit/extensions/AsymptoticTestCase.java | 606 ++-- .../junit/extensions/AsymptoticTestDecorator.java | 340 +- .../apache/qpid/junit/extensions/BaseThrottle.java | 196 +- .../qpid/junit/extensions/BatchedThrottle.java | 188 +- .../junit/extensions/DurationTestDecorator.java | 398 +-- .../qpid/junit/extensions/InstrumentedTest.java | 132 +- .../qpid/junit/extensions/NullResultPrinter.java | 184 +- .../ParameterVariationTestDecorator.java | 344 +- .../qpid/junit/extensions/ScaledTestDecorator.java | 750 ++--- .../qpid/junit/extensions/SetupTaskAware.java | 110 +- .../qpid/junit/extensions/SetupTaskHandler.java | 184 +- .../qpid/junit/extensions/ShutdownHookable.java | 84 +- .../qpid/junit/extensions/SleepThrottle.java | 162 +- .../apache/qpid/junit/extensions/TKTestResult.java | 1250 +++---- .../apache/qpid/junit/extensions/TKTestRunner.java | 1388 ++++---- .../TestRunnerImprovedErrorHandling.java | 262 +- .../qpid/junit/extensions/TestThreadAware.java | 98 +- .../org/apache/qpid/junit/extensions/Throttle.java | 146 +- .../qpid/junit/extensions/TimingController.java | 350 +- .../junit/extensions/TimingControllerAware.java | 86 +- .../extensions/WrappedSuiteTestDecorator.java | 268 +- .../extensions/listeners/CSVTestListener.java | 1064 +++--- .../extensions/listeners/ConsoleTestListener.java | 528 +-- .../junit/extensions/listeners/TKTestListener.java | 264 +- .../extensions/listeners/XMLTestListener.java | 800 ++--- .../junit/extensions/util/CommandLineParser.java | 1574 ++++----- .../extensions/util/ContextualProperties.java | 988 +++--- .../qpid/junit/extensions/util/MathUtils.java | 856 ++--- .../junit/extensions/util/ParsedProperties.java | 780 ++--- .../apache/qpid/junit/extensions/util/SizeOf.java | 188 +- .../qpid/junit/extensions/util/StackQueue.java | 262 +- .../extensions/util/TestContextProperties.java | 404 +-- .../main/java/org/apache/qpid/ping/PingClient.java | 214 +- .../org/apache/qpid/ping/PingDurableClient.java | 904 ++--- .../org/apache/qpid/ping/PingLatencyTestPerf.java | 622 ++-- .../org/apache/qpid/ping/PingSendOnlyClient.java | 186 +- .../java/org/apache/qpid/ping/PingTestPerf.java | 392 +-- .../apache/qpid/requestreply/PingPongBouncer.java | 906 +++--- .../apache/qpid/requestreply/PingPongProducer.java | 3440 ++++++++++---------- .../apache/qpid/requestreply/PingPongTestPerf.java | 502 +-- .../qpid/test/testcases/MessageThroughputPerf.java | 398 +-- .../exchange/MessagingTestConfigProperties.java | 616 ++-- .../ReturnUnroutableMandatoryMessageTest.java | 624 ++-- .../apache/qpid/test/framework/AMQPPublisher.java | 108 +- .../qpid/test/framework/BrokerLifecycleAware.java | 140 +- .../apache/qpid/test/framework/CauseFailure.java | 84 +- .../test/framework/CauseFailureUserPrompt.java | 130 +- .../qpid/test/framework/FrameworkTestContext.java | 96 +- .../test/framework/LocalAMQPCircuitFactory.java | 336 +- .../qpid/test/framework/LocalCircuitFactory.java | 632 ++-- .../qpid/test/framework/MessageIdentityVector.java | 334 +- .../test/framework/NotApplicableAssertion.java | 224 +- .../apache/qpid/test/framework/TestCaseVector.java | 176 +- .../framework/distributedtesting/TestClient.java | 994 +++--- .../localcircuit/LocalAMQPPublisherImpl.java | 266 +- .../test/framework/qpid/AMQPFeatureDecorator.java | 192 +- .../test/framework/qpid/CauseFailureDecorator.java | 190 +- .../qpid/test/framework/qpid/CauseFailureInVM.java | 140 +- .../test/framework/qpid/InVMBrokerDecorator.java | 270 +- .../apache/qpid/test/testcases/FailoverTest.java | 238 +- .../qpid/test/testcases/ImmediateMessageTest.java | 606 ++-- .../qpid/test/testcases/MandatoryMessageTest.java | 642 ++-- .../apache/qpid/test/testcases/RollbackTest.java | 264 +- .../org/apache/qpid/test/testcases/TTLTest.java | 302 +- .../qpid/testutil/QpidClientConnectionHelper.java | 592 ++-- 166 files changed, 32444 insertions(+), 32444 deletions(-) (limited to 'java') diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index e7c887f306..f1b383eac9 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -1,240 +1,240 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.exchange; - -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.exchange.ExchangeDefaults; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.server.management.MBeanConstructor; -import org.apache.qpid.server.management.MBeanDescription; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import java.util.List; -import java.util.Map; -import java.util.ArrayList; -import java.util.concurrent.CopyOnWriteArraySet; - -public class FanoutExchange extends AbstractExchange -{ - private static final Logger _logger = Logger.getLogger(FanoutExchange.class); - - /** - * Maps from queue name to queue instances - */ - private final CopyOnWriteArraySet _queues = new CopyOnWriteArraySet(); - - /** - * MBean class implementing the management interfaces. - */ - @MBeanDescription("Management Bean for Fanout Exchange") - private final class FanoutExchangeMBean extends ExchangeMBean - { - @MBeanConstructor("Creates an MBean for AMQ fanout exchange") - public FanoutExchangeMBean() throws JMException - { - super(); - _exchangeType = "fanout"; - init(); - } - - public TabularData bindings() throws OpenDataException - { - - _bindingList = new TabularDataSupport(_bindinglistDataType); - - for (AMQQueue queue : _queues) - { - String queueName = queue.getName().toString(); - - Object[] bindingItemValues = {queueName, new String[]{queueName}}; - CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); - _bindingList.put(bindingData); - } - - return _bindingList; - } - - public void createNewBinding(String queueName, String binding) throws JMException - { - AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (queue == null) - { - throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); - } - - try - { - queue.bind(new AMQShortString(binding), null, FanoutExchange.this); - } - catch (AMQException ex) - { - throw new MBeanException(ex); - } - } - - } // End of MBean class - - protected ExchangeMBean createMBean() throws AMQException - { - try - { - return new FanoutExchange.FanoutExchangeMBean(); - } - catch (JMException ex) - { - _logger.error("Exception occured in creating the direct exchange mbean", ex); - throw new AMQException("Exception occured in creating the direct exchange mbean", ex); - } - } - - public static final ExchangeType TYPE = new ExchangeType() - { - - public AMQShortString getName() - { - return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; - } - - public Class getExchangeClass() - { - return FanoutExchange.class; - } - - public FanoutExchange newInstance(VirtualHost host, - AMQShortString name, - boolean durable, - int ticket, - boolean autoDelete) throws AMQException - { - FanoutExchange exch = new FanoutExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); - return exch; - } - - public AMQShortString getDefaultExchangeName() - { - return ExchangeDefaults.FANOUT_EXCHANGE_NAME; - } - }; - - public Map> getBindings() - { - return null; - } - - public AMQShortString getType() - { - return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; - } - - public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException - { - assert queue != null; - - if (_queues.contains(queue)) - { - _logger.debug("Queue " + queue + " is already registered"); - } - else - { - _queues.add(queue); - _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this); - } - } - - public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException - { - assert queue != null; - - if (!_queues.remove(queue)) - { - throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + ". "); - } - } - - public void route(AMQMessage payload) throws AMQException - { - final MessagePublishInfo publishInfo = payload.getMessagePublishInfo(); - final AMQShortString routingKey = publishInfo.getRoutingKey(); - if ((_queues == null) || _queues.isEmpty()) - { - String msg = "No queues bound to " + this; - if (publishInfo.isMandatory() || publishInfo.isImmediate()) - { - throw new NoRouteException(msg, payload); - } - else - { - _logger.warn(msg); - } - } - else - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Publishing message to queue " + _queues); - } - - payload.enqueue(new ArrayList(_queues)); - - } - } - - public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) - { - return isBound(routingKey, queue); - } - - public boolean isBound(AMQShortString routingKey, AMQQueue queue) - { - return _queues.contains(queue); - } - - public boolean isBound(AMQShortString routingKey) - { - - return (_queues != null) && !_queues.isEmpty(); - } - - public boolean isBound(AMQQueue queue) - { - - return _queues.contains(queue); - } - - public boolean hasBindings() - { - return !_queues.isEmpty(); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArraySet; + +public class FanoutExchange extends AbstractExchange +{ + private static final Logger _logger = Logger.getLogger(FanoutExchange.class); + + /** + * Maps from queue name to queue instances + */ + private final CopyOnWriteArraySet _queues = new CopyOnWriteArraySet(); + + /** + * MBean class implementing the management interfaces. + */ + @MBeanDescription("Management Bean for Fanout Exchange") + private final class FanoutExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ fanout exchange") + public FanoutExchangeMBean() throws JMException + { + super(); + _exchangeType = "fanout"; + init(); + } + + public TabularData bindings() throws OpenDataException + { + + _bindingList = new TabularDataSupport(_bindinglistDataType); + + for (AMQQueue queue : _queues) + { + String queueName = queue.getName().toString(); + + Object[] bindingItemValues = {queueName, new String[]{queueName}}; + CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); + _bindingList.put(bindingData); + } + + return _bindingList; + } + + public void createNewBinding(String queueName, String binding) throws JMException + { + AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); + } + + try + { + queue.bind(new AMQShortString(binding), null, FanoutExchange.this); + } + catch (AMQException ex) + { + throw new MBeanException(ex); + } + } + + } // End of MBean class + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new FanoutExchange.FanoutExchangeMBean(); + } + catch (JMException ex) + { + _logger.error("Exception occured in creating the direct exchange mbean", ex); + throw new AMQException("Exception occured in creating the direct exchange mbean", ex); + } + } + + public static final ExchangeType TYPE = new ExchangeType() + { + + public AMQShortString getName() + { + return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; + } + + public Class getExchangeClass() + { + return FanoutExchange.class; + } + + public FanoutExchange newInstance(VirtualHost host, + AMQShortString name, + boolean durable, + int ticket, + boolean autoDelete) throws AMQException + { + FanoutExchange exch = new FanoutExchange(); + exch.initialise(host, name, durable, ticket, autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + return ExchangeDefaults.FANOUT_EXCHANGE_NAME; + } + }; + + public Map> getBindings() + { + return null; + } + + public AMQShortString getType() + { + return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; + } + + public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + + if (_queues.contains(queue)) + { + _logger.debug("Queue " + queue + " is already registered"); + } + else + { + _queues.add(queue); + _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this); + } + } + + public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + + if (!_queues.remove(queue)) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + ". "); + } + } + + public void route(AMQMessage payload) throws AMQException + { + final MessagePublishInfo publishInfo = payload.getMessagePublishInfo(); + final AMQShortString routingKey = publishInfo.getRoutingKey(); + if ((_queues == null) || _queues.isEmpty()) + { + String msg = "No queues bound to " + this; + if (publishInfo.isMandatory() || publishInfo.isImmediate()) + { + throw new NoRouteException(msg, payload); + } + else + { + _logger.warn(msg); + } + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Publishing message to queue " + _queues); + } + + payload.enqueue(new ArrayList(_queues)); + + } + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + return isBound(routingKey, queue); + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + return _queues.contains(queue); + } + + public boolean isBound(AMQShortString routingKey) + { + + return (_queues != null) && !_queues.isEmpty(); + } + + public boolean isBound(AMQQueue queue) + { + + return _queues.contains(queue); + } + + public boolean hasBindings() + { + return !_queues.isEmpty(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java index dd712a404c..133c97a146 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java @@ -1,65 +1,65 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.server.handler; - -import org.apache.qpid.framing.*; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.AMQException; - -/** - * @author Apache Software Foundation - * - * - */ -public class AccessRequestHandler implements StateAwareMethodListener -{ - private static final AccessRequestHandler _instance = new AccessRequestHandler(); - - - public static AccessRequestHandler getInstance() - { - return _instance; - } - - private AccessRequestHandler() - { - } - - public void methodReceived(AMQStateManager stateManager, AccessRequestBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - - MethodRegistry methodRegistry = session.getMethodRegistry(); - - // We don't implement access control class, but to keep clients happy that expect it - // always use the "0" ticket. - AccessRequestOkBody response = methodRegistry.createAccessRequestOkBody(0); - - session.writeFrame(response.generateFrame(channelId)); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.AMQException; + +/** + * @author Apache Software Foundation + * + * + */ +public class AccessRequestHandler implements StateAwareMethodListener +{ + private static final AccessRequestHandler _instance = new AccessRequestHandler(); + + + public static AccessRequestHandler getInstance() + { + return _instance; + } + + private AccessRequestHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AccessRequestBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + + // We don't implement access control class, but to keep clients happy that expect it + // always use the "0" ticket. + AccessRequestOkBody response = methodRegistry.createAccessRequestOkBody(0); + + session.writeFrame(response.generateFrame(channelId)); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java index f8f9127809..3731116cba 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -1,101 +1,101 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * - */ - -package org.apache.qpid.server.handler; - -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.BasicGetBody; -import org.apache.qpid.framing.BasicGetEmptyBody; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.access.Permission; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.virtualhost.VirtualHost; - -public class BasicGetMethodHandler implements StateAwareMethodListener -{ - private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class); - - private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler(); - - public static BasicGetMethodHandler getInstance() - { - return _instance; - } - - private BasicGetMethodHandler() - { - } - - public void methodReceived(AMQStateManager stateManager, BasicGetBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - - - VirtualHost vHost = session.getVirtualHost(); - - AMQChannel channel = session.getChannel(channelId); - if (channel == null) - { - throw body.getChannelNotFoundException(channelId); - } - else - { - AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue()); - - if (queue == null) - { - _log.info("No queue for '" + body.getQueue() + "'"); - if(body.getQueue()!=null) - { - throw body.getConnectionException(AMQConstant.NOT_FOUND, - "No such queue, '" + body.getQueue()+ "'"); - } - else - { - throw body.getConnectionException(AMQConstant.NOT_ALLOWED, - "No queue name provided, no default queue defined."); - } - } - else - { - - //Perform ACLs - vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue); - - if (!queue.performGet(session, channel, !body.getNoAck())) - { - MethodRegistry methodRegistry = session.getMethodRegistry(); - // TODO - set clusterId - BasicGetEmptyBody responseBody = methodRegistry.createBasicGetEmptyBody(null); - - - session.writeFrame(responseBody.generateFrame(channelId)); - } - } - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicGetBody; +import org.apache.qpid.framing.BasicGetEmptyBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class BasicGetMethodHandler implements StateAwareMethodListener +{ + private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class); + + private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler(); + + public static BasicGetMethodHandler getInstance() + { + return _instance; + } + + private BasicGetMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicGetBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + + VirtualHost vHost = session.getVirtualHost(); + + AMQChannel channel = session.getChannel(channelId); + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + else + { + AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue()); + + if (queue == null) + { + _log.info("No queue for '" + body.getQueue() + "'"); + if(body.getQueue()!=null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, + "No such queue, '" + body.getQueue()+ "'"); + } + else + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED, + "No queue name provided, no default queue defined."); + } + } + else + { + + //Perform ACLs + vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue); + + if (!queue.performGet(session, channel, !body.getNoAck())) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + // TODO - set clusterId + BasicGetEmptyBody responseBody = methodRegistry.createBasicGetEmptyBody(null); + + + session.writeFrame(responseBody.generateFrame(channelId)); + } + } + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java index 3e2cab2e53..bca35be535 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java @@ -1,75 +1,75 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.server.handler; - -import org.apache.log4j.Logger; - -import org.apache.qpid.framing.BasicRecoverBody; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.BasicRecoverSyncBody; -import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; -import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.AMQException; - -public class BasicRecoverSyncMethodHandler implements StateAwareMethodListener -{ - private static final Logger _logger = Logger.getLogger(BasicRecoverSyncMethodHandler.class); - - private static final BasicRecoverSyncMethodHandler _instance = new BasicRecoverSyncMethodHandler(); - - public static BasicRecoverSyncMethodHandler getInstance() - { - return _instance; - } - - public void methodReceived(AMQStateManager stateManager, BasicRecoverSyncBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - - _logger.debug("Recover received on protocol session " + session + " and channel " + channelId); - AMQChannel channel = session.getChannel(channelId); - - - if (channel == null) - { - throw body.getChannelNotFoundException(channelId); - } - - channel.resend(body.getRequeue()); - - // Qpid 0-8 hacks a synchronous -ok onto recover. - // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant - if(session.getProtocolVersion().equals(ProtocolVersion.v0_9)) - { - MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); - AMQMethodBody recoverOk = methodRegistry.createBasicRecoverSyncOkBody(); - session.writeFrame(recoverOk.generateFrame(channelId)); - - } - - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.handler; + +import org.apache.log4j.Logger; + +import org.apache.qpid.framing.BasicRecoverBody; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.BasicRecoverSyncBody; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.AMQException; + +public class BasicRecoverSyncMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(BasicRecoverSyncMethodHandler.class); + + private static final BasicRecoverSyncMethodHandler _instance = new BasicRecoverSyncMethodHandler(); + + public static BasicRecoverSyncMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, BasicRecoverSyncBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + _logger.debug("Recover received on protocol session " + session + " and channel " + channelId); + AMQChannel channel = session.getChannel(channelId); + + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.resend(body.getRequeue()); + + // Qpid 0-8 hacks a synchronous -ok onto recover. + // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant + if(session.getProtocolVersion().equals(ProtocolVersion.v0_9)) + { + MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); + AMQMethodBody recoverOk = methodRegistry.createBasicRecoverSyncOkBody(); + session.writeFrame(recoverOk.generateFrame(channelId)); + + } + + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java index cce49f13c7..a854c97f60 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java @@ -1,121 +1,121 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * - */ - -package org.apache.qpid.server.handler; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.QueuePurgeBody; -import org.apache.qpid.framing.QueuePurgeOkBody; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.protocol.AMQMethodEvent; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.security.access.Permission; - -public class QueuePurgeHandler implements StateAwareMethodListener -{ - private static final QueuePurgeHandler _instance = new QueuePurgeHandler(); - - public static QueuePurgeHandler getInstance() - { - return _instance; - } - - private final boolean _failIfNotFound; - - public QueuePurgeHandler() - { - this(true); - } - - public QueuePurgeHandler(boolean failIfNotFound) - { - _failIfNotFound = failIfNotFound; - } - - public void methodReceived(AMQStateManager stateManager, QueuePurgeBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - VirtualHost virtualHost = session.getVirtualHost(); - QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); - - AMQChannel channel = session.getChannel(channelId); - - - AMQQueue queue; - if(body.getQueue() == null) - { - - if (channel == null) - { - throw body.getChannelNotFoundException(channelId); - } - - //get the default queue on the channel: - queue = channel.getDefaultQueue(); - - if(queue == null) - { - if(_failIfNotFound) - { - throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified."); - } - } - } - else - { - queue = queueRegistry.getQueue(body.getQueue()); - } - - if(queue == null) - { - if(_failIfNotFound) - { - throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); - } - } - else - { - - //Perform ACLs - virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue); - - long purged = queue.clearQueue(channel.getStoreContext()); - - - if(!body.getNowait()) - { - - MethodRegistry methodRegistry = session.getMethodRegistry(); - AMQMethodBody responseBody = methodRegistry.createQueuePurgeOkBody(purged); - session.writeFrame(responseBody.generateFrame(channelId)); - - } - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueuePurgeBody; +import org.apache.qpid.framing.QueuePurgeOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.security.access.Permission; + +public class QueuePurgeHandler implements StateAwareMethodListener +{ + private static final QueuePurgeHandler _instance = new QueuePurgeHandler(); + + public static QueuePurgeHandler getInstance() + { + return _instance; + } + + private final boolean _failIfNotFound; + + public QueuePurgeHandler() + { + this(true); + } + + public QueuePurgeHandler(boolean failIfNotFound) + { + _failIfNotFound = failIfNotFound; + } + + public void methodReceived(AMQStateManager stateManager, QueuePurgeBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + AMQChannel channel = session.getChannel(channelId); + + + AMQQueue queue; + if(body.getQueue() == null) + { + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + //get the default queue on the channel: + queue = channel.getDefaultQueue(); + + if(queue == null) + { + if(_failIfNotFound) + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified."); + } + } + } + else + { + queue = queueRegistry.getQueue(body.getQueue()); + } + + if(queue == null) + { + if(_failIfNotFound) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); + } + } + else + { + + //Perform ACLs + virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue); + + long purged = queue.clearQueue(channel.getStoreContext()); + + + if(!body.getNowait()) + { + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createQueuePurgeOkBody(purged); + session.writeFrame(responseBody.generateFrame(channelId)); + + } + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java index 9475b83c8f..d24e4f6ffa 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java @@ -1,566 +1,566 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.handler; - -import java.util.Map; -import java.util.HashMap; - -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.framing.*; -import org.apache.qpid.AMQException; - -public class ServerMethodDispatcherImpl implements MethodDispatcher -{ - private final AMQStateManager _stateManager; - - private static interface DispatcherFactory - { - public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager); - } - - private static final Map _dispatcherFactories = - new HashMap(); - - - static - { - _dispatcherFactories.put(ProtocolVersion.v8_0, - new DispatcherFactory() - { - public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) - { - return new ServerMethodDispatcherImpl_8_0(stateManager); - } - }); - - _dispatcherFactories.put(ProtocolVersion.v0_9, - new DispatcherFactory() - { - public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) - { - return new ServerMethodDispatcherImpl_0_9(stateManager); - } - }); - - } - - - private static final AccessRequestHandler _accessRequestHandler = AccessRequestHandler.getInstance(); - private static final ChannelCloseHandler _channelCloseHandler = ChannelCloseHandler.getInstance(); - private static final ChannelOpenHandler _channelOpenHandler = ChannelOpenHandler.getInstance(); - private static final ChannelCloseOkHandler _channelCloseOkHandler = ChannelCloseOkHandler.getInstance(); - private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); - private static final ConnectionCloseOkMethodHandler _connectionCloseOkMethodHandler = ConnectionCloseOkMethodHandler.getInstance(); - private static final ConnectionOpenMethodHandler _connectionOpenMethodHandler = ConnectionOpenMethodHandler.getInstance(); - private static final ConnectionTuneOkMethodHandler _connectionTuneOkMethodHandler = ConnectionTuneOkMethodHandler.getInstance(); - private static final ConnectionSecureOkMethodHandler _connectionSecureOkMethodHandler = ConnectionSecureOkMethodHandler.getInstance(); - private static final ConnectionStartOkMethodHandler _connectionStartOkMethodHandler = ConnectionStartOkMethodHandler.getInstance(); - private static final ExchangeDeclareHandler _exchangeDeclareHandler = ExchangeDeclareHandler.getInstance(); - private static final ExchangeDeleteHandler _exchangeDeleteHandler = ExchangeDeleteHandler.getInstance(); - private static final ExchangeBoundHandler _exchangeBoundHandler = ExchangeBoundHandler.getInstance(); - private static final BasicAckMethodHandler _basicAckMethodHandler = BasicAckMethodHandler.getInstance(); - private static final BasicRecoverMethodHandler _basicRecoverMethodHandler = BasicRecoverMethodHandler.getInstance(); - private static final BasicConsumeMethodHandler _basicConsumeMethodHandler = BasicConsumeMethodHandler.getInstance(); - private static final BasicGetMethodHandler _basicGetMethodHandler = BasicGetMethodHandler.getInstance(); - private static final BasicCancelMethodHandler _basicCancelMethodHandler = BasicCancelMethodHandler.getInstance(); - private static final BasicPublishMethodHandler _basicPublishMethodHandler = BasicPublishMethodHandler.getInstance(); - private static final BasicQosHandler _basicQosHandler = BasicQosHandler.getInstance(); - private static final QueueBindHandler _queueBindHandler = QueueBindHandler.getInstance(); - private static final QueueDeclareHandler _queueDeclareHandler = QueueDeclareHandler.getInstance(); - private static final QueueDeleteHandler _queueDeleteHandler = QueueDeleteHandler.getInstance(); - private static final QueuePurgeHandler _queuePurgeHandler = QueuePurgeHandler.getInstance(); - private static final ChannelFlowHandler _channelFlowHandler = ChannelFlowHandler.getInstance(); - private static final TxSelectHandler _txSelectHandler = TxSelectHandler.getInstance(); - private static final TxCommitHandler _txCommitHandler = TxCommitHandler.getInstance(); - private static final TxRollbackHandler _txRollbackHandler = TxRollbackHandler.getInstance(); - private static final BasicRejectMethodHandler _basicRejectMethodHandler = BasicRejectMethodHandler.getInstance(); - - - - public static MethodDispatcher createMethodDispatcher(AMQStateManager stateManager, ProtocolVersion protocolVersion) - { - return _dispatcherFactories.get(protocolVersion).createMethodDispatcher(stateManager); - } - - - public ServerMethodDispatcherImpl(AMQStateManager stateManager) - { - _stateManager = stateManager; - } - - - protected AMQStateManager getStateManager() - { - return _stateManager; - } - - - - public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException - { - _accessRequestHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException - { - _basicAckMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException - { - _basicCancelMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException - { - _basicConsumeMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException - { - _basicGetMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException - { - _basicPublishMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException - { - _basicQosHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException - { - _basicRecoverMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException - { - _basicRejectMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException - { - _channelOpenHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException - { - _channelCloseHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException - { - _channelCloseOkHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException - { - _channelFlowHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - - public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException - { - _connectionOpenMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException - { - _connectionCloseMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException - { - _connectionCloseOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - - public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException - { - _connectionSecureOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException - { - _connectionStartOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException - { - _connectionTuneOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException - { - _exchangeBoundHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException - { - _exchangeDeclareHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException - { - _exchangeDeleteHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException - { - _queueBindHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException - { - _queueDeclareHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException - { - _queueDeleteHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException - { - _queuePurgeHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException - { - _txCommitHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException - { - _txRollbackHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException - { - _txSelectHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - - -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import java.util.Map; +import java.util.HashMap; + +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; + +public class ServerMethodDispatcherImpl implements MethodDispatcher +{ + private final AMQStateManager _stateManager; + + private static interface DispatcherFactory + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager); + } + + private static final Map _dispatcherFactories = + new HashMap(); + + + static + { + _dispatcherFactories.put(ProtocolVersion.v8_0, + new DispatcherFactory() + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) + { + return new ServerMethodDispatcherImpl_8_0(stateManager); + } + }); + + _dispatcherFactories.put(ProtocolVersion.v0_9, + new DispatcherFactory() + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) + { + return new ServerMethodDispatcherImpl_0_9(stateManager); + } + }); + + } + + + private static final AccessRequestHandler _accessRequestHandler = AccessRequestHandler.getInstance(); + private static final ChannelCloseHandler _channelCloseHandler = ChannelCloseHandler.getInstance(); + private static final ChannelOpenHandler _channelOpenHandler = ChannelOpenHandler.getInstance(); + private static final ChannelCloseOkHandler _channelCloseOkHandler = ChannelCloseOkHandler.getInstance(); + private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); + private static final ConnectionCloseOkMethodHandler _connectionCloseOkMethodHandler = ConnectionCloseOkMethodHandler.getInstance(); + private static final ConnectionOpenMethodHandler _connectionOpenMethodHandler = ConnectionOpenMethodHandler.getInstance(); + private static final ConnectionTuneOkMethodHandler _connectionTuneOkMethodHandler = ConnectionTuneOkMethodHandler.getInstance(); + private static final ConnectionSecureOkMethodHandler _connectionSecureOkMethodHandler = ConnectionSecureOkMethodHandler.getInstance(); + private static final ConnectionStartOkMethodHandler _connectionStartOkMethodHandler = ConnectionStartOkMethodHandler.getInstance(); + private static final ExchangeDeclareHandler _exchangeDeclareHandler = ExchangeDeclareHandler.getInstance(); + private static final ExchangeDeleteHandler _exchangeDeleteHandler = ExchangeDeleteHandler.getInstance(); + private static final ExchangeBoundHandler _exchangeBoundHandler = ExchangeBoundHandler.getInstance(); + private static final BasicAckMethodHandler _basicAckMethodHandler = BasicAckMethodHandler.getInstance(); + private static final BasicRecoverMethodHandler _basicRecoverMethodHandler = BasicRecoverMethodHandler.getInstance(); + private static final BasicConsumeMethodHandler _basicConsumeMethodHandler = BasicConsumeMethodHandler.getInstance(); + private static final BasicGetMethodHandler _basicGetMethodHandler = BasicGetMethodHandler.getInstance(); + private static final BasicCancelMethodHandler _basicCancelMethodHandler = BasicCancelMethodHandler.getInstance(); + private static final BasicPublishMethodHandler _basicPublishMethodHandler = BasicPublishMethodHandler.getInstance(); + private static final BasicQosHandler _basicQosHandler = BasicQosHandler.getInstance(); + private static final QueueBindHandler _queueBindHandler = QueueBindHandler.getInstance(); + private static final QueueDeclareHandler _queueDeclareHandler = QueueDeclareHandler.getInstance(); + private static final QueueDeleteHandler _queueDeleteHandler = QueueDeleteHandler.getInstance(); + private static final QueuePurgeHandler _queuePurgeHandler = QueuePurgeHandler.getInstance(); + private static final ChannelFlowHandler _channelFlowHandler = ChannelFlowHandler.getInstance(); + private static final TxSelectHandler _txSelectHandler = TxSelectHandler.getInstance(); + private static final TxCommitHandler _txCommitHandler = TxCommitHandler.getInstance(); + private static final TxRollbackHandler _txRollbackHandler = TxRollbackHandler.getInstance(); + private static final BasicRejectMethodHandler _basicRejectMethodHandler = BasicRejectMethodHandler.getInstance(); + + + + public static MethodDispatcher createMethodDispatcher(AMQStateManager stateManager, ProtocolVersion protocolVersion) + { + return _dispatcherFactories.get(protocolVersion).createMethodDispatcher(stateManager); + } + + + public ServerMethodDispatcherImpl(AMQStateManager stateManager) + { + _stateManager = stateManager; + } + + + protected AMQStateManager getStateManager() + { + return _stateManager; + } + + + + public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException + { + _accessRequestHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException + { + _basicAckMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException + { + _basicCancelMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException + { + _basicConsumeMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException + { + _basicGetMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException + { + _basicPublishMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException + { + _basicQosHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException + { + _basicRecoverMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException + { + _basicRejectMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException + { + _channelOpenHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException + { + _channelCloseHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException + { + _channelCloseOkHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException + { + _channelFlowHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + + public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException + { + _connectionOpenMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException + { + _connectionCloseMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException + { + _connectionCloseOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + + public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException + { + _connectionSecureOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException + { + _connectionStartOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException + { + _connectionTuneOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException + { + _exchangeBoundHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException + { + _exchangeDeclareHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException + { + _exchangeDeleteHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException + { + _queueBindHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException + { + _queueDeclareHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException + { + _queueDeleteHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException + { + _queuePurgeHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException + { + _txCommitHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException + { + _txRollbackHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException + { + _txSelectHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java index 8b1dca77ba..382a85347b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java @@ -1,164 +1,164 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.handler; - - -import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; -import org.apache.qpid.framing.*; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.AMQException; - - - -public class ServerMethodDispatcherImpl_0_9 - extends ServerMethodDispatcherImpl - implements MethodDispatcher_0_9 - -{ - - private static final BasicRecoverSyncMethodHandler _basicRecoverSyncMethodHandler = - BasicRecoverSyncMethodHandler.getInstance(); - private static final QueueUnbindHandler _queueUnbindHandler = - QueueUnbindHandler.getInstance(); - - - public ServerMethodDispatcherImpl_0_9(AMQStateManager stateManager) - { - super(stateManager); - } - - public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException - { - _basicRecoverSyncMethodHandler.methodReceived(getStateManager(), body, channelId); - return true; - } - - public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException - { - _queueUnbindHandler.methodReceived(getStateManager(),body,channelId); - return true; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + + +import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.AMQException; + + + +public class ServerMethodDispatcherImpl_0_9 + extends ServerMethodDispatcherImpl + implements MethodDispatcher_0_9 + +{ + + private static final BasicRecoverSyncMethodHandler _basicRecoverSyncMethodHandler = + BasicRecoverSyncMethodHandler.getInstance(); + private static final QueueUnbindHandler _queueUnbindHandler = + QueueUnbindHandler.getInstance(); + + + public ServerMethodDispatcherImpl_0_9(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException + { + _basicRecoverSyncMethodHandler.methodReceived(getStateManager(), body, channelId); + return true; + } + + public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException + { + _queueUnbindHandler.methodReceived(getStateManager(),body,channelId); + return true; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java index d599ca3d4e..22f64cf7d3 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java @@ -1,86 +1,86 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.handler; - -import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; -import org.apache.qpid.framing.*; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.AMQException; - -public class ServerMethodDispatcherImpl_8_0 - extends ServerMethodDispatcherImpl - implements MethodDispatcher_8_0 -{ - public ServerMethodDispatcherImpl_8_0(AMQStateManager stateManager) - { - super(stateManager); - } - - public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException - { - return false; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.AMQException; + +public class ServerMethodDispatcherImpl_8_0 + extends ServerMethodDispatcherImpl + implements MethodDispatcher_8_0 +{ + public ServerMethodDispatcherImpl_8_0(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException + { + return false; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java b/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java index fb18519fe1..0abb3cdd7d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java @@ -1,33 +1,33 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.handler; - - -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.AMQException; - -public class UnexpectedMethodException extends AMQException -{ - public UnexpectedMethodException(AMQMethodBody body) - { - super("Unexpected method recevied: " + body.getClass().getName()); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.AMQException; + +public class UnexpectedMethodException extends AMQException +{ + public UnexpectedMethodException(AMQMethodBody body) + { + super("Unexpected method recevied: " + body.getClass().getName()); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java index e01c5aabbf..576d577b40 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java @@ -1,57 +1,57 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -/* - * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. - * Supported AMQP versions: - * 8-0 - */ -package org.apache.qpid.server.output; - -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.AMQException; - -public interface ProtocolOutputConverter -{ - void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag); - - interface Factory - { - ProtocolOutputConverter newInstance(AMQProtocolSession session); - } - - void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException; - - void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException; - - byte getProtocolMinorVersion(); - - byte getProtocolMajorVersion(); - - void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) - throws AMQException; - - void writeFrame(AMQDataBlock block); -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output; + +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.AMQException; + +public interface ProtocolOutputConverter +{ + void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag); + + interface Factory + { + ProtocolOutputConverter newInstance(AMQProtocolSession session); + } + + void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException; + + void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException; + + byte getProtocolMinorVersion(); + + byte getProtocolMajorVersion(); + + void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException; + + void writeFrame(AMQDataBlock block); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java index 36e7e88fd6..02fb1429c0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java @@ -1,61 +1,61 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -/* - * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. - * Supported AMQP versions: - * 8-0 - */ -package org.apache.qpid.server.output; - -import org.apache.qpid.server.output.ProtocolOutputConverter.Factory; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.framing.ProtocolVersion; - -import java.util.Map; -import java.util.HashMap; - -public class ProtocolOutputConverterRegistry -{ - - private static final Map _registry = - new HashMap(); - - - static - { - register(ProtocolVersion.v8_0, org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl.getInstanceFactory()); - register(ProtocolVersion.v0_9, org.apache.qpid.server.output.amqp0_9.ProtocolOutputConverterImpl.getInstanceFactory()); - - } - - private static void register(ProtocolVersion version, Factory converter) - { - - _registry.put(version,converter); - } - - - public static ProtocolOutputConverter getConverter(AMQProtocolSession session) - { - return _registry.get(session.getProtocolVersion()).newInstance(session); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output; + +import org.apache.qpid.server.output.ProtocolOutputConverter.Factory; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.framing.ProtocolVersion; + +import java.util.Map; +import java.util.HashMap; + +public class ProtocolOutputConverterRegistry +{ + + private static final Map _registry = + new HashMap(); + + + static + { + register(ProtocolVersion.v8_0, org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl.getInstanceFactory()); + register(ProtocolVersion.v0_9, org.apache.qpid.server.output.amqp0_9.ProtocolOutputConverterImpl.getInstanceFactory()); + + } + + private static void register(ProtocolVersion version, Factory converter) + { + + _registry.put(version,converter); + } + + + public static ProtocolOutputConverter getConverter(AMQProtocolSession session) + { + return _registry.get(session.getProtocolVersion()).newInstance(session); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java index d7a879180a..d4cb7c878f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java @@ -1,285 +1,285 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -/* - * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. - * Supported AMQP versions: - * 8-0 - */ -package org.apache.qpid.server.output.amqp0_8; - -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQMessageHandle; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.AMQException; - -import org.apache.mina.common.ByteBuffer; - -import java.util.Iterator; - -public class ProtocolOutputConverterImpl implements ProtocolOutputConverter -{ - - - public static Factory getInstanceFactory() - { - return new Factory() - { - - public ProtocolOutputConverter newInstance(AMQProtocolSession session) - { - return new ProtocolOutputConverterImpl(session); - } - }; - } - - private final AMQProtocolSession _protocolSession; - - private ProtocolOutputConverterImpl(AMQProtocolSession session) - { - _protocolSession = session; - } - - - public AMQProtocolSession getProtocolSession() - { - return _protocolSession; - } - - public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException - { - AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - message.getContentHeaderBody()); - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final Long messageId = message.getMessageId(); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - - if(bodyCount == 0) - { - SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, - contentHeader); - - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); - AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext,messageId, i); - writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); - } - - - } - - - } - - - public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException - { - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final long messageId = message.getMessageId(); - - AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); - - - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - message.getContentHeaderBody()); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - if(bodyCount == 0) - { - SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, - contentHeader); - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); - AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext, messageId, i); - writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); - } - - - } - - - } - - - private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException - { - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicDeliverBody deliverBody = - methodRegistry.createBasicDeliverBody(consumerTag, - deliveryTag, - messageHandle.isRedelivered(), - pb.getExchange(), - pb.getRoutingKey()); - AMQFrame deliverFrame = deliverBody.generateFrame(channelId); - - - return deliverFrame; - } - - private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) - throws AMQException - { - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicGetOkBody getOkBody = - methodRegistry.createBasicGetOkBody(deliveryTag, - messageHandle.isRedelivered(), - pb.getExchange(), - pb.getRoutingKey(), - queueSize); - AMQFrame getOkFrame = getOkBody.generateFrame(channelId); - - return getOkFrame; - } - - public byte getProtocolMinorVersion() - { - return getProtocolSession().getProtocolMinorVersion(); - } - - public byte getProtocolMajorVersion() - { - return getProtocolSession().getProtocolMajorVersion(); - } - - private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException - { - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicReturnBody basicReturnBody = - methodRegistry.createBasicReturnBody(replyCode, - replyText, - message.getMessagePublishInfo().getExchange(), - message.getMessagePublishInfo().getRoutingKey()); - AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); - - return returnFrame; - } - - public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) - throws AMQException - { - AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); - - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - message.getContentHeaderBody()); - - Iterator bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - if (bodyFrameIterator.hasNext()) - { - AMQDataBlock firstContentBody = bodyFrameIterator.next(); - AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - } - else - { - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); - - writeFrame(compositeBlock); - } - - // - // Now start writing out the other content bodies - // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded - // - while (bodyFrameIterator.hasNext()) - { - writeFrame(bodyFrameIterator.next()); - } - } - - - public void writeFrame(AMQDataBlock block) - { - getProtocolSession().writeFrame(block); - } - - - public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) - { - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag); - writeFrame(basicCancelOkBody.generateFrame(channelId)); - - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output.amqp0_8; + +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.AMQException; + +import org.apache.mina.common.ByteBuffer; + +import java.util.Iterator; + +public class ProtocolOutputConverterImpl implements ProtocolOutputConverter +{ + + + public static Factory getInstanceFactory() + { + return new Factory() + { + + public ProtocolOutputConverter newInstance(AMQProtocolSession session) + { + return new ProtocolOutputConverterImpl(session); + } + }; + } + + private final AMQProtocolSession _protocolSession; + + private ProtocolOutputConverterImpl(AMQProtocolSession session) + { + _protocolSession = session; + } + + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final Long messageId = message.getMessageId(); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext,messageId, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException + { + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final long messageId = message.getMessageId(); + + AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); + + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, messageId, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicDeliverBody deliverBody = + methodRegistry.createBasicDeliverBody(consumerTag, + deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey()); + AMQFrame deliverFrame = deliverBody.generateFrame(channelId); + + + return deliverFrame; + } + + private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicGetOkBody getOkBody = + methodRegistry.createBasicGetOkBody(deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey(), + queueSize); + AMQFrame getOkFrame = getOkBody.generateFrame(channelId); + + return getOkFrame; + } + + public byte getProtocolMinorVersion() + { + return getProtocolSession().getProtocolMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolSession().getProtocolMajorVersion(); + } + + private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicReturnBody basicReturnBody = + methodRegistry.createBasicReturnBody(replyCode, + replyText, + message.getMessagePublishInfo().getExchange(), + message.getMessagePublishInfo().getRoutingKey()); + AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); + + return returnFrame; + } + + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + Iterator bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + if (bodyFrameIterator.hasNext()) + { + AMQDataBlock firstContentBody = bodyFrameIterator.next(); + AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); + + writeFrame(compositeBlock); + } + + // + // Now start writing out the other content bodies + // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded + // + while (bodyFrameIterator.hasNext()) + { + writeFrame(bodyFrameIterator.next()); + } + } + + + public void writeFrame(AMQDataBlock block) + { + getProtocolSession().writeFrame(block); + } + + + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) + { + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag); + writeFrame(basicCancelOkBody.generateFrame(channelId)); + + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java index 48d2ca9bc9..f87d3bcae1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java @@ -1,397 +1,397 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.output.amqp0_9; - -import org.apache.mina.common.ByteBuffer; - -import java.util.Iterator; - -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQMessageHandle; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; -import org.apache.qpid.AMQException; -import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; - -public class ProtocolOutputConverterImpl implements ProtocolOutputConverter -{ - private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); - private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter(); - - - public static Factory getInstanceFactory() - { - return new Factory() - { - - public ProtocolOutputConverter newInstance(AMQProtocolSession session) - { - return new ProtocolOutputConverterImpl(session); - } - }; - } - - private final AMQProtocolSession _protocolSession; - - private ProtocolOutputConverterImpl(AMQProtocolSession session) - { - _protocolSession = session; - } - - - public AMQProtocolSession getProtocolSession() - { - return _protocolSession; - } - - public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException - { - AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); - final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody(); - - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final Long messageId = message.getMessageId(); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - - if(bodyCount == 0) - { - SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody, - contentHeaderBody); - - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb); - - CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext,messageId, i); - writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); - } - - - } - - - } - - private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody) - { - - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - contentHeaderBody); - return contentHeader; - } - - - public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException - { - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final long messageId = message.getMessageId(); - - AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); - - - AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - if(bodyCount == 0) - { - SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, - contentHeader); - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)); - AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext, messageId, i); - writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); - } - - - } - - - } - - - private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag) - throws AMQException - { - - - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - - final AMQBody returnBlock = new AMQBody() - { - - - - private final boolean _isRedelivered = messageHandle.isRedelivered(); - private final AMQShortString _exchangeName = pb.getExchange(); - private final AMQShortString _routingKey = pb.getRoutingKey(); - - - public AMQBody _underlyingBody; - - public AMQBody createAMQBody() - { - return METHOD_REGISTRY.createBasicDeliverBody(consumerTag, - deliveryTag, - _isRedelivered, - _exchangeName, - _routingKey); - - - - - - } - - public byte getFrameType() - { - return AMQMethodBody.TYPE; - } - - public int getSize() - { - if(_underlyingBody == null) - { - _underlyingBody = createAMQBody(); - } - return _underlyingBody.getSize(); - } - - public void writePayload(ByteBuffer buffer) - { - if(_underlyingBody == null) - { - _underlyingBody = createAMQBody(); - } - _underlyingBody.writePayload(buffer); - } - - public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession) - throws AMQException - { - throw new AMQException("This block should never be dispatched!"); - } - }; - return returnBlock; - } - - private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) - throws AMQException - { - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - - BasicGetOkBody getOkBody = - METHOD_REGISTRY.createBasicGetOkBody(deliveryTag, - messageHandle.isRedelivered(), - pb.getExchange(), - pb.getRoutingKey(), - queueSize); - AMQFrame getOkFrame = getOkBody.generateFrame(channelId); - - return getOkFrame; - } - - public byte getProtocolMinorVersion() - { - return getProtocolSession().getProtocolMinorVersion(); - } - - public byte getProtocolMajorVersion() - { - return getProtocolSession().getProtocolMajorVersion(); - } - - private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException - { - - BasicReturnBody basicReturnBody = - METHOD_REGISTRY.createBasicReturnBody(replyCode, - replyText, - message.getMessagePublishInfo().getExchange(), - message.getMessagePublishInfo().getRoutingKey()); - AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); - - return returnFrame; - } - - public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) - throws AMQException - { - AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); - - AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); - - Iterator bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - if (bodyFrameIterator.hasNext()) - { - AMQDataBlock firstContentBody = bodyFrameIterator.next(); - AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - } - else - { - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); - - writeFrame(compositeBlock); - } - - // - // Now start writing out the other content bodies - // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded - // - while (bodyFrameIterator.hasNext()) - { - writeFrame(bodyFrameIterator.next()); - } - } - - - public void writeFrame(AMQDataBlock block) - { - getProtocolSession().writeFrame(block); - } - - - public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) - { - - BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag); - writeFrame(basicCancelOkBody.generateFrame(channelId)); - - } - - - public static final class CompositeAMQBodyBlock extends AMQDataBlock - { - public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead(); - - private final AMQBody _methodBody; - private final AMQBody _headerBody; - private final AMQBody _contentBody; - private final int _channel; - - - public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody) - { - _channel = channel; - _methodBody = methodBody; - _headerBody = headerBody; - _contentBody = contentBody; - - } - - public long getSize() - { - return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize(); - } - - public void writePayload(ByteBuffer buffer) - { - AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody); - } - } - - public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock - { - public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead(); - - private final AMQBody _methodBody; - private final AMQBody _headerBody; - private final int _channel; - - - public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody) - { - _channel = channel; - _methodBody = methodBody; - _headerBody = headerBody; - - } - - public long getSize() - { - return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ; - } - - public void writePayload(ByteBuffer buffer) - { - AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody); - } - } - -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.output.amqp0_9; + +import org.apache.mina.common.ByteBuffer; + +import java.util.Iterator; + +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +public class ProtocolOutputConverterImpl implements ProtocolOutputConverter +{ + private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); + private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter(); + + + public static Factory getInstanceFactory() + { + return new Factory() + { + + public ProtocolOutputConverter newInstance(AMQProtocolSession session) + { + return new ProtocolOutputConverterImpl(session); + } + }; + } + + private final AMQProtocolSession _protocolSession; + + private ProtocolOutputConverterImpl(AMQProtocolSession session) + { + _protocolSession = session; + } + + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); + final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody(); + + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final Long messageId = message.getMessageId(); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + + if(bodyCount == 0) + { + SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody, + contentHeaderBody); + + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb); + + CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext,messageId, i); + writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); + } + + + } + + + } + + private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody) + { + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + contentHeaderBody); + return contentHeader; + } + + + public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException + { + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final long messageId = message.getMessageId(); + + AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); + + + AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, messageId, i); + writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); + } + + + } + + + } + + + private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag) + throws AMQException + { + + + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + + final AMQBody returnBlock = new AMQBody() + { + + + + private final boolean _isRedelivered = messageHandle.isRedelivered(); + private final AMQShortString _exchangeName = pb.getExchange(); + private final AMQShortString _routingKey = pb.getRoutingKey(); + + + public AMQBody _underlyingBody; + + public AMQBody createAMQBody() + { + return METHOD_REGISTRY.createBasicDeliverBody(consumerTag, + deliveryTag, + _isRedelivered, + _exchangeName, + _routingKey); + + + + + + } + + public byte getFrameType() + { + return AMQMethodBody.TYPE; + } + + public int getSize() + { + if(_underlyingBody == null) + { + _underlyingBody = createAMQBody(); + } + return _underlyingBody.getSize(); + } + + public void writePayload(ByteBuffer buffer) + { + if(_underlyingBody == null) + { + _underlyingBody = createAMQBody(); + } + _underlyingBody.writePayload(buffer); + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession) + throws AMQException + { + throw new AMQException("This block should never be dispatched!"); + } + }; + return returnBlock; + } + + private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + + BasicGetOkBody getOkBody = + METHOD_REGISTRY.createBasicGetOkBody(deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey(), + queueSize); + AMQFrame getOkFrame = getOkBody.generateFrame(channelId); + + return getOkFrame; + } + + public byte getProtocolMinorVersion() + { + return getProtocolSession().getProtocolMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolSession().getProtocolMajorVersion(); + } + + private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + + BasicReturnBody basicReturnBody = + METHOD_REGISTRY.createBasicReturnBody(replyCode, + replyText, + message.getMessagePublishInfo().getExchange(), + message.getMessagePublishInfo().getRoutingKey()); + AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); + + return returnFrame; + } + + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); + + AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); + + Iterator bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + if (bodyFrameIterator.hasNext()) + { + AMQDataBlock firstContentBody = bodyFrameIterator.next(); + AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); + + writeFrame(compositeBlock); + } + + // + // Now start writing out the other content bodies + // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded + // + while (bodyFrameIterator.hasNext()) + { + writeFrame(bodyFrameIterator.next()); + } + } + + + public void writeFrame(AMQDataBlock block) + { + getProtocolSession().writeFrame(block); + } + + + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) + { + + BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag); + writeFrame(basicCancelOkBody.generateFrame(channelId)); + + } + + + public static final class CompositeAMQBodyBlock extends AMQDataBlock + { + public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead(); + + private final AMQBody _methodBody; + private final AMQBody _headerBody; + private final AMQBody _contentBody; + private final int _channel; + + + public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody) + { + _channel = channel; + _methodBody = methodBody; + _headerBody = headerBody; + _contentBody = contentBody; + + } + + public long getSize() + { + return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize(); + } + + public void writePayload(ByteBuffer buffer) + { + AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody); + } + } + + public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock + { + public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead(); + + private final AMQBody _methodBody; + private final AMQBody _headerBody; + private final int _channel; + + + public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody) + { + _channel = channel; + _methodBody = methodBody; + _headerBody = headerBody; + + } + + public long getSize() + { + return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ; + } + + public void writePayload(ByteBuffer buffer) + { + AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody); + } + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java index a7599a3e0d..92f951ce39 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java @@ -1,46 +1,46 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.protocol; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.protocol.AMQMethodEvent; - -/** - * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Represents failure to handle an AMQP method. - *
- * - * @todo Not an AMQP exception as no status code. - * - * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a - * Runtime. - */ -public class AMQNoMethodHandlerException extends AMQException -{ - public AMQNoMethodHandlerException(AMQMethodEvent evt) - { - super("AMQMethodEvent " + evt + " was not processed by any listener on Broker."); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +/** + * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to handle an AMQP method. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a + * Runtime. + */ +public class AMQNoMethodHandlerException extends AMQException +{ + public AMQNoMethodHandlerException(AMQMethodEvent evt) + { + super("AMQMethodEvent " + evt + " was not processed by any listener on Broker."); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java index 6e72aa062f..bb2db8d506 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java @@ -1,46 +1,46 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.protocol; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQDataBlock; - -/** - * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Represents failure to cast a frame to its expected type. - *
- * - * @todo Not an AMQP exception as no status code. - * - * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would - * be better just to leave that as a ClassCastException. However, check the framing layer catches this error - * first. - */ -public class UnknnownMessageTypeException extends AMQException -{ - public UnknnownMessageTypeException(AMQDataBlock message) - { - super("Unknown message type: " + message.getClass().getName() + ": " + message); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; + +/** + * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to cast a frame to its expected type. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would + * be better just to leave that as a ClassCastException. However, check the framing layer catches this error + * first. + */ +public class UnknnownMessageTypeException extends AMQException +{ + public UnknnownMessageTypeException(AMQDataBlock message) + { + super("Unknown message type: " + message.getClass().getName() + ": " + message); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java index 6f9efd3200..65115e4103 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java @@ -1,138 +1,138 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.queue; - -import org.apache.qpid.AMQException; - -public enum NotificationCheck -{ - - MESSAGE_COUNT_ALERT - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - int msgCount; - final long maximumMessageCount = queue.getMaximumMessageCount(); - if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount) - { - listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached."); - return true; - } - return false; - } - }, - MESSAGE_SIZE_ALERT(true) - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - final long maximumMessageSize = queue.getMaximumMessageSize(); - if(maximumMessageSize != 0) - { - // Check for threshold message size - long messageSize; - try - { - messageSize = (msg == null) ? 0 : msg.getContentHeaderBody().bodySize; - } - catch (AMQException e) - { - messageSize = 0; - } - - - if (messageSize >= maximumMessageSize) - { - listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]"); - return true; - } - } - return false; - } - - }, - QUEUE_DEPTH_ALERT - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - // Check for threshold queue depth in bytes - final long maximumQueueDepth = queue.getMaximumQueueDepth(); - - if(maximumQueueDepth != 0) - { - final long queueDepth = queue.getQueueDepth(); - - if (queueDepth >= maximumQueueDepth) - { - listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached."); - return true; - } - } - return false; - } - - }, - MESSAGE_AGE_ALERT - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - - final long maxMessageAge = queue.getMaximumMessageAge(); - if(maxMessageAge != 0) - { - final long currentTime = System.currentTimeMillis(); - final long thresholdTime = currentTime - maxMessageAge; - final long firstArrivalTime = queue.getOldestMessageArrivalTime(); - - if(firstArrivalTime < thresholdTime) - { - long oldestAge = currentTime - firstArrivalTime; - listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached."); - - return true; - } - } - return false; - - } - - } - ; - - private final boolean _messageSpecific; - - NotificationCheck() - { - this(false); - } - - NotificationCheck(boolean messageSpecific) - { - _messageSpecific = messageSpecific; - } - - public boolean isMessageSpecific() - { - return _messageSpecific; - } - - abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener); - -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; + +public enum NotificationCheck +{ + + MESSAGE_COUNT_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + int msgCount; + final long maximumMessageCount = queue.getMaximumMessageCount(); + if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount) + { + listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached."); + return true; + } + return false; + } + }, + MESSAGE_SIZE_ALERT(true) + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + final long maximumMessageSize = queue.getMaximumMessageSize(); + if(maximumMessageSize != 0) + { + // Check for threshold message size + long messageSize; + try + { + messageSize = (msg == null) ? 0 : msg.getContentHeaderBody().bodySize; + } + catch (AMQException e) + { + messageSize = 0; + } + + + if (messageSize >= maximumMessageSize) + { + listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]"); + return true; + } + } + return false; + } + + }, + QUEUE_DEPTH_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + // Check for threshold queue depth in bytes + final long maximumQueueDepth = queue.getMaximumQueueDepth(); + + if(maximumQueueDepth != 0) + { + final long queueDepth = queue.getQueueDepth(); + + if (queueDepth >= maximumQueueDepth) + { + listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached."); + return true; + } + } + return false; + } + + }, + MESSAGE_AGE_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + + final long maxMessageAge = queue.getMaximumMessageAge(); + if(maxMessageAge != 0) + { + final long currentTime = System.currentTimeMillis(); + final long thresholdTime = currentTime - maxMessageAge; + final long firstArrivalTime = queue.getOldestMessageArrivalTime(); + + if(firstArrivalTime < thresholdTime) + { + long oldestAge = currentTime - firstArrivalTime; + listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached."); + + return true; + } + } + return false; + + } + + } + ; + + private final boolean _messageSpecific; + + NotificationCheck() + { + this(false); + } + + NotificationCheck(boolean messageSpecific) + { + _messageSpecific = messageSpecific; + } + + public boolean isMessageSpecific() + { + return _messageSpecific; + } + + abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener); + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java index 959ca03c80..f1e7c98387 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java @@ -1,27 +1,27 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.queue; - - -public interface QueueNotificationListener -{ - void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg); -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + + +public interface QueueNotificationListener +{ + void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java index 85d804457e..70a76dd8c2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java @@ -1,44 +1,44 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.virtualhost; - -import java.io.IOException; - -import org.apache.qpid.server.management.MBeanAttribute; - -/** - * The management interface exposed to allow management of an Exchange. - * @version 0.1 - */ -public interface ManagedVirtualHost -{ - static final String TYPE = "VirtualHost"; - - /** - * Returns the name of the managed virtualHost. - * @return the name of the exchange. - * @throws java.io.IOException - */ - @MBeanAttribute(name="Name", description= TYPE + " Name") - String getName() throws IOException; - - -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.virtualhost; + +import java.io.IOException; + +import org.apache.qpid.server.management.MBeanAttribute; + +/** + * The management interface exposed to allow management of an Exchange. + * @version 0.1 + */ +public interface ManagedVirtualHost +{ + static final String TYPE = "VirtualHost"; + + /** + * Returns the name of the managed virtualHost. + * @return the name of the exchange. + * @throws java.io.IOException + */ + @MBeanAttribute(name="Name", description= TYPE + " Name") + String getName() throws IOException; + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java index 27917fac8a..9b1619c609 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java @@ -1,70 +1,70 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.virtualhost; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - - -public class VirtualHostRegistry -{ - private final Map _registry = new ConcurrentHashMap(); - - - private String _defaultVirtualHostName; - - public synchronized void registerVirtualHost(VirtualHost host) throws Exception - { - if(_registry.containsKey(host.getName())) - { - throw new Exception("Virtual Host with name " + host.getName() + " already registered."); - } - _registry.put(host.getName(),host); - } - - public VirtualHost getVirtualHost(String name) - { - if(name == null || name.trim().length() == 0 ) - { - name = getDefaultVirtualHostName(); - } - - return _registry.get(name); - } - - private String getDefaultVirtualHostName() - { - return _defaultVirtualHostName; - } - - public void setDefaultVirtualHostName(String defaultVirtualHostName) - { - _defaultVirtualHostName = defaultVirtualHostName; - } - - - public Collection getVirtualHosts() - { - return new ArrayList(_registry.values()); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.virtualhost; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public class VirtualHostRegistry +{ + private final Map _registry = new ConcurrentHashMap(); + + + private String _defaultVirtualHostName; + + public synchronized void registerVirtualHost(VirtualHost host) throws Exception + { + if(_registry.containsKey(host.getName())) + { + throw new Exception("Virtual Host with name " + host.getName() + " already registered."); + } + _registry.put(host.getName(),host); + } + + public VirtualHost getVirtualHost(String name) + { + if(name == null || name.trim().length() == 0 ) + { + name = getDefaultVirtualHostName(); + } + + return _registry.get(name); + } + + private String getDefaultVirtualHostName() + { + return _defaultVirtualHostName; + } + + public void setDefaultVirtualHostName(String defaultVirtualHostName) + { + _defaultVirtualHostName = defaultVirtualHostName; + } + + + public Collection getVirtualHosts() + { + return new ArrayList(_registry.values()); + } +} diff --git a/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java b/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java index 691020307a..ed8e4ad80f 100644 --- a/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java +++ b/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java @@ -1,343 +1,343 @@ -/* - * - * 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.sasl; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import javax.security.auth.callback.*; -import javax.security.sasl.Sasl; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslClientFactory; -import javax.security.sasl.SaslException; - -import org.apache.log4j.Logger; - -import org.apache.qpid.util.PrettyPrintingUtils; - -/** - * Implements a factory for generating Sasl client implementations. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Provide a list of supported encryption mechansims that meet a defined set of Sasl properties. - *
Provide the best matching supported Sasl mechanism to a preference ordered list of mechanisms and Sasl - * properties. - *
Perform username and password request call backs. CallBackHandler - *
- */ -public class ClientFactoryImpl implements SaslClientFactory -{ - //private static final Logger log = Logger.getLogger(ClientFactoryImpl.class); - - /** Holds the names of the supported encryption mechanisms. */ - private static final String[] SUPPORTED_MECHANISMS = { "CRAM-MD5", "PLAIN" }; - - /** Defines index of the CRAM-MD5 mechanism within the supported mechanisms. */ - private static final int CRAM_MD5 = 0; - - /** Defines index of the PLAIN mechanism within the supported mechanisms. */ - private static final int PLAIN = 1; - - /** Bit mapping of the no plain text policy. */ - private static final int NOPLAINTEXT = 0x0001; - - /** Bit mapping of the no susceptible active attacks policy. */ - private static final int NOACTIVE = 0x0002; - - /** Bit mapping of the no susceptible to dictionary attacks policy. */ - private static final int NODICTIONARY = 0x0004; - - /** Bit mapping of the must use forward secrecy between sessions policy. */ - private static final int FORWARD_SECRECY = 0x0008; - - /** Bit mapping of the no anonymous logins policy. */ - private static final int NOANONYMOUS = 0x0010; - - /** Bit mapping of the must pass credentials policy. */ - private static final int PASS_CREDENTIALS = 0x0020; - - /** Defines a mapping from supported mechanisms to supported policy flags. */ - private static final int[] SUPPPORTED_MECHANISMS_POLICIES = - { - NOPLAINTEXT | NOANONYMOUS, // CRAM-MD5 - NOANONYMOUS // PLAIN - }; - - /** - * Creates a SaslClient using the parameters supplied. - * - * @param mechanisms The non-null list of mechanism names to try. Each is the IANA-registered name of a SASL - * mechanism. (e.g. "GSSAPI", "CRAM-MD5"). - * @param authorizationId The possibly null protocol-dependent identification to be used for authorization. - * If null or empty, the server derives an authorization ID from the client's authentication - * credentials. When the SASL authentication completes successfully, the specified entity is - * granted access. - * @param protocol The non-null string name of the protocol for which the authentication is being performed - * (e.g., "ldap"). - * @param serverName The non-null fully qualified host name of the server to authenticate to. - * @param props The possibly null set of properties used to select the SASL mechanism and to configure the - * authentication exchange of the selected mechanism. See the Sasl class for a list - * of standard properties. Other, possibly mechanism-specific, properties can be included. - * Properties not relevant to the selected mechanism are ignored. - * @param cbh The possibly null callback handler to used by the SASL mechanisms to get further - * information from the application/library to complete the authentication. For example, a - * SASL mechanism might require the authentication ID, password and realm from the caller. - * The authentication ID is requested by using a NameCallback. - * The password is requested by using a PasswordCallback. - * The realm is requested by using a RealmChoiceCallback if there is a list - * of realms to choose from, and by using a RealmCallback if - * the realm must be entered. - * - * @return A possibly null SaslClient created using the parameters supplied. If null, this factory cannot - * produce a SaslClient using the parameters supplied. - * - * @throws javax.security.sasl.SaslException If cannot create a SaslClient because of an error. - */ - public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, - Map props, CallbackHandler cbh) throws SaslException - { - /*log.debug("public SaslClient createSaslClient(String[] mechanisms = " + PrettyPrintingUtils.printArray(mechanisms) - + ", String authorizationId = " + authorizationId + ", String protocol = " + protocol - + ", String serverName = " + serverName + ", Map props = " + props + ", CallbackHandler cbh): called");*/ - - // Get a list of all supported mechanisms that matched the required properties. - String[] matchingMechanisms = getMechanismNames(props); - //log.debug("matchingMechanisms = " + PrettyPrintingUtils.printArray(matchingMechanisms)); - - // Scan down the list of mechanisms until the first one that matches one of the matching supported mechanisms - // is found. - String chosenMechanism = null; - - for (int i = 0; i < mechanisms.length; i++) - { - String mechanism = mechanisms[i]; - - for (int j = 0; j < matchingMechanisms.length; j++) - { - String matchingMechanism = matchingMechanisms[j]; - - if (mechanism.equals(matchingMechanism)) - { - chosenMechanism = mechanism; - - break; - } - } - - // Stop scanning if a match has been found. - if (chosenMechanism != null) - { - break; - } - } - - // Check that a matching mechanism was found or return null otherwise. - if (chosenMechanism == null) - { - //log.debug("No matching mechanism could be found."); - - return null; - } - - // Instantiate an appropriate client type for the chosen mechanism. - if (chosenMechanism.equals(SUPPORTED_MECHANISMS[CRAM_MD5])) - { - Object[] uinfo = getUserInfo("CRAM-MD5", authorizationId, cbh); - - //log.debug("Using CRAM-MD5 mechanism."); - - return new CramMD5Client((String) uinfo[0], (byte[]) uinfo[1]); - } - else - { - Object[] uinfo = getUserInfo("PLAIN", authorizationId, cbh); - - //log.debug("Using PLAIN mechanism."); - - return new PlainClient(authorizationId, (String) uinfo[0], (byte[]) uinfo[1]); - } - } - - /** - * Returns an array of names of mechanisms that match the specified - * mechanism selection policies. - * - * @param props The possibly null set of properties used to specify the - * security policy of the SASL mechanisms. For example, if props - * contains the Sasl.POLICY_NOPLAINTEXT property with the value - * "true", then the factory must not return any SASL mechanisms - * that are susceptible to simple plain passive attacks. - * See the Sasl class for a complete list of policy properties. - * Non-policy related properties, if present in props, are ignored. - * - * @return A non-null array containing a IANA-registered SASL mechanism names. - */ - public String[] getMechanismNames(Map props) - { - //log.debug("public String[] getMechanismNames(Map props = " + props + "): called"); - - // Used to build up the valid mechanisms in. - List validMechanisms = new ArrayList(); - - // Transform the Sasl properties into a set of bit mapped flags indicating the required properties of the - // encryption mechanism employed. - int requiredFlags = bitMapSaslProperties(props); - //log.debug("requiredFlags = " + requiredFlags); - - // Scan down the list of supported mechanisms filtering in only those that satisfy all of the desired - // encryption properties. - for (int i = 0; i < SUPPORTED_MECHANISMS.length; i++) - { - int mechanismFlags = SUPPPORTED_MECHANISMS_POLICIES[i]; - //log.debug("mechanismFlags = " + mechanismFlags); - - // Check if the current mechanism contains all of the required flags. - if ((requiredFlags & ~mechanismFlags) == 0) - { - //log.debug("Mechanism " + SUPPORTED_MECHANISMS[i] + " meets the required properties."); - validMechanisms.add(SUPPORTED_MECHANISMS[i]); - } - } - - String[] result = (String[]) validMechanisms.toArray(new String[validMechanisms.size()]); - - //log.debug("result = " + PrettyPrintingUtils.printArray(result)); - - return result; - } - - /** - * Transforms a set of Sasl properties, defined using the property names in javax.security.sasl.Sasl, into - * a bit mapped set of property flags encoded using the bit mapping constants defined in this class. - * - * @param properties The Sasl properties to bit map. - * - * @return A set of bit mapped properties encoded in an integer. - */ - private int bitMapSaslProperties(Map properties) - { - //log.debug("private int bitMapSaslProperties(Map properties = " + properties + "): called"); - - int result = 0; - - // No flags set if no properties are set. - if (properties == null) - { - return result; - } - - if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOPLAINTEXT))) - { - result |= NOPLAINTEXT; - } - - if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOACTIVE))) - { - result |= NOACTIVE; - } - - if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NODICTIONARY))) - { - result |= NODICTIONARY; - } - - if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOANONYMOUS))) - { - result |= NOANONYMOUS; - } - - if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_FORWARD_SECRECY))) - { - result |= FORWARD_SECRECY; - } - - if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_PASS_CREDENTIALS))) - { - result |= PASS_CREDENTIALS; - } - - return result; - } - - /** - * Uses the specified call back handler to query for the users log in name and password. - * - * @param prefix A prefix to prepend onto the username and password queries. - * @param authorizationId The default autorhization name. - * @param cbh The call back handler. - * - * @return The username and password from the callback. - * - * @throws SaslException If the callback fails for any reason. - */ - private Object[] getUserInfo(String prefix, String authorizationId, CallbackHandler cbh) throws SaslException - { - // Check that the callback handler is defined. - if (cbh == null) - { - throw new SaslException("Callback handler to get username/password required."); - } - - try - { - String userPrompt = prefix + " authentication id: "; - String passwdPrompt = prefix + " password: "; - - NameCallback ncb = - (authorizationId == null) ? new NameCallback(userPrompt) : new NameCallback(userPrompt, authorizationId); - PasswordCallback pcb = new PasswordCallback(passwdPrompt, false); - - // Ask the call back handler to get the users name and password. - cbh.handle(new Callback[] { ncb, pcb }); - - char[] pw = pcb.getPassword(); - - byte[] bytepw; - String authId; - - if (pw != null) - { - bytepw = new String(pw).getBytes("UTF8"); - pcb.clearPassword(); - } - else - { - bytepw = null; - } - - authId = ncb.getName(); - - return new Object[] { authId, bytepw }; - } - catch (IOException e) - { - throw new SaslException("Cannot get password.", e); - } - catch (UnsupportedCallbackException e) - { - throw new SaslException("Cannot get userid/password.", e); - } - } -} +/* + * + * 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.sasl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.security.auth.callback.*; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; + +import org.apache.log4j.Logger; + +import org.apache.qpid.util.PrettyPrintingUtils; + +/** + * Implements a factory for generating Sasl client implementations. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Provide a list of supported encryption mechansims that meet a defined set of Sasl properties. + *
Provide the best matching supported Sasl mechanism to a preference ordered list of mechanisms and Sasl + * properties. + *
Perform username and password request call backs. CallBackHandler + *
+ */ +public class ClientFactoryImpl implements SaslClientFactory +{ + //private static final Logger log = Logger.getLogger(ClientFactoryImpl.class); + + /** Holds the names of the supported encryption mechanisms. */ + private static final String[] SUPPORTED_MECHANISMS = { "CRAM-MD5", "PLAIN" }; + + /** Defines index of the CRAM-MD5 mechanism within the supported mechanisms. */ + private static final int CRAM_MD5 = 0; + + /** Defines index of the PLAIN mechanism within the supported mechanisms. */ + private static final int PLAIN = 1; + + /** Bit mapping of the no plain text policy. */ + private static final int NOPLAINTEXT = 0x0001; + + /** Bit mapping of the no susceptible active attacks policy. */ + private static final int NOACTIVE = 0x0002; + + /** Bit mapping of the no susceptible to dictionary attacks policy. */ + private static final int NODICTIONARY = 0x0004; + + /** Bit mapping of the must use forward secrecy between sessions policy. */ + private static final int FORWARD_SECRECY = 0x0008; + + /** Bit mapping of the no anonymous logins policy. */ + private static final int NOANONYMOUS = 0x0010; + + /** Bit mapping of the must pass credentials policy. */ + private static final int PASS_CREDENTIALS = 0x0020; + + /** Defines a mapping from supported mechanisms to supported policy flags. */ + private static final int[] SUPPPORTED_MECHANISMS_POLICIES = + { + NOPLAINTEXT | NOANONYMOUS, // CRAM-MD5 + NOANONYMOUS // PLAIN + }; + + /** + * Creates a SaslClient using the parameters supplied. + * + * @param mechanisms The non-null list of mechanism names to try. Each is the IANA-registered name of a SASL + * mechanism. (e.g. "GSSAPI", "CRAM-MD5"). + * @param authorizationId The possibly null protocol-dependent identification to be used for authorization. + * If null or empty, the server derives an authorization ID from the client's authentication + * credentials. When the SASL authentication completes successfully, the specified entity is + * granted access. + * @param protocol The non-null string name of the protocol for which the authentication is being performed + * (e.g., "ldap"). + * @param serverName The non-null fully qualified host name of the server to authenticate to. + * @param props The possibly null set of properties used to select the SASL mechanism and to configure the + * authentication exchange of the selected mechanism. See the Sasl class for a list + * of standard properties. Other, possibly mechanism-specific, properties can be included. + * Properties not relevant to the selected mechanism are ignored. + * @param cbh The possibly null callback handler to used by the SASL mechanisms to get further + * information from the application/library to complete the authentication. For example, a + * SASL mechanism might require the authentication ID, password and realm from the caller. + * The authentication ID is requested by using a NameCallback. + * The password is requested by using a PasswordCallback. + * The realm is requested by using a RealmChoiceCallback if there is a list + * of realms to choose from, and by using a RealmCallback if + * the realm must be entered. + * + * @return A possibly null SaslClient created using the parameters supplied. If null, this factory cannot + * produce a SaslClient using the parameters supplied. + * + * @throws javax.security.sasl.SaslException If cannot create a SaslClient because of an error. + */ + public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, + Map props, CallbackHandler cbh) throws SaslException + { + /*log.debug("public SaslClient createSaslClient(String[] mechanisms = " + PrettyPrintingUtils.printArray(mechanisms) + + ", String authorizationId = " + authorizationId + ", String protocol = " + protocol + + ", String serverName = " + serverName + ", Map props = " + props + ", CallbackHandler cbh): called");*/ + + // Get a list of all supported mechanisms that matched the required properties. + String[] matchingMechanisms = getMechanismNames(props); + //log.debug("matchingMechanisms = " + PrettyPrintingUtils.printArray(matchingMechanisms)); + + // Scan down the list of mechanisms until the first one that matches one of the matching supported mechanisms + // is found. + String chosenMechanism = null; + + for (int i = 0; i < mechanisms.length; i++) + { + String mechanism = mechanisms[i]; + + for (int j = 0; j < matchingMechanisms.length; j++) + { + String matchingMechanism = matchingMechanisms[j]; + + if (mechanism.equals(matchingMechanism)) + { + chosenMechanism = mechanism; + + break; + } + } + + // Stop scanning if a match has been found. + if (chosenMechanism != null) + { + break; + } + } + + // Check that a matching mechanism was found or return null otherwise. + if (chosenMechanism == null) + { + //log.debug("No matching mechanism could be found."); + + return null; + } + + // Instantiate an appropriate client type for the chosen mechanism. + if (chosenMechanism.equals(SUPPORTED_MECHANISMS[CRAM_MD5])) + { + Object[] uinfo = getUserInfo("CRAM-MD5", authorizationId, cbh); + + //log.debug("Using CRAM-MD5 mechanism."); + + return new CramMD5Client((String) uinfo[0], (byte[]) uinfo[1]); + } + else + { + Object[] uinfo = getUserInfo("PLAIN", authorizationId, cbh); + + //log.debug("Using PLAIN mechanism."); + + return new PlainClient(authorizationId, (String) uinfo[0], (byte[]) uinfo[1]); + } + } + + /** + * Returns an array of names of mechanisms that match the specified + * mechanism selection policies. + * + * @param props The possibly null set of properties used to specify the + * security policy of the SASL mechanisms. For example, if props + * contains the Sasl.POLICY_NOPLAINTEXT property with the value + * "true", then the factory must not return any SASL mechanisms + * that are susceptible to simple plain passive attacks. + * See the Sasl class for a complete list of policy properties. + * Non-policy related properties, if present in props, are ignored. + * + * @return A non-null array containing a IANA-registered SASL mechanism names. + */ + public String[] getMechanismNames(Map props) + { + //log.debug("public String[] getMechanismNames(Map props = " + props + "): called"); + + // Used to build up the valid mechanisms in. + List validMechanisms = new ArrayList(); + + // Transform the Sasl properties into a set of bit mapped flags indicating the required properties of the + // encryption mechanism employed. + int requiredFlags = bitMapSaslProperties(props); + //log.debug("requiredFlags = " + requiredFlags); + + // Scan down the list of supported mechanisms filtering in only those that satisfy all of the desired + // encryption properties. + for (int i = 0; i < SUPPORTED_MECHANISMS.length; i++) + { + int mechanismFlags = SUPPPORTED_MECHANISMS_POLICIES[i]; + //log.debug("mechanismFlags = " + mechanismFlags); + + // Check if the current mechanism contains all of the required flags. + if ((requiredFlags & ~mechanismFlags) == 0) + { + //log.debug("Mechanism " + SUPPORTED_MECHANISMS[i] + " meets the required properties."); + validMechanisms.add(SUPPORTED_MECHANISMS[i]); + } + } + + String[] result = (String[]) validMechanisms.toArray(new String[validMechanisms.size()]); + + //log.debug("result = " + PrettyPrintingUtils.printArray(result)); + + return result; + } + + /** + * Transforms a set of Sasl properties, defined using the property names in javax.security.sasl.Sasl, into + * a bit mapped set of property flags encoded using the bit mapping constants defined in this class. + * + * @param properties The Sasl properties to bit map. + * + * @return A set of bit mapped properties encoded in an integer. + */ + private int bitMapSaslProperties(Map properties) + { + //log.debug("private int bitMapSaslProperties(Map properties = " + properties + "): called"); + + int result = 0; + + // No flags set if no properties are set. + if (properties == null) + { + return result; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOPLAINTEXT))) + { + result |= NOPLAINTEXT; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOACTIVE))) + { + result |= NOACTIVE; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NODICTIONARY))) + { + result |= NODICTIONARY; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOANONYMOUS))) + { + result |= NOANONYMOUS; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_FORWARD_SECRECY))) + { + result |= FORWARD_SECRECY; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_PASS_CREDENTIALS))) + { + result |= PASS_CREDENTIALS; + } + + return result; + } + + /** + * Uses the specified call back handler to query for the users log in name and password. + * + * @param prefix A prefix to prepend onto the username and password queries. + * @param authorizationId The default autorhization name. + * @param cbh The call back handler. + * + * @return The username and password from the callback. + * + * @throws SaslException If the callback fails for any reason. + */ + private Object[] getUserInfo(String prefix, String authorizationId, CallbackHandler cbh) throws SaslException + { + // Check that the callback handler is defined. + if (cbh == null) + { + throw new SaslException("Callback handler to get username/password required."); + } + + try + { + String userPrompt = prefix + " authentication id: "; + String passwdPrompt = prefix + " password: "; + + NameCallback ncb = + (authorizationId == null) ? new NameCallback(userPrompt) : new NameCallback(userPrompt, authorizationId); + PasswordCallback pcb = new PasswordCallback(passwdPrompt, false); + + // Ask the call back handler to get the users name and password. + cbh.handle(new Callback[] { ncb, pcb }); + + char[] pw = pcb.getPassword(); + + byte[] bytepw; + String authId; + + if (pw != null) + { + bytepw = new String(pw).getBytes("UTF8"); + pcb.clearPassword(); + } + else + { + bytepw = null; + } + + authId = ncb.getName(); + + return new Object[] { authId, bytepw }; + } + catch (IOException e) + { + throw new SaslException("Cannot get password.", e); + } + catch (UnsupportedCallbackException e) + { + throw new SaslException("Cannot get userid/password.", e); + } + } +} diff --git a/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java b/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java index 7d15f03329..4f890ffed5 100644 --- a/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java +++ b/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java @@ -1,347 +1,347 @@ -/* - * - * 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.sasl; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import javax.security.sasl.Sasl; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; - -/** - * Implements the CRAM-MD5 SASL mechanism. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Concatenate the user id and password hashed by a challenge string as the challenge response. - *
Ensure password is wiped once a challenge has been processed. - *
- */ -public class CramMD5Client implements SaslClient -{ - /** Defines the HMAC block size. */ - private static final int MD5_BLOCKSIZE = 64; - - /** Flag used to indicate that the authentication has completed. */ - private boolean completed = false; - - private String authenticationId; - private byte[] password; - - /** - * Creates a PLAIN SASL client for an authorization id, authentication id and password. - * - * @param authenticationId The authentication id. - * @param password The password. - * - * @throws SaslException If the authentication id or password is null. - */ - CramMD5Client(String authenticationId, byte[] password) throws SaslException - { - // Check that a username and password are specified. - if ((authenticationId == null) || (password == null)) - { - throw new SaslException("CRAM: authentication id and password must be specified"); - } - - // Keep the log in credentials. - this.authenticationId = authenticationId; - this.password = password; - } - - /** - * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). - * - * @return A non-null string representing the IANA-registered mechanism name. - */ - public String getMechanismName() - { - return "CRAM-MD5"; - } - - /** - * Determines whether this mechanism has an optional initial response. If true, caller should call - * evaluateChallenge() with an empty array to get the initial response. - * - *

CRAM-MD5 has no intial response. - * - * @return true if this mechanism has an initial response. - */ - public boolean hasInitialResponse() - { - return false; - } - - /** - * Evaluates the challenge data and generates a response. If a challenge is received from the server during the - * authentication process, this method is called to prepare an appropriate next response to submit to the server. - * - *

The initial response for the SASL command, for the CRAM-MD5 mechanism is the concatenation of authentication - * ID and password HMAC-MD5 hashed by the server challenge. - * - * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. - * - * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" - * status and the challenge only contains data for the client to update its state and no response - * needs to be sent to the server. The response is a zero-length byte array if the client is to send a - * response with no data. - * - * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a - * response. - */ - public byte[] evaluateChallenge(byte[] challenge) throws SaslException - { - // Check that the authentication has not already been performed. - if (completed) - { - throw new IllegalStateException("CRAM-MD5 authentication already completed."); - } - - // Check if the password is null, this will be the case if a previous attempt to authenticated failed. - if (password == null) - { - throw new IllegalStateException("CRAM-MD5 authentication previously aborted due to error."); - } - - // Generate a keyed-MD5 digest from the user's password keyed by the challenge bytes. - try - { - String digest = hmac_md5(password, challenge); - String result = authenticationId + " " + digest; - - completed = true; - - return result.getBytes("UTF8"); - } - catch (java.security.NoSuchAlgorithmException e) - { - throw new SaslException("MD5 algorithm not available on platform", e); - } - catch (java.io.UnsupportedEncodingException e) - { - throw new SaslException("UTF8 not available on platform", e); - } - finally - { - clearPassword(); - } - } - - /** - * Determines whether the authentication exchange has completed. This method may be called at any time, but - * typically, it will not be called until the caller has received indication from the server (in a protocol-specific - * manner) that the exchange has completed. - * - * @return true if the authentication exchange has completed; false otherwise. - */ - public boolean isComplete() - { - return completed; - } - - /** - * Unwraps a byte array received from the server. This method can be called only after the authentication exchange - * has completed (i.e., when isComplete() returns true) and only if the authentication exchange has - * negotiated integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is - * thrown. - * - *

incoming is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet - * field that represents the length. offset and len specify the portion of incoming - * to use. - * - * @param incoming A non-null byte array containing the encoded bytes from the server. - * @param offset The starting position at incoming of the bytes to use. - * @param len The number of bytes from incoming to use. - * - * @return A non-null byte array containing the decoded bytes. - * - * @throws javax.security.sasl.SaslException If incoming cannot be successfully unwrapped. - * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of - * protection has neither integrity nor privacy. - */ - public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException - { - throw new SaslException("CRAM-MD5 does not support quality of protection."); - } - - /** - * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has - * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated - * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. - * - *

The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the - * leading four octet field that represents the length. offset and len specify the portion of - * outgoing to use. - * - * @param outgoing A non-null byte array containing the bytes to encode. - * @param offset The starting position at outgoing of the bytes to use. - * @param len The number of bytes from outgoing to use. - * - * @return A non-null byte array containing the encoded bytes. - * - * @throws javax.security.sasl.SaslException If outgoing cannot be successfully wrapped. - * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of - * protection has neither integrity nor privacy. - */ - public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException - { - throw new SaslException("CRAM-MD5 does not support quality of protection."); - } - - /** - * Retrieves the negotiated property. This method can be called only after the authentication exchange has - * completed (i.e., when isComplete() returns true); otherwise, an IllegalStateException is thrown. - * - * @param propName The non-null property name. - * - * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to - * this mechanism. - * - * @throws IllegalStateException If this authentication exchange has not completed. - */ - public Object getNegotiatedProperty(String propName) - { - if (completed) - { - if (propName.equals(Sasl.QOP)) - { - return "auth"; - } - else - { - return null; - } - } - else - { - throw new IllegalStateException("CRAM-MD5 authentication not completed"); - } - } - - /** - * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this - * method invalidates the SaslClient instance. This method is idempotent. - * - * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. - */ - public void dispose() throws SaslException - { } - - /* - * Hashes its input arguments according to HMAC-MD5 (RFC 2104) and returns the resulting digest in its ASCII - * representation. - * - *

The HMAC-MD5 function is described as follows: - *

-     *     MD5(key XOR opad, MD5(key XOR ipad, text))
-     * 
- * - *

Where key is an n byte key, ipad is the byte 0x36 repeated 64 times, opad is the byte 0x5c repeated 64 times - * and text is the data to be protected. - * - * @param key The key to hash by. - * @param text The plain text to hash. - * - * @return The hashed text. - * - * @throws NoSuchAlgorithmException If the Java platform does not supply an MD5 implementation. - */ - private static final String hmac_md5(byte[] key, byte[] text) throws NoSuchAlgorithmException - { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - - /* digest the key if longer than 64 bytes */ - if (key.length > 64) - { - key = md5.digest(key); - } - - byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */ - byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */ - byte[] digest; - int i; - - /* store key in pads */ - for (i = 0; i < MD5_BLOCKSIZE; i++) - { - for (; i < key.length; i++) - { - ipad[i] = key[i]; - opad[i] = key[i]; - } - - ipad[i] = 0x00; - opad[i] = 0x00; - } - - /* XOR key with pads */ - for (i = 0; i < MD5_BLOCKSIZE; i++) - { - ipad[i] ^= 0x36; - opad[i] ^= 0x5c; - } - - /* inner MD5 */ - md5.update(ipad); - md5.update(text); - digest = md5.digest(); - - /* outer MD5 */ - md5.update(opad); - md5.update(digest); - digest = md5.digest(); - - // Get character representation of digest - StringBuffer digestString = new StringBuffer(); - - for (i = 0; i < digest.length; i++) - { - if ((digest[i] & 0x000000ff) < 0x10) - { - digestString.append("0" + Integer.toHexString(digest[i] & 0x000000ff)); - } - else - { - digestString.append(Integer.toHexString(digest[i] & 0x000000ff)); - } - } - - return (digestString.toString()); - } - - /** - * Overwrites the password with zeros. - */ - private void clearPassword() - { - if (password != null) - { - // Zero out password. - for (int i = 0; i < password.length; i++) - { - password[i] = (byte) 0; - } - - password = null; - } - } -} +/* + * + * 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.sasl; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * Implements the CRAM-MD5 SASL mechanism. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Concatenate the user id and password hashed by a challenge string as the challenge response. + *
Ensure password is wiped once a challenge has been processed. + *
+ */ +public class CramMD5Client implements SaslClient +{ + /** Defines the HMAC block size. */ + private static final int MD5_BLOCKSIZE = 64; + + /** Flag used to indicate that the authentication has completed. */ + private boolean completed = false; + + private String authenticationId; + private byte[] password; + + /** + * Creates a PLAIN SASL client for an authorization id, authentication id and password. + * + * @param authenticationId The authentication id. + * @param password The password. + * + * @throws SaslException If the authentication id or password is null. + */ + CramMD5Client(String authenticationId, byte[] password) throws SaslException + { + // Check that a username and password are specified. + if ((authenticationId == null) || (password == null)) + { + throw new SaslException("CRAM: authentication id and password must be specified"); + } + + // Keep the log in credentials. + this.authenticationId = authenticationId; + this.password = password; + } + + /** + * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). + * + * @return A non-null string representing the IANA-registered mechanism name. + */ + public String getMechanismName() + { + return "CRAM-MD5"; + } + + /** + * Determines whether this mechanism has an optional initial response. If true, caller should call + * evaluateChallenge() with an empty array to get the initial response. + * + *

CRAM-MD5 has no intial response. + * + * @return true if this mechanism has an initial response. + */ + public boolean hasInitialResponse() + { + return false; + } + + /** + * Evaluates the challenge data and generates a response. If a challenge is received from the server during the + * authentication process, this method is called to prepare an appropriate next response to submit to the server. + * + *

The initial response for the SASL command, for the CRAM-MD5 mechanism is the concatenation of authentication + * ID and password HMAC-MD5 hashed by the server challenge. + * + * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. + * + * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" + * status and the challenge only contains data for the client to update its state and no response + * needs to be sent to the server. The response is a zero-length byte array if the client is to send a + * response with no data. + * + * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a + * response. + */ + public byte[] evaluateChallenge(byte[] challenge) throws SaslException + { + // Check that the authentication has not already been performed. + if (completed) + { + throw new IllegalStateException("CRAM-MD5 authentication already completed."); + } + + // Check if the password is null, this will be the case if a previous attempt to authenticated failed. + if (password == null) + { + throw new IllegalStateException("CRAM-MD5 authentication previously aborted due to error."); + } + + // Generate a keyed-MD5 digest from the user's password keyed by the challenge bytes. + try + { + String digest = hmac_md5(password, challenge); + String result = authenticationId + " " + digest; + + completed = true; + + return result.getBytes("UTF8"); + } + catch (java.security.NoSuchAlgorithmException e) + { + throw new SaslException("MD5 algorithm not available on platform", e); + } + catch (java.io.UnsupportedEncodingException e) + { + throw new SaslException("UTF8 not available on platform", e); + } + finally + { + clearPassword(); + } + } + + /** + * Determines whether the authentication exchange has completed. This method may be called at any time, but + * typically, it will not be called until the caller has received indication from the server (in a protocol-specific + * manner) that the exchange has completed. + * + * @return true if the authentication exchange has completed; false otherwise. + */ + public boolean isComplete() + { + return completed; + } + + /** + * Unwraps a byte array received from the server. This method can be called only after the authentication exchange + * has completed (i.e., when isComplete() returns true) and only if the authentication exchange has + * negotiated integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is + * thrown. + * + *

incoming is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet + * field that represents the length. offset and len specify the portion of incoming + * to use. + * + * @param incoming A non-null byte array containing the encoded bytes from the server. + * @param offset The starting position at incoming of the bytes to use. + * @param len The number of bytes from incoming to use. + * + * @return A non-null byte array containing the decoded bytes. + * + * @throws javax.security.sasl.SaslException If incoming cannot be successfully unwrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("CRAM-MD5 does not support quality of protection."); + } + + /** + * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. + * + *

The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the + * leading four octet field that represents the length. offset and len specify the portion of + * outgoing to use. + * + * @param outgoing A non-null byte array containing the bytes to encode. + * @param offset The starting position at outgoing of the bytes to use. + * @param len The number of bytes from outgoing to use. + * + * @return A non-null byte array containing the encoded bytes. + * + * @throws javax.security.sasl.SaslException If outgoing cannot be successfully wrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("CRAM-MD5 does not support quality of protection."); + } + + /** + * Retrieves the negotiated property. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true); otherwise, an IllegalStateException is thrown. + * + * @param propName The non-null property name. + * + * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to + * this mechanism. + * + * @throws IllegalStateException If this authentication exchange has not completed. + */ + public Object getNegotiatedProperty(String propName) + { + if (completed) + { + if (propName.equals(Sasl.QOP)) + { + return "auth"; + } + else + { + return null; + } + } + else + { + throw new IllegalStateException("CRAM-MD5 authentication not completed"); + } + } + + /** + * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this + * method invalidates the SaslClient instance. This method is idempotent. + * + * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. + */ + public void dispose() throws SaslException + { } + + /* + * Hashes its input arguments according to HMAC-MD5 (RFC 2104) and returns the resulting digest in its ASCII + * representation. + * + *

The HMAC-MD5 function is described as follows: + *

+     *     MD5(key XOR opad, MD5(key XOR ipad, text))
+     * 
+ * + *

Where key is an n byte key, ipad is the byte 0x36 repeated 64 times, opad is the byte 0x5c repeated 64 times + * and text is the data to be protected. + * + * @param key The key to hash by. + * @param text The plain text to hash. + * + * @return The hashed text. + * + * @throws NoSuchAlgorithmException If the Java platform does not supply an MD5 implementation. + */ + private static final String hmac_md5(byte[] key, byte[] text) throws NoSuchAlgorithmException + { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + + /* digest the key if longer than 64 bytes */ + if (key.length > 64) + { + key = md5.digest(key); + } + + byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */ + byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */ + byte[] digest; + int i; + + /* store key in pads */ + for (i = 0; i < MD5_BLOCKSIZE; i++) + { + for (; i < key.length; i++) + { + ipad[i] = key[i]; + opad[i] = key[i]; + } + + ipad[i] = 0x00; + opad[i] = 0x00; + } + + /* XOR key with pads */ + for (i = 0; i < MD5_BLOCKSIZE; i++) + { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + /* inner MD5 */ + md5.update(ipad); + md5.update(text); + digest = md5.digest(); + + /* outer MD5 */ + md5.update(opad); + md5.update(digest); + digest = md5.digest(); + + // Get character representation of digest + StringBuffer digestString = new StringBuffer(); + + for (i = 0; i < digest.length; i++) + { + if ((digest[i] & 0x000000ff) < 0x10) + { + digestString.append("0" + Integer.toHexString(digest[i] & 0x000000ff)); + } + else + { + digestString.append(Integer.toHexString(digest[i] & 0x000000ff)); + } + } + + return (digestString.toString()); + } + + /** + * Overwrites the password with zeros. + */ + private void clearPassword() + { + if (password != null) + { + // Zero out password. + for (int i = 0; i < password.length; i++) + { + password[i] = (byte) 0; + } + + password = null; + } + } +} diff --git a/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java b/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java index 97e607c57e..f5a9f150bb 100644 --- a/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java +++ b/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java @@ -1,275 +1,275 @@ -/* - * - * 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.sasl; - -import javax.security.sasl.Sasl; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; - -/** - * Implements the PLAIN SASL mechanism. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Concatenate the user id and password in plain text as the challenge respose. - *
Ensure password is wiped once a challenge has been processed. - *
- */ -public class PlainClient implements SaslClient -{ - /** Flag used to indicate that the authentication has completed. */ - private boolean completed = false; - - private String authorizationId; - private String authenticationId; - private byte[] password; - - private static byte SEPERATOR = 0; // US-ASCII - - /** - * Creates a PLAIN SASL client for an authorization id, authentication id and password. - * - * @param authorizationId The authorization id. May be null. - * @param authenticationId The authentication id. - * @param password The password. - * - * @throws SaslException If the authentication id or password is null. - */ - PlainClient(String authorizationId, String authenticationId, byte[] password) throws SaslException - { - // Check that a username and password are specified. - if ((authenticationId == null) || (password == null)) - { - throw new SaslException("PLAIN: authentication ID and password must be specified"); - } - - // Keep the log in credentials. - this.authorizationId = authorizationId; - this.authenticationId = authenticationId; - this.password = password; - } - - /** - * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). - * - * @return A non-null string representing the IANA-registered mechanism name. - */ - public String getMechanismName() - { - return "PLAIN"; - } - - /** - * Determines whether this mechanism has an optional initial response. If true, caller should call - * evaluateChallenge() with an empty array to get the initial response. - * - * @return true if this mechanism has an initial response. - */ - public boolean hasInitialResponse() - { - return true; - } - - /** - * Evaluates the challenge data and generates a response. If a challenge is received from the server during the - * authentication process, this method is called to prepare an appropriate next response to submit to the server. - * - *

The initial response for the SASL command, for the PLAIN mechanism is the concatenation of authorization ID, - * authentication ID and password, with each component separated by the US-ASCII byte. - * - * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. - * - * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" - * status and the challenge only contains data for the client to update its state and no response - * needs to be sent to the server. The response is a zero-length byte array if the client is to send a - * response with no data. - * - * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a - * response. - */ - public byte[] evaluateChallenge(byte[] challenge) throws SaslException - { - // Check that the authentication has not already been performed. - if (completed) - { - throw new IllegalStateException("PLAIN authentication already completed"); - } - - try - { - // Get the authorization and authentication ids in bytes. - byte[] authorizationBytes = (authorizationId != null) ? authorizationId.getBytes("UTF8") : null; - byte[] authenticationBytes = authenticationId.getBytes("UTF8"); - - // Create an array big enough to hold the results. - byte[] result = - new byte[password.length + authenticationBytes.length + 2 - + ((authorizationBytes == null) ? 0 : authorizationBytes.length)]; - - // Copy the authorization id, authentication id and password into the results. - int pos = 0; - if (authorizationBytes != null) - { - System.arraycopy(authorizationBytes, 0, result, 0, authorizationBytes.length); - pos = authorizationBytes.length; - } - - result[pos++] = SEPERATOR; - System.arraycopy(authenticationBytes, 0, result, pos, authenticationBytes.length); - - pos += authenticationBytes.length; - result[pos++] = SEPERATOR; - - System.arraycopy(password, 0, result, pos, password.length); - - completed = true; - - return result; - } - catch (java.io.UnsupportedEncodingException e) - { - throw new SaslException("Cannot get UTF-8 encoding of ids", e); - } - finally - { - clearPassword(); - } - } - - /** - * Determines whether the authentication exchange has completed. This method may be called at any time, but - * typically, it will not be called until the caller has received indication from the server (in a protocol-specific - * manner) that the exchange has completed. - * - * @return true if the authentication exchange has completed; false otherwise. - */ - public boolean isComplete() - { - return completed; - } - - /** - * Unwraps a byte array received from the server. This method can be called only after the authentication exchange has - * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated - * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. - * - *

incoming is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet - * field that represents the length. offset and len specify the portion of incoming - * to use. - * - * @param incoming A non-null byte array containing the encoded bytes - * from the server. - * @param offset The starting position at incoming of the bytes to use. - * @param len The number of bytes from incoming to use. - * - * @return A non-null byte array containing the decoded bytes. - * - * @throws javax.security.sasl.SaslException If incoming cannot be successfully unwrapped. - * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of - * protection has neither integrity nor privacy. - */ - public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException - { - throw new SaslException("PLAIN does not support quality of protection."); - } - - /** - * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has - * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated - * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. - * - *

The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the - * leading four octet field that represents the length. offset and len specify the portion of - * outgoing to use. - * - * @param outgoing A non-null byte array containing the bytes to encode. - * @param offset The starting position at outgoing of the bytes to use. - * @param len The number of bytes from outgoing to use. - * - * @return A non-null byte array containing the encoded bytes. - * - * @throws javax.security.sasl.SaslException If outgoing cannot be successfully wrapped. - * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of - * protection has neither integrity nor privacy. - */ - public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException - { - throw new SaslException("PLAIN does not support quality of protection."); - } - - /** - * Retrieves the negotiated property. This method can be called only after the authentication exchange has - * completed (i.e., when isComplete() returns true); otherwise, an IllegalStateException is thrown. - * - * @param propName The non-null property name. - * - * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to - * this mechanism. - * - * @throws IllegalStateException If this authentication exchange has not completed. - */ - public Object getNegotiatedProperty(String propName) - { - if (completed) - { - if (propName.equals(Sasl.QOP)) - { - return "auth"; - } - else - { - return null; - } - } - else - { - throw new IllegalStateException("PLAIN authentication not completed"); - } - } - - /** - * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this - * method invalidates the SaslClient instance. This method is idempotent. - * - * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. - */ - public void dispose() throws SaslException - { - clearPassword(); - } - - /** - * Overwrites the password with zeros. - */ - private void clearPassword() - { - if (password != null) - { - // Zero out password. - for (int i = 0; i < password.length; i++) - { - password[i] = (byte) 0; - } - - password = null; - } - } -} +/* + * + * 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.sasl; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * Implements the PLAIN SASL mechanism. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Concatenate the user id and password in plain text as the challenge respose. + *
Ensure password is wiped once a challenge has been processed. + *
+ */ +public class PlainClient implements SaslClient +{ + /** Flag used to indicate that the authentication has completed. */ + private boolean completed = false; + + private String authorizationId; + private String authenticationId; + private byte[] password; + + private static byte SEPERATOR = 0; // US-ASCII + + /** + * Creates a PLAIN SASL client for an authorization id, authentication id and password. + * + * @param authorizationId The authorization id. May be null. + * @param authenticationId The authentication id. + * @param password The password. + * + * @throws SaslException If the authentication id or password is null. + */ + PlainClient(String authorizationId, String authenticationId, byte[] password) throws SaslException + { + // Check that a username and password are specified. + if ((authenticationId == null) || (password == null)) + { + throw new SaslException("PLAIN: authentication ID and password must be specified"); + } + + // Keep the log in credentials. + this.authorizationId = authorizationId; + this.authenticationId = authenticationId; + this.password = password; + } + + /** + * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). + * + * @return A non-null string representing the IANA-registered mechanism name. + */ + public String getMechanismName() + { + return "PLAIN"; + } + + /** + * Determines whether this mechanism has an optional initial response. If true, caller should call + * evaluateChallenge() with an empty array to get the initial response. + * + * @return true if this mechanism has an initial response. + */ + public boolean hasInitialResponse() + { + return true; + } + + /** + * Evaluates the challenge data and generates a response. If a challenge is received from the server during the + * authentication process, this method is called to prepare an appropriate next response to submit to the server. + * + *

The initial response for the SASL command, for the PLAIN mechanism is the concatenation of authorization ID, + * authentication ID and password, with each component separated by the US-ASCII byte. + * + * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. + * + * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" + * status and the challenge only contains data for the client to update its state and no response + * needs to be sent to the server. The response is a zero-length byte array if the client is to send a + * response with no data. + * + * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a + * response. + */ + public byte[] evaluateChallenge(byte[] challenge) throws SaslException + { + // Check that the authentication has not already been performed. + if (completed) + { + throw new IllegalStateException("PLAIN authentication already completed"); + } + + try + { + // Get the authorization and authentication ids in bytes. + byte[] authorizationBytes = (authorizationId != null) ? authorizationId.getBytes("UTF8") : null; + byte[] authenticationBytes = authenticationId.getBytes("UTF8"); + + // Create an array big enough to hold the results. + byte[] result = + new byte[password.length + authenticationBytes.length + 2 + + ((authorizationBytes == null) ? 0 : authorizationBytes.length)]; + + // Copy the authorization id, authentication id and password into the results. + int pos = 0; + if (authorizationBytes != null) + { + System.arraycopy(authorizationBytes, 0, result, 0, authorizationBytes.length); + pos = authorizationBytes.length; + } + + result[pos++] = SEPERATOR; + System.arraycopy(authenticationBytes, 0, result, pos, authenticationBytes.length); + + pos += authenticationBytes.length; + result[pos++] = SEPERATOR; + + System.arraycopy(password, 0, result, pos, password.length); + + completed = true; + + return result; + } + catch (java.io.UnsupportedEncodingException e) + { + throw new SaslException("Cannot get UTF-8 encoding of ids", e); + } + finally + { + clearPassword(); + } + } + + /** + * Determines whether the authentication exchange has completed. This method may be called at any time, but + * typically, it will not be called until the caller has received indication from the server (in a protocol-specific + * manner) that the exchange has completed. + * + * @return true if the authentication exchange has completed; false otherwise. + */ + public boolean isComplete() + { + return completed; + } + + /** + * Unwraps a byte array received from the server. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. + * + *

incoming is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet + * field that represents the length. offset and len specify the portion of incoming + * to use. + * + * @param incoming A non-null byte array containing the encoded bytes + * from the server. + * @param offset The starting position at incoming of the bytes to use. + * @param len The number of bytes from incoming to use. + * + * @return A non-null byte array containing the decoded bytes. + * + * @throws javax.security.sasl.SaslException If incoming cannot be successfully unwrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("PLAIN does not support quality of protection."); + } + + /** + * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. + * + *

The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the + * leading four octet field that represents the length. offset and len specify the portion of + * outgoing to use. + * + * @param outgoing A non-null byte array containing the bytes to encode. + * @param offset The starting position at outgoing of the bytes to use. + * @param len The number of bytes from outgoing to use. + * + * @return A non-null byte array containing the encoded bytes. + * + * @throws javax.security.sasl.SaslException If outgoing cannot be successfully wrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("PLAIN does not support quality of protection."); + } + + /** + * Retrieves the negotiated property. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true); otherwise, an IllegalStateException is thrown. + * + * @param propName The non-null property name. + * + * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to + * this mechanism. + * + * @throws IllegalStateException If this authentication exchange has not completed. + */ + public Object getNegotiatedProperty(String propName) + { + if (completed) + { + if (propName.equals(Sasl.QOP)) + { + return "auth"; + } + else + { + return null; + } + } + else + { + throw new IllegalStateException("PLAIN authentication not completed"); + } + } + + /** + * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this + * method invalidates the SaslClient instance. This method is idempotent. + * + * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. + */ + public void dispose() throws SaslException + { + clearPassword(); + } + + /** + * Overwrites the password with zeros. + */ + private void clearPassword() + { + if (password != null) + { + // Zero out password. + for (int i = 0; i < password.length; i++) + { + password[i] = (byte) 0; + } + + password = null; + } + } +} diff --git a/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java b/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java index f9a5c42c3d..ef075a0cad 100644 --- a/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java +++ b/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java @@ -1,61 +1,61 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.sasl; - -import java.security.AccessController; -import java.security.PrivilegedAction; - -import org.apache.log4j.Logger; - -/** - * A SASL provider for Java 1.4. Declares the capabilities of this implementation, which supports PLAIN and CRAM-MD5 - * client implementations only. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Declare PLAIN SASL support. - *
Declare CRAM-MD5 SASL support. - *
- */ -public class Provider extends java.security.Provider -{ - //Logger log = Logger.getLogger(Provider.class); - - private static final String info = "Qpid SASL provider" + "(implements client mechanisms for: PLAIN, CRAM-MD5)"; - - public Provider() - { - super("QpidSASL", 1.4, info); - - //log.debug("public Provider(): called"); - - AccessController.doPrivileged(new PrivilegedAction() - { - public Object run() - { - put("SaslClientFactory.PLAIN", "org.apache.qpid.sasl.ClientFactoryImpl"); - put("SaslClientFactory.CRAM-MD5", "org.apache.qpid.sasl.ClientFactoryImpl"); - - return null; - } - }); - } -} +/* + * + * 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.sasl; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.apache.log4j.Logger; + +/** + * A SASL provider for Java 1.4. Declares the capabilities of this implementation, which supports PLAIN and CRAM-MD5 + * client implementations only. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Declare PLAIN SASL support. + *
Declare CRAM-MD5 SASL support. + *
+ */ +public class Provider extends java.security.Provider +{ + //Logger log = Logger.getLogger(Provider.class); + + private static final String info = "Qpid SASL provider" + "(implements client mechanisms for: PLAIN, CRAM-MD5)"; + + public Provider() + { + super("QpidSASL", 1.4, info); + + //log.debug("public Provider(): called"); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + put("SaslClientFactory.PLAIN", "org.apache.qpid.sasl.ClientFactoryImpl"); + put("SaslClientFactory.CRAM-MD5", "org.apache.qpid.sasl.ClientFactoryImpl"); + + return null; + } + }); + } +} diff --git a/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java b/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java index 468beda5b5..77cc79bc49 100644 --- a/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java +++ b/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java @@ -1,66 +1,66 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.integration.client; - -import junit.framework.Assert; -import junit.framework.TestCase; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import org.apache.qpid.client.AMQConnection; - -/** - * Implements a trivial broker connection test, to a broker on the default port on localhost, to check that SASL - * authentication works. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Check that a connection to a broker can be established. - *
- */ -public class ConnectionTest extends TestCase -{ - private String BROKER_URL = "tcp://localhost:5672"; - - public ConnectionTest(String name) - { - super(name); - } - - /** Check that a connection to a broker can be established. */ - public void testConnection() throws Exception - { - // Open a connection to the broker and close it again. - AMQConnection conn = new AMQConnection(BROKER_URL, "guest", "guest", "clientid", "test"); - conn.close(); - } - - protected void setUp() - { - NDC.push(getName()); - } - - protected void tearDown() - { - NDC.pop(); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.integration.client; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.client.AMQConnection; + +/** + * Implements a trivial broker connection test, to a broker on the default port on localhost, to check that SASL + * authentication works. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Check that a connection to a broker can be established. + *
+ */ +public class ConnectionTest extends TestCase +{ + private String BROKER_URL = "tcp://localhost:5672"; + + public ConnectionTest(String name) + { + super(name); + } + + /** Check that a connection to a broker can be established. */ + public void testConnection() throws Exception + { + // Open a connection to the broker and close it again. + AMQConnection conn = new AMQConnection(BROKER_URL, "guest", "guest", "clientid", "test"); + conn.close(); + } + + protected void setUp() + { + NDC.push(getName()); + } + + protected void tearDown() + { + NDC.pop(); + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java b/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java index 93f10761e2..7e257e0c20 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java @@ -1,26 +1,26 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.client; - -public interface AMQSessionAdapter -{ - public AMQSession getSession(); -} +/* + * + * 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.client; + +public interface AMQSessionAdapter +{ + public AMQSession getSession(); +} diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java b/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java index 768e2862b3..fa2afb3ee4 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java @@ -1,40 +1,40 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.client; - -import org.apache.qpid.framing.AMQShortString; - -public class AMQUndefinedDestination extends AMQDestination -{ - - private static final AMQShortString UNKNOWN_EXCHANGE_CLASS = new AMQShortString("unknown"); - - - public AMQUndefinedDestination(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName) - { - super(exchange, UNKNOWN_EXCHANGE_CLASS, routingKey, queueName); - } - - public boolean isNameRequired() - { - return getAMQQueueName() == null; - } -} +/* + * + * 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.client; + +import org.apache.qpid.framing.AMQShortString; + +public class AMQUndefinedDestination extends AMQDestination +{ + + private static final AMQShortString UNKNOWN_EXCHANGE_CLASS = new AMQShortString("unknown"); + + + public AMQUndefinedDestination(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName) + { + super(exchange, UNKNOWN_EXCHANGE_CLASS, routingKey, queueName); + } + + public boolean isNameRequired() + { + return getAMQQueueName() == null; + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java b/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java index 8cb285c1ac..7cc548915c 100644 --- a/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java +++ b/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java @@ -1,66 +1,66 @@ -/* - * - * 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.client; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; - -import org.apache.qpid.framing.AMQShortString; - -public enum CustomJMSXProperty -{ - JMS_AMQP_NULL, - JMS_QPID_DESTTYPE, - JMSXGroupID, - JMSXGroupSeq, - JMSXUserID; - - - private final AMQShortString _nameAsShortString; - - CustomJMSXProperty() - { - _nameAsShortString = new AMQShortString(toString()); - } - - public AMQShortString getShortStringName() - { - return _nameAsShortString; - } - - private static Enumeration _names; - - public static synchronized Enumeration asEnumeration() - { - if(_names == null) - { - CustomJMSXProperty[] properties = values(); - ArrayList nameList = new ArrayList(properties.length); - for(CustomJMSXProperty property : properties) - { - nameList.add(property.toString()); - } - _names = Collections.enumeration(nameList); - } - return _names; - } -} +/* + * + * 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.client; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; + +import org.apache.qpid.framing.AMQShortString; + +public enum CustomJMSXProperty +{ + JMS_AMQP_NULL, + JMS_QPID_DESTTYPE, + JMSXGroupID, + JMSXGroupSeq, + JMSXUserID; + + + private final AMQShortString _nameAsShortString; + + CustomJMSXProperty() + { + _nameAsShortString = new AMQShortString(toString()); + } + + public AMQShortString getShortStringName() + { + return _nameAsShortString; + } + + private static Enumeration _names; + + public static synchronized Enumeration asEnumeration() + { + if(_names == null) + { + CustomJMSXProperty[] properties = values(); + ArrayList nameList = new ArrayList(properties.length); + for(CustomJMSXProperty property : properties) + { + nameList.add(property.toString()); + } + _names = Collections.enumeration(nameList); + } + return _names; + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java b/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java index 03d25aa243..7f8e80c73a 100644 --- a/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java +++ b/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java @@ -1,38 +1,38 @@ -/* - * 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.client; - -import javax.jms.Destination; -import javax.jms.JMSException; - -/** - * Provides support for covenience interface implemented by both AMQTemporaryTopic and AMQTemporaryQueue - * so that operations related to their "temporary-ness" can be abstracted out. - */ -interface TemporaryDestination extends Destination -{ - - public void delete() throws JMSException; - public AMQSession getSession(); - public boolean isDeleted(); - -} +/* + * 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.client; + +import javax.jms.Destination; +import javax.jms.JMSException; + +/** + * Provides support for covenience interface implemented by both AMQTemporaryTopic and AMQTemporaryQueue + * so that operations related to their "temporary-ness" can be abstracted out. + */ +interface TemporaryDestination extends Destination +{ + + public void delete() throws JMSException; + public AMQSession getSession(); + public boolean isDeleted(); + +} diff --git a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java index 1ec98efe0e..51cc94965a 100644 --- a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java +++ b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java @@ -1,75 +1,75 @@ -/* - * 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.client.failover; - -import org.apache.qpid.client.AMQConnection; - -/** - * FailoverNoopSupport is a {@link FailoverSupport} implementation that does not really provide any failover support - * at all. It wraps a {@link FailoverProtectedOperation} but should that operation throw {@link FailoverException} this - * support class simply re-raises that exception as an IllegalStateException. This support wrapper should only be - * used where the caller can be certain that the failover protected operation cannot acutally throw a failover exception, - * for example, because the caller already holds a lock preventing that condition from arising. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Perform a fail-over protected operation raising providing no handling of fail-over conditions. - *
- */ -public class FailoverNoopSupport implements FailoverSupport -{ - /** The protected operation that is to be retried in the event of fail-over. */ - FailoverProtectedOperation operation; - - /** The connection on which the fail-over protected operation is to be performed. */ - AMQConnection connection; - - /** - * Creates an automatic retrying fail-over handler for the specified operation. - * - * @param operation The fail-over protected operation to wrap in this handler. - */ - public FailoverNoopSupport(FailoverProtectedOperation operation, AMQConnection con) - { - this.operation = operation; - this.connection = con; - } - - /** - * Delegates to another continuation which is to be provided with fail-over handling. - * - * @return The return value from the delegated to continuation. - * @throws E Any exception that the delegated to continuation may raise. - */ - public T execute() throws E - { - try - { - return operation.execute(); - } - catch (FailoverException e) - { - throw new IllegalStateException("Fail-over interupted no-op failover support. " - + "No-op support should only be used where the caller is certain fail-over cannot occur.", e); - } - } -} +/* + * 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.client.failover; + +import org.apache.qpid.client.AMQConnection; + +/** + * FailoverNoopSupport is a {@link FailoverSupport} implementation that does not really provide any failover support + * at all. It wraps a {@link FailoverProtectedOperation} but should that operation throw {@link FailoverException} this + * support class simply re-raises that exception as an IllegalStateException. This support wrapper should only be + * used where the caller can be certain that the failover protected operation cannot acutally throw a failover exception, + * for example, because the caller already holds a lock preventing that condition from arising. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Perform a fail-over protected operation raising providing no handling of fail-over conditions. + *
+ */ +public class FailoverNoopSupport implements FailoverSupport +{ + /** The protected operation that is to be retried in the event of fail-over. */ + FailoverProtectedOperation operation; + + /** The connection on which the fail-over protected operation is to be performed. */ + AMQConnection connection; + + /** + * Creates an automatic retrying fail-over handler for the specified operation. + * + * @param operation The fail-over protected operation to wrap in this handler. + */ + public FailoverNoopSupport(FailoverProtectedOperation operation, AMQConnection con) + { + this.operation = operation; + this.connection = con; + } + + /** + * Delegates to another continuation which is to be provided with fail-over handling. + * + * @return The return value from the delegated to continuation. + * @throws E Any exception that the delegated to continuation may raise. + */ + public T execute() throws E + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + throw new IllegalStateException("Fail-over interupted no-op failover support. " + + "No-op support should only be used where the caller is certain fail-over cannot occur.", e); + } + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java index 9a7f43926e..e9c5f24791 100644 --- a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java +++ b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java @@ -1,49 +1,49 @@ -/* - * 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.client.failover; - -/** - * FailoverProtectedOperation is a continuation for an operation that may throw a {@link FailoverException} because - * it has been interrupted by the fail-over process. The {@link FailoverRetrySupport} class defines support wrappers - * for failover protected operations, in order to provide different handling schemes when failovers occurr. - * - *

The type of checked exception that the operation may perform has been generified, in order that fail over - * protected operations can be defined that raise arbitrary exceptions. The actuall exception types used should not - * be sub-classes of FailoverException, or else catching FailoverException in the {@link FailoverRetrySupport} classes - * will mask the exception. - * - *

- *
CRC Card
Responsibilities - *
Perform an operation that may be interrupted by fail-over. - *
- */ -public interface FailoverProtectedOperation -{ - /** - * Performs the continuations work. - * - * @return Provdes scope for the continuation to return an arbitrary value. - * - * @throws FailoverException If the operation is interrupted by a fail-over notification. - */ - public abstract T execute() throws E, FailoverException; -} +/* + * 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.client.failover; + +/** + * FailoverProtectedOperation is a continuation for an operation that may throw a {@link FailoverException} because + * it has been interrupted by the fail-over process. The {@link FailoverRetrySupport} class defines support wrappers + * for failover protected operations, in order to provide different handling schemes when failovers occurr. + * + *

The type of checked exception that the operation may perform has been generified, in order that fail over + * protected operations can be defined that raise arbitrary exceptions. The actuall exception types used should not + * be sub-classes of FailoverException, or else catching FailoverException in the {@link FailoverRetrySupport} classes + * will mask the exception. + * + *

+ *
CRC Card
Responsibilities + *
Perform an operation that may be interrupted by fail-over. + *
+ */ +public interface FailoverProtectedOperation +{ + /** + * Performs the continuations work. + * + * @return Provdes scope for the continuation to return an arbitrary value. + * + * @throws FailoverException If the operation is interrupted by a fail-over notification. + */ + public abstract T execute() throws E, FailoverException; +} diff --git a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java index e756d7baf9..cf7e978c03 100644 --- a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java +++ b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java @@ -1,135 +1,135 @@ -/* - * - * 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.client.failover; - -import org.apache.qpid.client.AMQConnection; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified - * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due - * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request, - * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This - * automatic retrying is continued until the continuation succeeds, or throws an exception (different to - * FailoverException, which is used to signal the failure of the original condition). - * - *

The blocking condition used is that the connection is not currently failing over, and the mutex used is the - * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods. - * These are used like a lock and condition variable. - * - *

The wrapped operation may throw a FailoverException, this is an exception that can be raised by a - * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a - * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are - * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous - * methods that should not be attempted when a fail-over is in progress. - * - *

Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be - * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want - * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation - * until it succeeds. Synchronous methods are usually coordinated with a - * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants - * to start and throws a FailoverException in response to this. - * - *

Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be - * started during fail-over, but be delayed until any current fail-over has completed. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Provide a continuation synchronized on a fail-over lock and condition. - *
Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception. - *
- * - * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see - * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary - * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation. - * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type - * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that. - * - * @todo InterruptedException not handled well. - */ -public class FailoverRetrySupport implements FailoverSupport -{ - /** Used for debugging. */ - private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class); - - /** The protected operation that is to be retried in the event of fail-over. */ - FailoverProtectedOperation operation; - - /** The connection on which the fail-over protected operation is to be performed. */ - AMQConnection connection; - - /** - * Creates an automatic retrying fail-over handler for the specified operation. - * - * @param operation The fail-over protected operation to wrap in this handler. - */ - public FailoverRetrySupport(FailoverProtectedOperation operation, AMQConnection con) - { - this.operation = operation; - this.connection = con; - } - - /** - * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats - * until the operation throws AMQException or succeeds without being interrupted by fail-over. - * - * @return The result of executing the continuation. - * - * @throws E Any underlying exception is allowed to fall through. - */ - public T execute() throws E - { - while (true) - { - try - { - connection.blockUntilNotFailingOver(); - } - catch (InterruptedException e) - { - _log.debug("Interrupted: " + e, e); - - return null; - } - - synchronized (connection.getFailoverMutex()) - { - try - { - return operation.execute(); - } - catch (FailoverException e) - { - _log.debug("Failover exception caught during operation: " + e, e); - } - catch (IllegalStateException e) - { - if (!(e.getMessage().startsWith("Fail-over interupted no-op failover support"))) - { - throw e; - } - } - } - } - } -} +/* + * + * 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.client.failover; + +import org.apache.qpid.client.AMQConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified + * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due + * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request, + * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This + * automatic retrying is continued until the continuation succeeds, or throws an exception (different to + * FailoverException, which is used to signal the failure of the original condition). + * + *

The blocking condition used is that the connection is not currently failing over, and the mutex used is the + * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods. + * These are used like a lock and condition variable. + * + *

The wrapped operation may throw a FailoverException, this is an exception that can be raised by a + * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a + * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are + * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous + * methods that should not be attempted when a fail-over is in progress. + * + *

Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be + * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want + * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation + * until it succeeds. Synchronous methods are usually coordinated with a + * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants + * to start and throws a FailoverException in response to this. + * + *

Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be + * started during fail-over, but be delayed until any current fail-over has completed. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Provide a continuation synchronized on a fail-over lock and condition. + *
Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception. + *
+ * + * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see + * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary + * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation. + * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type + * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that. + * + * @todo InterruptedException not handled well. + */ +public class FailoverRetrySupport implements FailoverSupport +{ + /** Used for debugging. */ + private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class); + + /** The protected operation that is to be retried in the event of fail-over. */ + FailoverProtectedOperation operation; + + /** The connection on which the fail-over protected operation is to be performed. */ + AMQConnection connection; + + /** + * Creates an automatic retrying fail-over handler for the specified operation. + * + * @param operation The fail-over protected operation to wrap in this handler. + */ + public FailoverRetrySupport(FailoverProtectedOperation operation, AMQConnection con) + { + this.operation = operation; + this.connection = con; + } + + /** + * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats + * until the operation throws AMQException or succeeds without being interrupted by fail-over. + * + * @return The result of executing the continuation. + * + * @throws E Any underlying exception is allowed to fall through. + */ + public T execute() throws E + { + while (true) + { + try + { + connection.blockUntilNotFailingOver(); + } + catch (InterruptedException e) + { + _log.debug("Interrupted: " + e, e); + + return null; + } + + synchronized (connection.getFailoverMutex()) + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + _log.debug("Failover exception caught during operation: " + e, e); + } + catch (IllegalStateException e) + { + if (!(e.getMessage().startsWith("Fail-over interupted no-op failover support"))) + { + throw e; + } + } + } + } + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java index a150d1446a..5bd36aa88b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java @@ -1,36 +1,36 @@ -package org.apache.qpid.client.handler; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.qpid.framing.*; -import org.apache.qpid.client.state.StateAwareMethodListener; -import org.apache.qpid.client.state.AMQStateManager; -import org.apache.qpid.client.protocol.AMQProtocolSession; -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQInvalidRoutingKeyException; -import org.apache.qpid.AMQChannelClosedException; -import org.apache.qpid.protocol.AMQConstant; - -public class AccessRequestOkMethodHandler implements StateAwareMethodListener -{ - private static final Logger _logger = LoggerFactory.getLogger(AccessRequestOkMethodHandler.class); - - private static AccessRequestOkMethodHandler _handler = new AccessRequestOkMethodHandler(); - - public static AccessRequestOkMethodHandler getInstance() - { - return _handler; - } - - public void methodReceived(AMQStateManager stateManager, AccessRequestOkBody method, int channelId) - throws AMQException - { - _logger.debug("AccessRequestOk method received"); - final AMQProtocolSession session = stateManager.getProtocolSession(); - session.setTicket(method.getTicket(), channelId); - - } -} +package org.apache.qpid.client.handler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.framing.*; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.AMQChannelClosedException; +import org.apache.qpid.protocol.AMQConstant; + +public class AccessRequestOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(AccessRequestOkMethodHandler.class); + + private static AccessRequestOkMethodHandler _handler = new AccessRequestOkMethodHandler(); + + public static AccessRequestOkMethodHandler getInstance() + { + return _handler; + } + + public void methodReceived(AMQStateManager stateManager, AccessRequestOkBody method, int channelId) + throws AMQException + { + _logger.debug("AccessRequestOk method received"); + final AMQProtocolSession session = stateManager.getProtocolSession(); + session.setTicket(method.getTicket(), channelId); + + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java index de976b05bd..afb7517a12 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java @@ -1,528 +1,528 @@ -/* - * - * 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.client.handler; - -import java.util.Map; -import java.util.HashMap; - -import org.apache.qpid.framing.*; -import org.apache.qpid.AMQException; -import org.apache.qpid.client.state.AMQStateManager; -import org.apache.qpid.client.state.AMQMethodNotImplementedException; - - -public class ClientMethodDispatcherImpl implements MethodDispatcher -{ - - - private static final BasicCancelOkMethodHandler _basicCancelOkMethodHandler = BasicCancelOkMethodHandler.getInstance(); - private static final BasicDeliverMethodHandler _basicDeliverMethodHandler = BasicDeliverMethodHandler.getInstance(); - private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance(); - private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance(); - private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance(); - private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); - private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance(); - private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance(); - private static final ConnectionSecureMethodHandler _connectionSecureMethodHandler = ConnectionSecureMethodHandler.getInstance(); - private static final ConnectionStartMethodHandler _connectionStartMethodHandler = ConnectionStartMethodHandler.getInstance(); - private static final ConnectionTuneMethodHandler _connectionTuneMethodHandler = ConnectionTuneMethodHandler.getInstance(); - private static final ExchangeBoundOkMethodHandler _exchangeBoundOkMethodHandler = ExchangeBoundOkMethodHandler.getInstance(); - private static final QueueDeleteOkMethodHandler _queueDeleteOkMethodHandler = QueueDeleteOkMethodHandler.getInstance(); - - - - private static interface DispatcherFactory - { - public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager); - } - - private static final Map _dispatcherFactories = - new HashMap(); - - static - { - _dispatcherFactories.put(ProtocolVersion.v8_0, - new DispatcherFactory() - { - public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager) - { - return new ClientMethodDispatcherImpl_8_0(stateManager); - } - }); - - _dispatcherFactories.put(ProtocolVersion.v0_9, - new DispatcherFactory() - { - public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager) - { - return new ClientMethodDispatcherImpl_0_9(stateManager); - } - }); - - } - - - public static ClientMethodDispatcherImpl newMethodDispatcher(ProtocolVersion version, AMQStateManager stateManager) - { - DispatcherFactory factory = _dispatcherFactories.get(version); - return factory.createMethodDispatcher(stateManager); - } - - - - - private AMQStateManager _stateManager; - - public ClientMethodDispatcherImpl(AMQStateManager stateManager) - { - _stateManager = stateManager; - } - - - public AMQStateManager getStateManager() - { - return _stateManager; - } - - public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException - { - _basicCancelOkMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException - { - _basicDeliverMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException - { - _basicReturnMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException - { - _channelCloseMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException - { - _channelFlowOkMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException - { - _connectionCloseMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException - { - _connectionOpenOkMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException - { - _connectionRedirectMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException - { - _connectionSecureMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException - { - _connectionStartMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException - { - _connectionTuneMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException - { - _queueDeleteOkMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException - { - _exchangeBoundOkMethodHandler.methodReceived(_stateManager,body,channelId); - return true; - } - - public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException - { - return false; - } - -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import java.util.Map; +import java.util.HashMap; + +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQMethodNotImplementedException; + + +public class ClientMethodDispatcherImpl implements MethodDispatcher +{ + + + private static final BasicCancelOkMethodHandler _basicCancelOkMethodHandler = BasicCancelOkMethodHandler.getInstance(); + private static final BasicDeliverMethodHandler _basicDeliverMethodHandler = BasicDeliverMethodHandler.getInstance(); + private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance(); + private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance(); + private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance(); + private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); + private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance(); + private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance(); + private static final ConnectionSecureMethodHandler _connectionSecureMethodHandler = ConnectionSecureMethodHandler.getInstance(); + private static final ConnectionStartMethodHandler _connectionStartMethodHandler = ConnectionStartMethodHandler.getInstance(); + private static final ConnectionTuneMethodHandler _connectionTuneMethodHandler = ConnectionTuneMethodHandler.getInstance(); + private static final ExchangeBoundOkMethodHandler _exchangeBoundOkMethodHandler = ExchangeBoundOkMethodHandler.getInstance(); + private static final QueueDeleteOkMethodHandler _queueDeleteOkMethodHandler = QueueDeleteOkMethodHandler.getInstance(); + + + + private static interface DispatcherFactory + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager); + } + + private static final Map _dispatcherFactories = + new HashMap(); + + static + { + _dispatcherFactories.put(ProtocolVersion.v8_0, + new DispatcherFactory() + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager) + { + return new ClientMethodDispatcherImpl_8_0(stateManager); + } + }); + + _dispatcherFactories.put(ProtocolVersion.v0_9, + new DispatcherFactory() + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQStateManager stateManager) + { + return new ClientMethodDispatcherImpl_0_9(stateManager); + } + }); + + } + + + public static ClientMethodDispatcherImpl newMethodDispatcher(ProtocolVersion version, AMQStateManager stateManager) + { + DispatcherFactory factory = _dispatcherFactories.get(version); + return factory.createMethodDispatcher(stateManager); + } + + + + + private AMQStateManager _stateManager; + + public ClientMethodDispatcherImpl(AMQStateManager stateManager) + { + _stateManager = stateManager; + } + + + public AMQStateManager getStateManager() + { + return _stateManager; + } + + public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException + { + _basicCancelOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException + { + _basicDeliverMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException + { + _basicReturnMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException + { + _channelCloseMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException + { + _channelFlowOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException + { + _connectionCloseMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException + { + _connectionOpenOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException + { + _connectionRedirectMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException + { + _connectionSecureMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException + { + _connectionStartMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException + { + _connectionTuneMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException + { + _queueDeleteOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException + { + _exchangeBoundOkMethodHandler.methodReceived(_stateManager,body,channelId); + return true; + } + + public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException + { + return false; + } + +} diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java index ae6d5e8283..e235368357 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java @@ -1,155 +1,155 @@ -/* - * - * 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.client.handler; - -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; - -import org.apache.qpid.AMQException; -import org.apache.qpid.client.state.AMQStateManager; -import org.apache.qpid.client.state.AMQMethodNotImplementedException; - - -public class ClientMethodDispatcherImpl_0_9 extends ClientMethodDispatcherImpl implements MethodDispatcher_0_9 -{ - public ClientMethodDispatcherImpl_0_9(AMQStateManager stateManager) - { - super(stateManager); - } - - - public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException - { - throw new AMQMethodNotImplementedException(body); - } - - public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException - { - return false; - } - - -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQMethodNotImplementedException; + + +public class ClientMethodDispatcherImpl_0_9 extends ClientMethodDispatcherImpl implements MethodDispatcher_0_9 +{ + public ClientMethodDispatcherImpl_0_9(AMQStateManager stateManager) + { + super(stateManager); + } + + + public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException + { + return false; + } + + +} diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java index 6bd6874cde..b0f003cd2d 100644 --- a/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java +++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java @@ -1,85 +1,85 @@ -/* - * - * 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.client.handler; - -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; - -import org.apache.qpid.AMQException; -import org.apache.qpid.client.state.AMQStateManager; - -public class ClientMethodDispatcherImpl_8_0 extends ClientMethodDispatcherImpl implements MethodDispatcher_8_0 -{ - public ClientMethodDispatcherImpl_8_0(AMQStateManager stateManager) - { - super(stateManager); - } - - public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException - { - return false; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + package org.apache.qpid.client.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; + +public class ClientMethodDispatcherImpl_8_0 extends ClientMethodDispatcherImpl implements MethodDispatcher_8_0 +{ + public ClientMethodDispatcherImpl_8_0(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException + { + return false; + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java index 5904131122..9bdde01bf3 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java @@ -1,802 +1,802 @@ -/* - * 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.client.message; - -import java.nio.charset.CharacterCodingException; -import java.nio.charset.Charset; - -import javax.jms.JMSException; -import javax.jms.MessageEOFException; -import javax.jms.MessageFormatException; -import javax.jms.MessageNotReadableException; -import javax.jms.MessageNotWriteableException; - -import org.apache.mina.common.ByteBuffer; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.BasicContentHeaderProperties; - -/** - * @author Apache Software Foundation - */ -public abstract class AbstractBytesTypedMessage extends AbstractBytesMessage -{ - - protected static final byte BOOLEAN_TYPE = (byte) 1; - - protected static final byte BYTE_TYPE = (byte) 2; - - protected static final byte BYTEARRAY_TYPE = (byte) 3; - - protected static final byte SHORT_TYPE = (byte) 4; - - protected static final byte CHAR_TYPE = (byte) 5; - - protected static final byte INT_TYPE = (byte) 6; - - protected static final byte LONG_TYPE = (byte) 7; - - protected static final byte FLOAT_TYPE = (byte) 8; - - protected static final byte DOUBLE_TYPE = (byte) 9; - - protected static final byte STRING_TYPE = (byte) 10; - - protected static final byte NULL_STRING_TYPE = (byte) 11; - - /** - * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read - * a byte array in multiple chunks, hence this is used to track how much is left to be read - */ - private int _byteArrayRemaining = -1; - - AbstractBytesTypedMessage() - { - this(null); - } - - /** - * Construct a stream message with existing data. - * - * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is - * set to auto expand - */ - AbstractBytesTypedMessage(ByteBuffer data) - { - super(data); // this instanties a content header - } - - - AbstractBytesTypedMessage(long messageNbr, BasicContentHeaderProperties contentHeader, AMQShortString exchange, - AMQShortString routingKey, ByteBuffer data) throws AMQException - { - super(messageNbr, contentHeader, exchange, routingKey, data); - } - - - protected byte readWireType() throws MessageFormatException, MessageEOFException, - MessageNotReadableException - { - checkReadable(); - checkAvailable(1); - return _data.get(); - } - - protected void writeTypeDiscriminator(byte type) throws MessageNotWriteableException - { - checkWritable(); - _data.put(type); - _changedData = true; - } - - protected boolean readBoolean() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - boolean result; - try - { - switch (wireType) - { - case BOOLEAN_TYPE: - checkAvailable(1); - result = readBooleanImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Boolean.parseBoolean(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a boolean"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private boolean readBooleanImpl() - { - return _data.get() != 0; - } - - protected byte readByte() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - byte result; - try - { - switch (wireType) - { - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Byte.parseByte(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a byte"); - } - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - return result; - } - - private byte readByteImpl() - { - return _data.get(); - } - - protected short readShort() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - short result; - try - { - switch (wireType) - { - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Short.parseShort(readStringImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a short"); - } - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - return result; - } - - private short readShortImpl() - { - return _data.getShort(); - } - - /** - * Note that this method reads a unicode character as two bytes from the stream - * - * @return the character read from the stream - * @throws javax.jms.JMSException - */ - protected char readChar() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - try - { - if(wireType == NULL_STRING_TYPE){ - throw new NullPointerException(); - } - - if (wireType != CHAR_TYPE) - { - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a char"); - } - else - { - checkAvailable(2); - return readCharImpl(); - } - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private char readCharImpl() - { - return _data.getChar(); - } - - protected int readInt() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - int result; - try - { - switch (wireType) - { - case INT_TYPE: - checkAvailable(4); - result = readIntImpl(); - break; - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Integer.parseInt(readStringImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to an int"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - protected int readIntImpl() - { - return _data.getInt(); - } - - protected long readLong() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - long result; - try - { - switch (wireType) - { - case LONG_TYPE: - checkAvailable(8); - result = readLongImpl(); - break; - case INT_TYPE: - checkAvailable(4); - result = readIntImpl(); - break; - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Long.parseLong(readStringImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a long"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private long readLongImpl() - { - return _data.getLong(); - } - - protected float readFloat() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - float result; - try - { - switch (wireType) - { - case FLOAT_TYPE: - checkAvailable(4); - result = readFloatImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Float.parseFloat(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a float"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private float readFloatImpl() - { - return _data.getFloat(); - } - - protected double readDouble() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - double result; - try - { - switch (wireType) - { - case DOUBLE_TYPE: - checkAvailable(8); - result = readDoubleImpl(); - break; - case FLOAT_TYPE: - checkAvailable(4); - result = readFloatImpl(); - break; - case STRING_TYPE: - checkAvailable(1); - result = Double.parseDouble(readStringImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a double"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - private double readDoubleImpl() - { - return _data.getDouble(); - } - - protected String readString() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - String result; - try - { - switch (wireType) - { - case STRING_TYPE: - checkAvailable(1); - result = readStringImpl(); - break; - case NULL_STRING_TYPE: - result = null; - throw new NullPointerException("data is null"); - case BOOLEAN_TYPE: - checkAvailable(1); - result = String.valueOf(readBooleanImpl()); - break; - case LONG_TYPE: - checkAvailable(8); - result = String.valueOf(readLongImpl()); - break; - case INT_TYPE: - checkAvailable(4); - result = String.valueOf(readIntImpl()); - break; - case SHORT_TYPE: - checkAvailable(2); - result = String.valueOf(readShortImpl()); - break; - case BYTE_TYPE: - checkAvailable(1); - result = String.valueOf(readByteImpl()); - break; - case FLOAT_TYPE: - checkAvailable(4); - result = String.valueOf(readFloatImpl()); - break; - case DOUBLE_TYPE: - checkAvailable(8); - result = String.valueOf(readDoubleImpl()); - break; - case CHAR_TYPE: - checkAvailable(2); - result = String.valueOf(readCharImpl()); - break; - default: - _data.position(position); - throw new MessageFormatException("Unable to convert " + wireType + " to a String"); - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - protected String readStringImpl() throws JMSException - { - try - { - return _data.getString(Charset.forName("UTF-8").newDecoder()); - } - catch (CharacterCodingException e) - { - JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e); - je.setLinkedException(e); - throw je; - } - } - - protected int readBytes(byte[] bytes) throws JMSException - { - if (bytes == null) - { - throw new IllegalArgumentException("byte array must not be null"); - } - checkReadable(); - // first call - if (_byteArrayRemaining == -1) - { - // type discriminator checked separately so you get a MessageFormatException rather than - // an EOF even in the case where both would be applicable - checkAvailable(1); - byte wireType = readWireType(); - if (wireType != BYTEARRAY_TYPE) - { - throw new MessageFormatException("Unable to convert " + wireType + " to a byte array"); - } - checkAvailable(4); - int size = _data.getInt(); - // length of -1 indicates null - if (size == -1) - { - return -1; - } - else - { - if (size > _data.remaining()) - { - throw new MessageEOFException("Byte array has stated length " + size + " but message only contains " + - _data.remaining() + " bytes"); - } - else - { - _byteArrayRemaining = size; - } - } - } - else if (_byteArrayRemaining == 0) - { - _byteArrayRemaining = -1; - return -1; - } - - int returnedSize = readBytesImpl(bytes); - if (returnedSize < bytes.length) - { - _byteArrayRemaining = -1; - } - return returnedSize; - } - - private int readBytesImpl(byte[] bytes) - { - int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining); - _byteArrayRemaining -= count; - - if (count == 0) - { - return 0; - } - else - { - _data.get(bytes, 0, count); - return count; - } - } - - protected Object readObject() throws JMSException - { - int position = _data.position(); - byte wireType = readWireType(); - Object result = null; - try - { - switch (wireType) - { - case BOOLEAN_TYPE: - checkAvailable(1); - result = readBooleanImpl(); - break; - case BYTE_TYPE: - checkAvailable(1); - result = readByteImpl(); - break; - case BYTEARRAY_TYPE: - checkAvailable(4); - int size = _data.getInt(); - if (size == -1) - { - result = null; - } - else - { - _byteArrayRemaining = size; - byte[] bytesResult = new byte[size]; - readBytesImpl(bytesResult); - result = bytesResult; - } - break; - case SHORT_TYPE: - checkAvailable(2); - result = readShortImpl(); - break; - case CHAR_TYPE: - checkAvailable(2); - result = readCharImpl(); - break; - case INT_TYPE: - checkAvailable(4); - result = readIntImpl(); - break; - case LONG_TYPE: - checkAvailable(8); - result = readLongImpl(); - break; - case FLOAT_TYPE: - checkAvailable(4); - result = readFloatImpl(); - break; - case DOUBLE_TYPE: - checkAvailable(8); - result = readDoubleImpl(); - break; - case NULL_STRING_TYPE: - result = null; - break; - case STRING_TYPE: - checkAvailable(1); - result = readStringImpl(); - break; - } - return result; - } - catch (RuntimeException e) - { - _data.position(position); - throw e; - } - } - - protected void writeBoolean(boolean b) throws JMSException - { - writeTypeDiscriminator(BOOLEAN_TYPE); - _data.put(b ? (byte) 1 : (byte) 0); - } - - protected void writeByte(byte b) throws JMSException - { - writeTypeDiscriminator(BYTE_TYPE); - _data.put(b); - } - - protected void writeShort(short i) throws JMSException - { - writeTypeDiscriminator(SHORT_TYPE); - _data.putShort(i); - } - - protected void writeChar(char c) throws JMSException - { - writeTypeDiscriminator(CHAR_TYPE); - _data.putChar(c); - } - - protected void writeInt(int i) throws JMSException - { - writeTypeDiscriminator(INT_TYPE); - writeIntImpl(i); - } - - protected void writeIntImpl(int i) - { - _data.putInt(i); - } - - protected void writeLong(long l) throws JMSException - { - writeTypeDiscriminator(LONG_TYPE); - _data.putLong(l); - } - - protected void writeFloat(float v) throws JMSException - { - writeTypeDiscriminator(FLOAT_TYPE); - _data.putFloat(v); - } - - protected void writeDouble(double v) throws JMSException - { - writeTypeDiscriminator(DOUBLE_TYPE); - _data.putDouble(v); - } - - protected void writeString(String string) throws JMSException - { - if (string == null) - { - writeTypeDiscriminator(NULL_STRING_TYPE); - } - else - { - writeTypeDiscriminator(STRING_TYPE); - try - { - writeStringImpl(string); - } - catch (CharacterCodingException e) - { - JMSException ex = new JMSException("Unable to encode string: " + e); - ex.setLinkedException(e); - throw ex; - } - } - } - - protected void writeStringImpl(String string) - throws CharacterCodingException - { - _data.putString(string, Charset.forName("UTF-8").newEncoder()); - // we must write the null terminator ourselves - _data.put((byte) 0); - } - - protected void writeBytes(byte[] bytes) throws JMSException - { - writeBytes(bytes, 0, bytes == null ? 0 : bytes.length); - } - - protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException - { - writeTypeDiscriminator(BYTEARRAY_TYPE); - if (bytes == null) - { - _data.putInt(-1); - } - else - { - _data.putInt(length); - _data.put(bytes, offset, length); - } - } - - protected void writeObject(Object object) throws JMSException - { - checkWritable(); - Class clazz; - - if (object == null) - { - // string handles the output of null values - clazz = String.class; - } - else - { - clazz = object.getClass(); - } - - if (clazz == Byte.class) - { - writeByte((Byte) object); - } - else if (clazz == Boolean.class) - { - writeBoolean((Boolean) object); - } - else if (clazz == byte[].class) - { - writeBytes((byte[]) object); - } - else if (clazz == Short.class) - { - writeShort((Short) object); - } - else if (clazz == Character.class) - { - writeChar((Character) object); - } - else if (clazz == Integer.class) - { - writeInt((Integer) object); - } - else if (clazz == Long.class) - { - writeLong((Long) object); - } - else if (clazz == Float.class) - { - writeFloat((Float) object); - } - else if (clazz == Double.class) - { - writeDouble((Double) object); - } - else if (clazz == String.class) - { - writeString((String) object); - } - else - { - throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); - } - } -} +/* + * 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.client.message; + +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +import javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +/** + * @author Apache Software Foundation + */ +public abstract class AbstractBytesTypedMessage extends AbstractBytesMessage +{ + + protected static final byte BOOLEAN_TYPE = (byte) 1; + + protected static final byte BYTE_TYPE = (byte) 2; + + protected static final byte BYTEARRAY_TYPE = (byte) 3; + + protected static final byte SHORT_TYPE = (byte) 4; + + protected static final byte CHAR_TYPE = (byte) 5; + + protected static final byte INT_TYPE = (byte) 6; + + protected static final byte LONG_TYPE = (byte) 7; + + protected static final byte FLOAT_TYPE = (byte) 8; + + protected static final byte DOUBLE_TYPE = (byte) 9; + + protected static final byte STRING_TYPE = (byte) 10; + + protected static final byte NULL_STRING_TYPE = (byte) 11; + + /** + * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read + * a byte array in multiple chunks, hence this is used to track how much is left to be read + */ + private int _byteArrayRemaining = -1; + + AbstractBytesTypedMessage() + { + this(null); + } + + /** + * Construct a stream message with existing data. + * + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + * set to auto expand + */ + AbstractBytesTypedMessage(ByteBuffer data) + { + super(data); // this instanties a content header + } + + + AbstractBytesTypedMessage(long messageNbr, BasicContentHeaderProperties contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) throws AMQException + { + super(messageNbr, contentHeader, exchange, routingKey, data); + } + + + protected byte readWireType() throws MessageFormatException, MessageEOFException, + MessageNotReadableException + { + checkReadable(); + checkAvailable(1); + return _data.get(); + } + + protected void writeTypeDiscriminator(byte type) throws MessageNotWriteableException + { + checkWritable(); + _data.put(type); + _changedData = true; + } + + protected boolean readBoolean() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + boolean result; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Boolean.parseBoolean(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a boolean"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private boolean readBooleanImpl() + { + return _data.get() != 0; + } + + protected byte readByte() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + byte result; + try + { + switch (wireType) + { + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Byte.parseByte(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a byte"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + private byte readByteImpl() + { + return _data.get(); + } + + protected short readShort() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + short result; + try + { + switch (wireType) + { + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Short.parseShort(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a short"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + private short readShortImpl() + { + return _data.getShort(); + } + + /** + * Note that this method reads a unicode character as two bytes from the stream + * + * @return the character read from the stream + * @throws javax.jms.JMSException + */ + protected char readChar() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + try + { + if(wireType == NULL_STRING_TYPE){ + throw new NullPointerException(); + } + + if (wireType != CHAR_TYPE) + { + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a char"); + } + else + { + checkAvailable(2); + return readCharImpl(); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private char readCharImpl() + { + return _data.getChar(); + } + + protected int readInt() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + int result; + try + { + switch (wireType) + { + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Integer.parseInt(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to an int"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected int readIntImpl() + { + return _data.getInt(); + } + + protected long readLong() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + long result; + try + { + switch (wireType) + { + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Long.parseLong(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a long"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private long readLongImpl() + { + return _data.getLong(); + } + + protected float readFloat() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + float result; + try + { + switch (wireType) + { + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Float.parseFloat(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a float"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private float readFloatImpl() + { + return _data.getFloat(); + } + + protected double readDouble() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + double result; + try + { + switch (wireType) + { + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Double.parseDouble(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a double"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private double readDoubleImpl() + { + return _data.getDouble(); + } + + protected String readString() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + String result; + try + { + switch (wireType) + { + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + case NULL_STRING_TYPE: + result = null; + throw new NullPointerException("data is null"); + case BOOLEAN_TYPE: + checkAvailable(1); + result = String.valueOf(readBooleanImpl()); + break; + case LONG_TYPE: + checkAvailable(8); + result = String.valueOf(readLongImpl()); + break; + case INT_TYPE: + checkAvailable(4); + result = String.valueOf(readIntImpl()); + break; + case SHORT_TYPE: + checkAvailable(2); + result = String.valueOf(readShortImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = String.valueOf(readByteImpl()); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = String.valueOf(readFloatImpl()); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = String.valueOf(readDoubleImpl()); + break; + case CHAR_TYPE: + checkAvailable(2); + result = String.valueOf(readCharImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a String"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected String readStringImpl() throws JMSException + { + try + { + return _data.getString(Charset.forName("UTF-8").newDecoder()); + } + catch (CharacterCodingException e) + { + JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e); + je.setLinkedException(e); + throw je; + } + } + + protected int readBytes(byte[] bytes) throws JMSException + { + if (bytes == null) + { + throw new IllegalArgumentException("byte array must not be null"); + } + checkReadable(); + // first call + if (_byteArrayRemaining == -1) + { + // type discriminator checked separately so you get a MessageFormatException rather than + // an EOF even in the case where both would be applicable + checkAvailable(1); + byte wireType = readWireType(); + if (wireType != BYTEARRAY_TYPE) + { + throw new MessageFormatException("Unable to convert " + wireType + " to a byte array"); + } + checkAvailable(4); + int size = _data.getInt(); + // length of -1 indicates null + if (size == -1) + { + return -1; + } + else + { + if (size > _data.remaining()) + { + throw new MessageEOFException("Byte array has stated length " + size + " but message only contains " + + _data.remaining() + " bytes"); + } + else + { + _byteArrayRemaining = size; + } + } + } + else if (_byteArrayRemaining == 0) + { + _byteArrayRemaining = -1; + return -1; + } + + int returnedSize = readBytesImpl(bytes); + if (returnedSize < bytes.length) + { + _byteArrayRemaining = -1; + } + return returnedSize; + } + + private int readBytesImpl(byte[] bytes) + { + int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining); + _byteArrayRemaining -= count; + + if (count == 0) + { + return 0; + } + else + { + _data.get(bytes, 0, count); + return count; + } + } + + protected Object readObject() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + Object result = null; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case BYTEARRAY_TYPE: + checkAvailable(4); + int size = _data.getInt(); + if (size == -1) + { + result = null; + } + else + { + _byteArrayRemaining = size; + byte[] bytesResult = new byte[size]; + readBytesImpl(bytesResult); + result = bytesResult; + } + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case CHAR_TYPE: + checkAvailable(2); + result = readCharImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case NULL_STRING_TYPE: + result = null; + break; + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected void writeBoolean(boolean b) throws JMSException + { + writeTypeDiscriminator(BOOLEAN_TYPE); + _data.put(b ? (byte) 1 : (byte) 0); + } + + protected void writeByte(byte b) throws JMSException + { + writeTypeDiscriminator(BYTE_TYPE); + _data.put(b); + } + + protected void writeShort(short i) throws JMSException + { + writeTypeDiscriminator(SHORT_TYPE); + _data.putShort(i); + } + + protected void writeChar(char c) throws JMSException + { + writeTypeDiscriminator(CHAR_TYPE); + _data.putChar(c); + } + + protected void writeInt(int i) throws JMSException + { + writeTypeDiscriminator(INT_TYPE); + writeIntImpl(i); + } + + protected void writeIntImpl(int i) + { + _data.putInt(i); + } + + protected void writeLong(long l) throws JMSException + { + writeTypeDiscriminator(LONG_TYPE); + _data.putLong(l); + } + + protected void writeFloat(float v) throws JMSException + { + writeTypeDiscriminator(FLOAT_TYPE); + _data.putFloat(v); + } + + protected void writeDouble(double v) throws JMSException + { + writeTypeDiscriminator(DOUBLE_TYPE); + _data.putDouble(v); + } + + protected void writeString(String string) throws JMSException + { + if (string == null) + { + writeTypeDiscriminator(NULL_STRING_TYPE); + } + else + { + writeTypeDiscriminator(STRING_TYPE); + try + { + writeStringImpl(string); + } + catch (CharacterCodingException e) + { + JMSException ex = new JMSException("Unable to encode string: " + e); + ex.setLinkedException(e); + throw ex; + } + } + } + + protected void writeStringImpl(String string) + throws CharacterCodingException + { + _data.putString(string, Charset.forName("UTF-8").newEncoder()); + // we must write the null terminator ourselves + _data.put((byte) 0); + } + + protected void writeBytes(byte[] bytes) throws JMSException + { + writeBytes(bytes, 0, bytes == null ? 0 : bytes.length); + } + + protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException + { + writeTypeDiscriminator(BYTEARRAY_TYPE); + if (bytes == null) + { + _data.putInt(-1); + } + else + { + _data.putInt(length); + _data.put(bytes, offset, length); + } + } + + protected void writeObject(Object object) throws JMSException + { + checkWritable(); + Class clazz; + + if (object == null) + { + // string handles the output of null values + clazz = String.class; + } + else + { + clazz = object.getClass(); + } + + if (clazz == Byte.class) + { + writeByte((Byte) object); + } + else if (clazz == Boolean.class) + { + writeBoolean((Boolean) object); + } + else if (clazz == byte[].class) + { + writeBytes((byte[]) object); + } + else if (clazz == Short.class) + { + writeShort((Short) object); + } + else if (clazz == Character.class) + { + writeChar((Character) object); + } + else if (clazz == Integer.class) + { + writeInt((Integer) object); + } + else if (clazz == Long.class) + { + writeLong((Long) object); + } + else if (clazz == Float.class) + { + writeFloat((Float) object); + } + else if (clazz == Double.class) + { + writeDouble((Double) object); + } + else if (clazz == String.class) + { + writeString((String) object); + } + else + { + throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); + } + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java index 39b4e1e27b..fec0117a03 100644 --- a/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java +++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java @@ -1,552 +1,552 @@ -/* - * - * 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.client.message; - -import java.util.Enumeration; - -import javax.jms.JMSException; -import javax.jms.MessageFormatException; - -import org.apache.mina.common.ByteBuffer; -import org.apache.qpid.AMQPInvalidClassException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; - - -public final class JMSHeaderAdapter -{ - private final FieldTable _headers; - - public JMSHeaderAdapter(FieldTable headers) - { - _headers = headers; - } - - - public FieldTable getHeaders() - { - return _headers; - } - - public boolean getBoolean(String string) throws JMSException - { - checkPropertyName(string); - Boolean b = getHeaders().getBoolean(string); - - if (b == null) - { - if (getHeaders().containsKey(string)) - { - Object str = getHeaders().getObject(string); - - if (str == null || !(str instanceof String)) - { - throw new MessageFormatException("getBoolean can't use " + string + " item."); - } - else - { - return Boolean.valueOf((String) str); - } - } - else - { - b = Boolean.valueOf(null); - } - } - - return b; - } - - public boolean getBoolean(AMQShortString string) throws JMSException - { - checkPropertyName(string); - Boolean b = getHeaders().getBoolean(string); - - if (b == null) - { - if (getHeaders().containsKey(string)) - { - Object str = getHeaders().getObject(string); - - if (str == null || !(str instanceof String)) - { - throw new MessageFormatException("getBoolean can't use " + string + " item."); - } - else - { - return Boolean.valueOf((String) str); - } - } - else - { - b = Boolean.valueOf(null); - } - } - - return b; - } - - public char getCharacter(String string) throws JMSException - { - checkPropertyName(string); - Character c = getHeaders().getCharacter(string); - - if (c == null) - { - if (getHeaders().isNullStringValue(string)) - { - throw new NullPointerException("Cannot convert null char"); - } - else - { - throw new MessageFormatException("getChar can't use " + string + " item."); - } - } - else - { - return (char) c; - } - } - - public byte[] getBytes(String string) throws JMSException - { - return getBytes(new AMQShortString(string)); - } - - public byte[] getBytes(AMQShortString string) throws JMSException - { - checkPropertyName(string); - - byte[] bs = getHeaders().getBytes(string); - - if (bs == null) - { - throw new MessageFormatException("getBytes can't use " + string + " item."); - } - else - { - return bs; - } - } - - public byte getByte(String string) throws JMSException - { - checkPropertyName(string); - Byte b = getHeaders().getByte(string); - if (b == null) - { - if (getHeaders().containsKey(string)) - { - Object str = getHeaders().getObject(string); - - if (str == null || !(str instanceof String)) - { - throw new MessageFormatException("getByte can't use " + string + " item."); - } - else - { - return Byte.valueOf((String) str); - } - } - else - { - b = Byte.valueOf(null); - } - } - - return b; - } - - public short getShort(String string) throws JMSException - { - checkPropertyName(string); - Short s = getHeaders().getShort(string); - - if (s == null) - { - s = Short.valueOf(getByte(string)); - } - - return s; - } - - public int getInteger(String string) throws JMSException - { - checkPropertyName(string); - Integer i = getHeaders().getInteger(string); - - if (i == null) - { - i = Integer.valueOf(getShort(string)); - } - - return i; - } - - public long getLong(String string) throws JMSException - { - checkPropertyName(string); - Long l = getHeaders().getLong(string); - - if (l == null) - { - l = Long.valueOf(getInteger(string)); - } - - return l; - } - - public float getFloat(String string) throws JMSException - { - checkPropertyName(string); - Float f = getHeaders().getFloat(string); - - if (f == null) - { - if (getHeaders().containsKey(string)) - { - Object str = getHeaders().getObject(string); - - if (str == null || !(str instanceof String)) - { - throw new MessageFormatException("getFloat can't use " + string + " item."); - } - else - { - return Float.valueOf((String) str); - } - } - else - { - f = Float.valueOf(null); - } - - } - - return f; - } - - public double getDouble(String string) throws JMSException - { - checkPropertyName(string); - Double d = getHeaders().getDouble(string); - - if (d == null) - { - d = Double.valueOf(getFloat(string)); - } - - return d; - } - - public String getString(String string) throws JMSException - { - checkPropertyName(string); - String s = getHeaders().getString(string); - - if (s == null) - { - if (getHeaders().containsKey(string)) - { - Object o = getHeaders().getObject(string); - if (o instanceof byte[]) - { - throw new MessageFormatException("getObject couldn't find " + string + " item."); - } - else - { - if (o == null) - { - return null; - } - else - { - s = String.valueOf(o); - } - } - }//else return s // null; - } - - return s; - } - - public Object getObject(String string) throws JMSException - { - checkPropertyName(string); - return getHeaders().getObject(string); - } - - public void setBoolean(AMQShortString string, boolean b) throws JMSException - { - checkPropertyName(string); - getHeaders().setBoolean(string, b); - } - - public void setBoolean(String string, boolean b) throws JMSException - { - checkPropertyName(string); - getHeaders().setBoolean(string, b); - } - - public void setChar(String string, char c) throws JMSException - { - checkPropertyName(string); - getHeaders().setChar(string, c); - } - - public Object setBytes(AMQShortString string, byte[] bytes) - { - checkPropertyName(string); - return getHeaders().setBytes(string, bytes); - } - - public Object setBytes(String string, byte[] bytes) - { - checkPropertyName(string); - return getHeaders().setBytes(string, bytes); - } - - public Object setBytes(String string, byte[] bytes, int start, int length) - { - checkPropertyName(string); - return getHeaders().setBytes(string, bytes, start, length); - } - - public void setByte(String string, byte b) throws JMSException - { - checkPropertyName(string); - getHeaders().setByte(string, b); - } - - public void setByte(AMQShortString string, byte b) throws JMSException - { - checkPropertyName(string); - getHeaders().setByte(string, b); - } - - - public void setShort(String string, short i) throws JMSException - { - checkPropertyName(string); - getHeaders().setShort(string, i); - } - - public void setInteger(String string, int i) throws JMSException - { - checkPropertyName(string); - getHeaders().setInteger(string, i); - } - - public void setInteger(AMQShortString string, int i) throws JMSException - { - checkPropertyName(string); - getHeaders().setInteger(string, i); - } - - public void setLong(String string, long l) throws JMSException - { - checkPropertyName(string); - getHeaders().setLong(string, l); - } - - public void setFloat(String string, float v) throws JMSException - { - checkPropertyName(string); - getHeaders().setFloat(string, v); - } - - public void setDouble(String string, double v) throws JMSException - { - checkPropertyName(string); - getHeaders().setDouble(string, v); - } - - public void setString(String string, String string1) throws JMSException - { - checkPropertyName(string); - getHeaders().setString(string, string1); - } - - public void setString(AMQShortString string, String string1) throws JMSException - { - checkPropertyName(string); - getHeaders().setString(string, string1); - } - - public void setObject(String string, Object object) throws JMSException - { - checkPropertyName(string); - try - { - getHeaders().setObject(string, object); - } - catch (AMQPInvalidClassException aice) - { - MessageFormatException mfe = new MessageFormatException("Only primatives are allowed object is:" + object.getClass()); - mfe.setLinkedException(aice); - throw mfe; - } - } - - public boolean itemExists(String string) throws JMSException - { - checkPropertyName(string); - return getHeaders().containsKey(string); - } - - public Enumeration getPropertyNames() - { - return getHeaders().getPropertyNames(); - } - - public void clear() - { - getHeaders().clear(); - } - - public boolean propertyExists(AMQShortString propertyName) - { - checkPropertyName(propertyName); - return getHeaders().propertyExists(propertyName); - } - - public boolean propertyExists(String propertyName) - { - checkPropertyName(propertyName); - return getHeaders().propertyExists(propertyName); - } - - public Object put(Object key, Object value) - { - checkPropertyName(key.toString()); - return getHeaders().setObject(key.toString(), value); - } - - public Object remove(AMQShortString propertyName) - { - checkPropertyName(propertyName); - return getHeaders().remove(propertyName); - } - - public Object remove(String propertyName) - { - checkPropertyName(propertyName); - return getHeaders().remove(propertyName); - } - - public boolean isEmpty() - { - return getHeaders().isEmpty(); - } - - public void writeToBuffer(ByteBuffer data) - { - getHeaders().writeToBuffer(data); - } - - public Enumeration getMapNames() - { - return getPropertyNames(); - } - - protected static void checkPropertyName(CharSequence propertyName) - { - if (propertyName == null) - { - throw new IllegalArgumentException("Property name must not be null"); - } - else if (propertyName.length() == 0) - { - throw new IllegalArgumentException("Property name must not be the empty string"); - } - - checkIdentiferFormat(propertyName); - } - - protected static void checkIdentiferFormat(CharSequence propertyName) - { -// JMS requirements 3.5.1 Property Names -// Identifiers: -// - An identifier is an unlimited-length character sequence that must begin -// with a Java identifier start character; all following characters must be Java -// identifier part characters. An identifier start character is any character for -// which the method Character.isJavaIdentifierStart returns true. This includes -// '_' and '$'. An identifier part character is any character for which the -// method Character.isJavaIdentifierPart returns true. -// - Identifiers cannot be the names NULL, TRUE, or FALSE. -// � Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or -// ESCAPE. -// � Identifiers are either header field references or property references. The -// type of a property value in a message selector corresponds to the type -// used to set the property. If a property that does not exist in a message is -// referenced, its value is NULL. The semantics of evaluating NULL values -// in a selector are described in Section 3.8.1.2, �Null Values.� -// � The conversions that apply to the get methods for properties do not -// apply when a property is used in a message selector expression. For -// example, suppose you set a property as a string value, as in the -// following: -// myMessage.setStringProperty("NumberOfOrders", "2"); -// The following expression in a message selector would evaluate to false, -// because a string cannot be used in an arithmetic expression: -// "NumberOfOrders > 1" -// � Identifiers are case sensitive. -// � Message header field references are restricted to JMSDeliveryMode, -// JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and -// JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be -// null and if so are treated as a NULL value. - - if (Boolean.getBoolean("strict-jms")) - { - // JMS start character - if (!(Character.isJavaIdentifierStart(propertyName.charAt(0)))) - { - throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid JMS identifier start character"); - } - - // JMS part character - int length = propertyName.length(); - for (int c = 1; c < length; c++) - { - if (!(Character.isJavaIdentifierPart(propertyName.charAt(c)))) - { - throw new IllegalArgumentException("Identifier '" + propertyName + "' contains an invalid JMS identifier character"); - } - } - - // JMS invalid names - if ((propertyName.equals("NULL") - || propertyName.equals("TRUE") - || propertyName.equals("FALSE") - || propertyName.equals("NOT") - || propertyName.equals("AND") - || propertyName.equals("OR") - || propertyName.equals("BETWEEN") - || propertyName.equals("LIKE") - || propertyName.equals("IN") - || propertyName.equals("IS") - || propertyName.equals("ESCAPE"))) - { - throw new IllegalArgumentException("Identifier '" + propertyName + "' is not allowed in JMS"); - } - } - - } -} +/* + * + * 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.client.message; + +import java.util.Enumeration; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQPInvalidClassException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; + + +public final class JMSHeaderAdapter +{ + private final FieldTable _headers; + + public JMSHeaderAdapter(FieldTable headers) + { + _headers = headers; + } + + + public FieldTable getHeaders() + { + return _headers; + } + + public boolean getBoolean(String string) throws JMSException + { + checkPropertyName(string); + Boolean b = getHeaders().getBoolean(string); + + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getBoolean can't use " + string + " item."); + } + else + { + return Boolean.valueOf((String) str); + } + } + else + { + b = Boolean.valueOf(null); + } + } + + return b; + } + + public boolean getBoolean(AMQShortString string) throws JMSException + { + checkPropertyName(string); + Boolean b = getHeaders().getBoolean(string); + + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getBoolean can't use " + string + " item."); + } + else + { + return Boolean.valueOf((String) str); + } + } + else + { + b = Boolean.valueOf(null); + } + } + + return b; + } + + public char getCharacter(String string) throws JMSException + { + checkPropertyName(string); + Character c = getHeaders().getCharacter(string); + + if (c == null) + { + if (getHeaders().isNullStringValue(string)) + { + throw new NullPointerException("Cannot convert null char"); + } + else + { + throw new MessageFormatException("getChar can't use " + string + " item."); + } + } + else + { + return (char) c; + } + } + + public byte[] getBytes(String string) throws JMSException + { + return getBytes(new AMQShortString(string)); + } + + public byte[] getBytes(AMQShortString string) throws JMSException + { + checkPropertyName(string); + + byte[] bs = getHeaders().getBytes(string); + + if (bs == null) + { + throw new MessageFormatException("getBytes can't use " + string + " item."); + } + else + { + return bs; + } + } + + public byte getByte(String string) throws JMSException + { + checkPropertyName(string); + Byte b = getHeaders().getByte(string); + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getByte can't use " + string + " item."); + } + else + { + return Byte.valueOf((String) str); + } + } + else + { + b = Byte.valueOf(null); + } + } + + return b; + } + + public short getShort(String string) throws JMSException + { + checkPropertyName(string); + Short s = getHeaders().getShort(string); + + if (s == null) + { + s = Short.valueOf(getByte(string)); + } + + return s; + } + + public int getInteger(String string) throws JMSException + { + checkPropertyName(string); + Integer i = getHeaders().getInteger(string); + + if (i == null) + { + i = Integer.valueOf(getShort(string)); + } + + return i; + } + + public long getLong(String string) throws JMSException + { + checkPropertyName(string); + Long l = getHeaders().getLong(string); + + if (l == null) + { + l = Long.valueOf(getInteger(string)); + } + + return l; + } + + public float getFloat(String string) throws JMSException + { + checkPropertyName(string); + Float f = getHeaders().getFloat(string); + + if (f == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getFloat can't use " + string + " item."); + } + else + { + return Float.valueOf((String) str); + } + } + else + { + f = Float.valueOf(null); + } + + } + + return f; + } + + public double getDouble(String string) throws JMSException + { + checkPropertyName(string); + Double d = getHeaders().getDouble(string); + + if (d == null) + { + d = Double.valueOf(getFloat(string)); + } + + return d; + } + + public String getString(String string) throws JMSException + { + checkPropertyName(string); + String s = getHeaders().getString(string); + + if (s == null) + { + if (getHeaders().containsKey(string)) + { + Object o = getHeaders().getObject(string); + if (o instanceof byte[]) + { + throw new MessageFormatException("getObject couldn't find " + string + " item."); + } + else + { + if (o == null) + { + return null; + } + else + { + s = String.valueOf(o); + } + } + }//else return s // null; + } + + return s; + } + + public Object getObject(String string) throws JMSException + { + checkPropertyName(string); + return getHeaders().getObject(string); + } + + public void setBoolean(AMQShortString string, boolean b) throws JMSException + { + checkPropertyName(string); + getHeaders().setBoolean(string, b); + } + + public void setBoolean(String string, boolean b) throws JMSException + { + checkPropertyName(string); + getHeaders().setBoolean(string, b); + } + + public void setChar(String string, char c) throws JMSException + { + checkPropertyName(string); + getHeaders().setChar(string, c); + } + + public Object setBytes(AMQShortString string, byte[] bytes) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes); + } + + public Object setBytes(String string, byte[] bytes) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes); + } + + public Object setBytes(String string, byte[] bytes, int start, int length) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes, start, length); + } + + public void setByte(String string, byte b) throws JMSException + { + checkPropertyName(string); + getHeaders().setByte(string, b); + } + + public void setByte(AMQShortString string, byte b) throws JMSException + { + checkPropertyName(string); + getHeaders().setByte(string, b); + } + + + public void setShort(String string, short i) throws JMSException + { + checkPropertyName(string); + getHeaders().setShort(string, i); + } + + public void setInteger(String string, int i) throws JMSException + { + checkPropertyName(string); + getHeaders().setInteger(string, i); + } + + public void setInteger(AMQShortString string, int i) throws JMSException + { + checkPropertyName(string); + getHeaders().setInteger(string, i); + } + + public void setLong(String string, long l) throws JMSException + { + checkPropertyName(string); + getHeaders().setLong(string, l); + } + + public void setFloat(String string, float v) throws JMSException + { + checkPropertyName(string); + getHeaders().setFloat(string, v); + } + + public void setDouble(String string, double v) throws JMSException + { + checkPropertyName(string); + getHeaders().setDouble(string, v); + } + + public void setString(String string, String string1) throws JMSException + { + checkPropertyName(string); + getHeaders().setString(string, string1); + } + + public void setString(AMQShortString string, String string1) throws JMSException + { + checkPropertyName(string); + getHeaders().setString(string, string1); + } + + public void setObject(String string, Object object) throws JMSException + { + checkPropertyName(string); + try + { + getHeaders().setObject(string, object); + } + catch (AMQPInvalidClassException aice) + { + MessageFormatException mfe = new MessageFormatException("Only primatives are allowed object is:" + object.getClass()); + mfe.setLinkedException(aice); + throw mfe; + } + } + + public boolean itemExists(String string) throws JMSException + { + checkPropertyName(string); + return getHeaders().containsKey(string); + } + + public Enumeration getPropertyNames() + { + return getHeaders().getPropertyNames(); + } + + public void clear() + { + getHeaders().clear(); + } + + public boolean propertyExists(AMQShortString propertyName) + { + checkPropertyName(propertyName); + return getHeaders().propertyExists(propertyName); + } + + public boolean propertyExists(String propertyName) + { + checkPropertyName(propertyName); + return getHeaders().propertyExists(propertyName); + } + + public Object put(Object key, Object value) + { + checkPropertyName(key.toString()); + return getHeaders().setObject(key.toString(), value); + } + + public Object remove(AMQShortString propertyName) + { + checkPropertyName(propertyName); + return getHeaders().remove(propertyName); + } + + public Object remove(String propertyName) + { + checkPropertyName(propertyName); + return getHeaders().remove(propertyName); + } + + public boolean isEmpty() + { + return getHeaders().isEmpty(); + } + + public void writeToBuffer(ByteBuffer data) + { + getHeaders().writeToBuffer(data); + } + + public Enumeration getMapNames() + { + return getPropertyNames(); + } + + protected static void checkPropertyName(CharSequence propertyName) + { + if (propertyName == null) + { + throw new IllegalArgumentException("Property name must not be null"); + } + else if (propertyName.length() == 0) + { + throw new IllegalArgumentException("Property name must not be the empty string"); + } + + checkIdentiferFormat(propertyName); + } + + protected static void checkIdentiferFormat(CharSequence propertyName) + { +// JMS requirements 3.5.1 Property Names +// Identifiers: +// - An identifier is an unlimited-length character sequence that must begin +// with a Java identifier start character; all following characters must be Java +// identifier part characters. An identifier start character is any character for +// which the method Character.isJavaIdentifierStart returns true. This includes +// '_' and '$'. An identifier part character is any character for which the +// method Character.isJavaIdentifierPart returns true. +// - Identifiers cannot be the names NULL, TRUE, or FALSE. +// � Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or +// ESCAPE. +// � Identifiers are either header field references or property references. The +// type of a property value in a message selector corresponds to the type +// used to set the property. If a property that does not exist in a message is +// referenced, its value is NULL. The semantics of evaluating NULL values +// in a selector are described in Section 3.8.1.2, �Null Values.� +// � The conversions that apply to the get methods for properties do not +// apply when a property is used in a message selector expression. For +// example, suppose you set a property as a string value, as in the +// following: +// myMessage.setStringProperty("NumberOfOrders", "2"); +// The following expression in a message selector would evaluate to false, +// because a string cannot be used in an arithmetic expression: +// "NumberOfOrders > 1" +// � Identifiers are case sensitive. +// � Message header field references are restricted to JMSDeliveryMode, +// JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and +// JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be +// null and if so are treated as a NULL value. + + if (Boolean.getBoolean("strict-jms")) + { + // JMS start character + if (!(Character.isJavaIdentifierStart(propertyName.charAt(0)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid JMS identifier start character"); + } + + // JMS part character + int length = propertyName.length(); + for (int c = 1; c < length; c++) + { + if (!(Character.isJavaIdentifierPart(propertyName.charAt(c)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' contains an invalid JMS identifier character"); + } + } + + // JMS invalid names + if ((propertyName.equals("NULL") + || propertyName.equals("TRUE") + || propertyName.equals("FALSE") + || propertyName.equals("NOT") + || propertyName.equals("AND") + || propertyName.equals("OR") + || propertyName.equals("BETWEEN") + || propertyName.equals("LIKE") + || propertyName.equals("IN") + || propertyName.equals("IS") + || propertyName.equals("ESCAPE"))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' is not allowed in JMS"); + } + } + + } +} diff --git a/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java index 5185278eef..2c99b9a97b 100644 --- a/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java +++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java @@ -1,32 +1,32 @@ -/* - * - * 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.client.state; - -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.AMQException; - -public class AMQMethodNotImplementedException extends AMQException -{ - public AMQMethodNotImplementedException(AMQMethodBody body) - { - super(null, "Unexpected Method Received: " + body.getClass().getName(), null); - } -} +/* + * + * 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.client.state; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.AMQException; + +public class AMQMethodNotImplementedException extends AMQException +{ + public AMQMethodNotImplementedException(AMQMethodBody body) + { + super(null, "Unexpected Method Received: " + body.getClass().getName(), null); + } +} diff --git a/java/client/src/main/java/org/apache/qpid/jms/Message.java b/java/client/src/main/java/org/apache/qpid/jms/Message.java index 6752ee616f..e65f9ad2f4 100644 --- a/java/client/src/main/java/org/apache/qpid/jms/Message.java +++ b/java/client/src/main/java/org/apache/qpid/jms/Message.java @@ -1,28 +1,28 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.jms; - -import javax.jms.JMSException; - -public interface Message extends javax.jms.Message -{ - public void acknowledgeThis() throws JMSException; -} +/* + * + * 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.jms; + +import javax.jms.JMSException; + +public interface Message extends javax.jms.Message +{ + public void acknowledgeThis() throws JMSException; +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java index a5279a195b..1738db7239 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java @@ -1,104 +1,104 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.test.unit.basic; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.testutil.QpidTestCase; - -import javax.jms.Session; -import javax.jms.QueueSession; -import javax.jms.Queue; -import javax.jms.QueueSender; -import javax.jms.TextMessage; -import javax.jms.InvalidDestinationException; - -public class InvalidDestinationTest extends QpidTestCase -{ - private AMQConnection _connection; - - protected void setUp() throws Exception - { - super.setUp(); - _connection = (AMQConnection) getConnection("guest", "guest"); - } - - protected void tearDown() throws Exception - { - _connection.close(); - super.tearDown(); - } - - - - public void testInvalidDestination() throws Exception - { - Queue invalidDestination = new AMQQueue("amq.direct","unknownQ"); - AMQQueue validDestination = new AMQQueue("amq.direct","knownQ"); - QueueSession queueSession = _connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); - - // This is the only easy way to create and bind a queue from the API :-( - queueSession.createConsumer(validDestination); - - QueueSender sender = queueSession.createSender(invalidDestination); - TextMessage msg = queueSession.createTextMessage("Hello"); - try - { - sender.send(msg); - fail("Expected InvalidDestinationException"); - } - catch (InvalidDestinationException ex) - { - // pass - } - sender.close(); - - sender = queueSession.createSender(null); - invalidDestination = new AMQQueue("amq.direct","unknownQ"); - - try - { - sender.send(invalidDestination,msg); - fail("Expected InvalidDestinationException"); - } - catch (InvalidDestinationException ex) - { - // pass - } - sender.send(validDestination,msg); - sender.close(); - validDestination = new AMQQueue("amq.direct","knownQ"); - sender = queueSession.createSender(validDestination); - sender.send(msg); - - - - - } - - - public static junit.framework.Test suite() - { - - return new junit.framework.TestSuite(InvalidDestinationTest.class); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.test.unit.basic; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.testutil.QpidTestCase; + +import javax.jms.Session; +import javax.jms.QueueSession; +import javax.jms.Queue; +import javax.jms.QueueSender; +import javax.jms.TextMessage; +import javax.jms.InvalidDestinationException; + +public class InvalidDestinationTest extends QpidTestCase +{ + private AMQConnection _connection; + + protected void setUp() throws Exception + { + super.setUp(); + _connection = (AMQConnection) getConnection("guest", "guest"); + } + + protected void tearDown() throws Exception + { + _connection.close(); + super.tearDown(); + } + + + + public void testInvalidDestination() throws Exception + { + Queue invalidDestination = new AMQQueue("amq.direct","unknownQ"); + AMQQueue validDestination = new AMQQueue("amq.direct","knownQ"); + QueueSession queueSession = _connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + + // This is the only easy way to create and bind a queue from the API :-( + queueSession.createConsumer(validDestination); + + QueueSender sender = queueSession.createSender(invalidDestination); + TextMessage msg = queueSession.createTextMessage("Hello"); + try + { + sender.send(msg); + fail("Expected InvalidDestinationException"); + } + catch (InvalidDestinationException ex) + { + // pass + } + sender.close(); + + sender = queueSession.createSender(null); + invalidDestination = new AMQQueue("amq.direct","unknownQ"); + + try + { + sender.send(invalidDestination,msg); + fail("Expected InvalidDestinationException"); + } + catch (InvalidDestinationException ex) + { + // pass + } + sender.send(validDestination,msg); + sender.close(); + validDestination = new AMQQueue("amq.direct","knownQ"); + sender = queueSession.createSender(validDestination); + sender.send(msg); + + + + + } + + + public static junit.framework.Test suite() + { + + return new junit.framework.TestSuite(InvalidDestinationTest.class); + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java index 34197f2608..46b99fac8d 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java @@ -1,221 +1,221 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.test.unit.client.temporaryqueue; - -import javax.jms.Connection; -import javax.jms.JMSException; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TemporaryQueue; -import javax.jms.TextMessage; -import junit.framework.Assert; - -import org.apache.qpid.testutil.QpidTestCase; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.transport.TransportConnection; - -import java.util.List; -import java.util.LinkedList; - -public class TemporaryQueueTest extends QpidTestCase -{ - protected void setUp() throws Exception - { - super.setUp(); - } - - protected void tearDown() throws Exception - { - super.tearDown(); - } - - protected Connection createConnection() throws Exception - { - return getConnection("guest", "guest"); - } - - public void testTempoaryQueue() throws Exception - { - Connection conn = createConnection(); - Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - TemporaryQueue queue = session.createTemporaryQueue(); - assertNotNull(queue); - MessageProducer producer = session.createProducer(queue); - MessageConsumer consumer = session.createConsumer(queue); - conn.start(); - producer.send(session.createTextMessage("hello")); - TextMessage tm = (TextMessage) consumer.receive(2000); - assertNotNull(tm); - assertEquals("hello", tm.getText()); - - try - { - queue.delete(); - fail("Expected JMSException : should not be able to delete while there are active consumers"); - } - catch (JMSException je) - { - ; //pass - } - - consumer.close(); - - try - { - queue.delete(); - } - catch (JMSException je) - { - fail("Unexpected Exception: " + je.getMessage()); - } - - conn.close(); - } - - public void tUniqueness() throws Exception - { - int numProcs = Runtime.getRuntime().availableProcessors(); - final int threadsProc = 5; - - runUniqueness(1, 10); - runUniqueness(numProcs * threadsProc, 10); - runUniqueness(numProcs * threadsProc, 100); - runUniqueness(numProcs * threadsProc, 500); - } - - void runUniqueness(int makers, int queues) throws Exception - { - Connection connection = createConnection(); - - Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); - - List tqList = new LinkedList(); - - //Create Makers - for (int m = 0; m < makers; m++) - { - tqList.add(new TempQueueMaker(session, queues)); - } - - - List threadList = new LinkedList(); - - //Create Makers - for (TempQueueMaker maker : tqList) - { - threadList.add(new Thread(maker)); - } - - //Start threads - for (Thread thread : threadList) - { - thread.start(); - } - - // Join Threads - for (Thread thread : threadList) - { - try - { - thread.join(); - } - catch (InterruptedException e) - { - fail("Couldn't correctly join threads"); - } - } - - - List list = new LinkedList(); - - // Test values - for (TempQueueMaker maker : tqList) - { - check(maker, list); - } - - Assert.assertEquals("Not enough queues made.", makers * queues, list.size()); - - connection.close(); - } - - private void check(TempQueueMaker tq, List list) - { - for (AMQQueue q : tq.getList()) - { - if (list.contains(q)) - { - fail(q + " already exists."); - } - else - { - list.add(q); - } - } - } - - - class TempQueueMaker implements Runnable - { - List _queues; - Session _session; - private int _count; - - - TempQueueMaker(Session session, int queues) throws JMSException - { - _queues = new LinkedList(); - - _count = queues; - - _session = session; - } - - public void run() - { - int i = 0; - try - { - for (; i < _count; i++) - { - _queues.add((AMQQueue) _session.createTemporaryQueue()); - } - } - catch (JMSException jmse) - { - //stop - } - } - - List getList() - { - return _queues; - } - } - - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(TemporaryQueueTest.class); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.test.unit.client.temporaryqueue; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TemporaryQueue; +import javax.jms.TextMessage; +import junit.framework.Assert; + +import org.apache.qpid.testutil.QpidTestCase; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.transport.TransportConnection; + +import java.util.List; +import java.util.LinkedList; + +public class TemporaryQueueTest extends QpidTestCase +{ + protected void setUp() throws Exception + { + super.setUp(); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + protected Connection createConnection() throws Exception + { + return getConnection("guest", "guest"); + } + + public void testTempoaryQueue() throws Exception + { + Connection conn = createConnection(); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryQueue queue = session.createTemporaryQueue(); + assertNotNull(queue); + MessageProducer producer = session.createProducer(queue); + MessageConsumer consumer = session.createConsumer(queue); + conn.start(); + producer.send(session.createTextMessage("hello")); + TextMessage tm = (TextMessage) consumer.receive(2000); + assertNotNull(tm); + assertEquals("hello", tm.getText()); + + try + { + queue.delete(); + fail("Expected JMSException : should not be able to delete while there are active consumers"); + } + catch (JMSException je) + { + ; //pass + } + + consumer.close(); + + try + { + queue.delete(); + } + catch (JMSException je) + { + fail("Unexpected Exception: " + je.getMessage()); + } + + conn.close(); + } + + public void tUniqueness() throws Exception + { + int numProcs = Runtime.getRuntime().availableProcessors(); + final int threadsProc = 5; + + runUniqueness(1, 10); + runUniqueness(numProcs * threadsProc, 10); + runUniqueness(numProcs * threadsProc, 100); + runUniqueness(numProcs * threadsProc, 500); + } + + void runUniqueness(int makers, int queues) throws Exception + { + Connection connection = createConnection(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + List tqList = new LinkedList(); + + //Create Makers + for (int m = 0; m < makers; m++) + { + tqList.add(new TempQueueMaker(session, queues)); + } + + + List threadList = new LinkedList(); + + //Create Makers + for (TempQueueMaker maker : tqList) + { + threadList.add(new Thread(maker)); + } + + //Start threads + for (Thread thread : threadList) + { + thread.start(); + } + + // Join Threads + for (Thread thread : threadList) + { + try + { + thread.join(); + } + catch (InterruptedException e) + { + fail("Couldn't correctly join threads"); + } + } + + + List list = new LinkedList(); + + // Test values + for (TempQueueMaker maker : tqList) + { + check(maker, list); + } + + Assert.assertEquals("Not enough queues made.", makers * queues, list.size()); + + connection.close(); + } + + private void check(TempQueueMaker tq, List list) + { + for (AMQQueue q : tq.getList()) + { + if (list.contains(q)) + { + fail(q + " already exists."); + } + else + { + list.add(q); + } + } + } + + + class TempQueueMaker implements Runnable + { + List _queues; + Session _session; + private int _count; + + + TempQueueMaker(Session session, int queues) throws JMSException + { + _queues = new LinkedList(); + + _count = queues; + + _session = session; + } + + public void run() + { + int i = 0; + try + { + for (; i < _count; i++) + { + _queues.add((AMQQueue) _session.createTemporaryQueue()); + } + } + catch (JMSException jmse) + { + //stop + } + } + + List getList() + { + return _queues; + } + } + + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TemporaryQueueTest.class); + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java index d25986d991..54b2ee95f4 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java @@ -1,144 +1,144 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.unit.close; - -import junit.framework.Assert; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.testutil.QpidTestCase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.qpid.junit.concurrency.TestRunnable; -import org.apache.qpid.junit.concurrency.ThreadTestCoordinator; - -import javax.jms.Connection; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.Session; - -/** - * This test forces the situation where a session is closed whilst a message consumer is still in its onMessage method. - * Running in AUTO_ACK mode, the close call ought to wait until the onMessage method completes, and the ack is sent - * before closing the connection. - * - *

CRC Card
Responsibilities Collaborations
Check that - * closing a connection whilst handling a message, blocks till completion of the handler.
- */ -public class CloseBeforeAckTest extends QpidTestCase -{ - private static final Logger log = LoggerFactory.getLogger(CloseBeforeAckTest.class); - - Connection connection; - Session session; - public static final String TEST_QUEUE_NAME = "TestQueue"; - private int TEST_COUNT = 25; - - class TestThread1 extends TestRunnable implements MessageListener - { - public void runWithExceptions() throws Exception - { - // Set this up to listen for message on the test session. - session.createConsumer(session.createQueue(TEST_QUEUE_NAME)).setMessageListener(this); - } - - public void onMessage(Message message) - { - // Give thread 2 permission to close the session. - allow(new int[] { 1 }); - - // Wait until thread 2 has closed the connection, or is blocked waiting for this to complete. - waitFor(new int[] { 1 }, true); - } - } - - TestThread1 testThread1 = new TestThread1(); - - TestRunnable testThread2 = - new TestRunnable() - { - public void runWithExceptions() throws Exception - { - // Send a message to be picked up by thread 1. - session.createProducer(null).send(session.createQueue(TEST_QUEUE_NAME), - session.createTextMessage("Hi there thread 1!")); - - // Wait for thread 1 to pick up the message and give permission to continue. - waitFor(new int[] { 0 }, false); - - // Close the connection. - session.close(); - - // Allow thread 1 to continue to completion, if it is erronously still waiting. - allow(new int[] { 1 }); - } - }; - - public void testCloseBeforeAutoAck_QPID_397() throws Exception - { - // Create a session in auto acknowledge mode. This problem shows up in auto acknowledge if the client acks - // message at the end of the onMessage method, after a close has been sent. - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - ThreadTestCoordinator tt = new ThreadTestCoordinator(2); - - tt.addTestThread(testThread1, 0); - tt.addTestThread(testThread2, 1); - tt.setDeadlockTimeout(500); - tt.run(); - - String errorMessage = tt.joinAndRetrieveMessages(); - - // Print any error messages or exceptions. - log.debug(errorMessage); - - if (!tt.getExceptions().isEmpty()) - { - for (Exception e : tt.getExceptions()) - { - log.debug("Exception thrown during test thread: ", e); - } - } - - Assert.assertTrue(errorMessage, "".equals(errorMessage)); - } - - public void closeBeforeAutoAckManyTimes() throws Exception - { - for (int i = 0; i < TEST_COUNT; i++) - { - testCloseBeforeAutoAck_QPID_397(); - } - } - - protected void setUp() throws Exception - { - super.setUp(); - connection = getConnection("guest", "guest"); - } - - protected void tearDown() throws Exception - { - super.tearDown(); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.close; + +import junit.framework.Assert; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.testutil.QpidTestCase; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.junit.concurrency.TestRunnable; +import org.apache.qpid.junit.concurrency.ThreadTestCoordinator; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; + +/** + * This test forces the situation where a session is closed whilst a message consumer is still in its onMessage method. + * Running in AUTO_ACK mode, the close call ought to wait until the onMessage method completes, and the ack is sent + * before closing the connection. + * + *

CRC Card
Responsibilities Collaborations
Check that + * closing a connection whilst handling a message, blocks till completion of the handler.
+ */ +public class CloseBeforeAckTest extends QpidTestCase +{ + private static final Logger log = LoggerFactory.getLogger(CloseBeforeAckTest.class); + + Connection connection; + Session session; + public static final String TEST_QUEUE_NAME = "TestQueue"; + private int TEST_COUNT = 25; + + class TestThread1 extends TestRunnable implements MessageListener + { + public void runWithExceptions() throws Exception + { + // Set this up to listen for message on the test session. + session.createConsumer(session.createQueue(TEST_QUEUE_NAME)).setMessageListener(this); + } + + public void onMessage(Message message) + { + // Give thread 2 permission to close the session. + allow(new int[] { 1 }); + + // Wait until thread 2 has closed the connection, or is blocked waiting for this to complete. + waitFor(new int[] { 1 }, true); + } + } + + TestThread1 testThread1 = new TestThread1(); + + TestRunnable testThread2 = + new TestRunnable() + { + public void runWithExceptions() throws Exception + { + // Send a message to be picked up by thread 1. + session.createProducer(null).send(session.createQueue(TEST_QUEUE_NAME), + session.createTextMessage("Hi there thread 1!")); + + // Wait for thread 1 to pick up the message and give permission to continue. + waitFor(new int[] { 0 }, false); + + // Close the connection. + session.close(); + + // Allow thread 1 to continue to completion, if it is erronously still waiting. + allow(new int[] { 1 }); + } + }; + + public void testCloseBeforeAutoAck_QPID_397() throws Exception + { + // Create a session in auto acknowledge mode. This problem shows up in auto acknowledge if the client acks + // message at the end of the onMessage method, after a close has been sent. + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ThreadTestCoordinator tt = new ThreadTestCoordinator(2); + + tt.addTestThread(testThread1, 0); + tt.addTestThread(testThread2, 1); + tt.setDeadlockTimeout(500); + tt.run(); + + String errorMessage = tt.joinAndRetrieveMessages(); + + // Print any error messages or exceptions. + log.debug(errorMessage); + + if (!tt.getExceptions().isEmpty()) + { + for (Exception e : tt.getExceptions()) + { + log.debug("Exception thrown during test thread: ", e); + } + } + + Assert.assertTrue(errorMessage, "".equals(errorMessage)); + } + + public void closeBeforeAutoAckManyTimes() throws Exception + { + for (int i = 0; i < TEST_COUNT; i++) + { + testCloseBeforeAutoAck_QPID_397(); + } + } + + protected void setUp() throws Exception + { + super.setUp(); + connection = getConnection("guest", "guest"); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } +} diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java index 0b9d0bdc2d..131cbd5f68 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java @@ -1,89 +1,89 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.test.unit.message; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.testutil.QpidTestCase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.jms.Connection; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Queue; -import javax.jms.Session; -import javax.jms.TextMessage; - -/** - * @author Apache Software Foundation - */ -public class JMSDestinationTest extends QpidTestCase -{ - private static final Logger _logger = LoggerFactory.getLogger(JMSDestinationTest.class); - - - protected void setUp() throws Exception - { - super.setUp(); - } - - protected void tearDown() throws Exception - { - super.tearDown(); - } - - public void testJMSDestination() throws Exception - { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); - AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); - Queue queue = - new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false, - true); - MessageConsumer consumer = consumerSession.createConsumer(queue); - - Connection con2 = (AMQConnection) getConnection("guest", "guest"); - Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); - MessageProducer producer = producerSession.createProducer(queue); - - TextMessage sentMsg = producerSession.createTextMessage("hello"); - assertNull(sentMsg.getJMSDestination()); - - producer.send(sentMsg); - - assertEquals(sentMsg.getJMSDestination(), queue); - - con2.close(); - - con.start(); - - TextMessage rm = (TextMessage) consumer.receive(); - assertNotNull(rm); - - assertEquals(rm.getJMSDestination(), queue); - con.close(); - } - -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.test.unit.message; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.testutil.QpidTestCase; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * @author Apache Software Foundation + */ +public class JMSDestinationTest extends QpidTestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(JMSDestinationTest.class); + + + protected void setUp() throws Exception + { + super.setUp(); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + public void testJMSDestination() throws Exception + { + AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = + new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false, + true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + + Connection con2 = (AMQConnection) getConnection("guest", "guest"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + + TextMessage sentMsg = producerSession.createTextMessage("hello"); + assertNull(sentMsg.getJMSDestination()); + + producer.send(sentMsg); + + assertEquals(sentMsg.getJMSDestination(), queue); + + con2.close(); + + con.start(); + + TextMessage rm = (TextMessage) consumer.receive(); + assertNotNull(rm); + + assertEquals(rm.getJMSDestination(), queue); + con.close(); + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java b/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java index b31765ce66..0eefc03016 100644 --- a/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java +++ b/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java @@ -1,43 +1,43 @@ -/* - * - * 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; - -/** - * AMQUnknownExchangeType represents coding error where unknown exchange type requested from exchange factory. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Represents unknown exchange type request. - *
- * - * @todo Not an AMQP exception as no status code. - * - * @todo Represent coding error, where unknown exchange type is requested by passing a string parameter. Use a type safe - * enum for the exchange type, or replace with IllegalArgumentException. Should be runtime. - */ -public class AMQUnknownExchangeType extends AMQException -{ - public AMQUnknownExchangeType(String message, Throwable cause) - { - super(null, message, cause); - } -} +/* + * + * 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; + +/** + * AMQUnknownExchangeType represents coding error where unknown exchange type requested from exchange factory. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents unknown exchange type request. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Represent coding error, where unknown exchange type is requested by passing a string parameter. Use a type safe + * enum for the exchange type, or replace with IllegalArgumentException. Should be runtime. + */ +public class AMQUnknownExchangeType extends AMQException +{ + public AMQUnknownExchangeType(String message, Throwable cause) + { + super(null, message, cause); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java index 64af717342..ad7f36f790 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java @@ -1,96 +1,96 @@ -package org.apache.qpid.framing; - -/* - * - * 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. - * - */ - -import org.apache.mina.common.ByteBuffer; -import org.apache.qpid.AMQChannelException; -import org.apache.qpid.AMQConnectionException; -import org.apache.qpid.AMQException; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; - -public abstract class AMQMethodBodyImpl implements AMQMethodBody -{ - public static final byte TYPE = 1; - - public AMQMethodBodyImpl() - { - } - - public byte getFrameType() - { - return TYPE; - } - - - /** unsigned short */ - abstract protected int getBodySize(); - - - public AMQFrame generateFrame(int channelId) - { - return new AMQFrame(channelId, this); - } - - /** - * Creates an AMQChannelException for the corresponding body type (a channel exception should include the class and - * method ids of the body it resulted from). - */ - - /** - * Convenience Method to create a channel not found exception - * - * @param channelId The channel id that is not found - * - * @return new AMQChannelException - */ - public AMQChannelException getChannelNotFoundException(int channelId) - { - return getChannelException(AMQConstant.NOT_FOUND, "Channel not found for id:" + channelId); - } - - public AMQChannelException getChannelException(AMQConstant code, String message) - { - return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); - } - - public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause) - { - return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); - } - - public AMQConnectionException getConnectionException(AMQConstant code, String message) - { - return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); - } - - public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause) - { - return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); - } - - public void handle(final int channelId, final AMQVersionAwareProtocolSession session) throws AMQException - { - session.methodFrameReceived(channelId, this); - } - -} +package org.apache.qpid.framing; + +/* + * + * 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. + * + */ + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +public abstract class AMQMethodBodyImpl implements AMQMethodBody +{ + public static final byte TYPE = 1; + + public AMQMethodBodyImpl() + { + } + + public byte getFrameType() + { + return TYPE; + } + + + /** unsigned short */ + abstract protected int getBodySize(); + + + public AMQFrame generateFrame(int channelId) + { + return new AMQFrame(channelId, this); + } + + /** + * Creates an AMQChannelException for the corresponding body type (a channel exception should include the class and + * method ids of the body it resulted from). + */ + + /** + * Convenience Method to create a channel not found exception + * + * @param channelId The channel id that is not found + * + * @return new AMQChannelException + */ + public AMQChannelException getChannelNotFoundException(int channelId) + { + return getChannelException(AMQConstant.NOT_FOUND, "Channel not found for id:" + channelId); + } + + public AMQChannelException getChannelException(AMQConstant code, String message) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); + } + + public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession session) throws AMQException + { + session.methodFrameReceived(channelId, this); + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java index 0030742e94..0c61d9db3c 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java @@ -1,30 +1,30 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.framing; - -import org.apache.mina.common.ByteBuffer; - - -public abstract interface AMQMethodBodyInstanceFactory -{ - public AMQMethodBody newInstance(ByteBuffer buffer, long size) throws AMQFrameDecodingException; -} +/* + * + * 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.framing; + +import org.apache.mina.common.ByteBuffer; + + +public abstract interface AMQMethodBodyInstanceFactory +{ + public AMQMethodBody newInstance(ByteBuffer buffer, long size) throws AMQFrameDecodingException; +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java index 4ffc9e0066..bfcc38ad60 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java @@ -1,90 +1,90 @@ -/* - * - * 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.framing; - -import org.apache.mina.common.ByteBuffer; - - -public interface AMQMethodFactory -{ - - // Connection Methods - - ConnectionCloseBody createConnectionClose(); - - // Access Methods - - AccessRequestBody createAccessRequest(boolean active, boolean exclusive, boolean passive, boolean read, AMQShortString realm, boolean write); - - - // Tx Methods - - TxSelectBody createTxSelect(); - - TxCommitBody createTxCommit(); - - TxRollbackBody createTxRollback(); - - // Channel Methods - - ChannelOpenBody createChannelOpen(); - - ChannelCloseBody createChannelClose(int replyCode, AMQShortString replyText); - - ChannelFlowBody createChannelFlow(boolean active); - - - // Exchange Methods - - - ExchangeBoundBody createExchangeBound(AMQShortString exchangeName, - AMQShortString queueName, - AMQShortString routingKey); - - ExchangeDeclareBody createExchangeDeclare(AMQShortString name, AMQShortString type, int ticket); - - - // Queue Methods - - QueueDeclareBody createQueueDeclare(AMQShortString name, FieldTable arguments, boolean autoDelete, boolean durable, boolean exclusive, boolean passive, int ticket); - - QueueBindBody createQueueBind(AMQShortString queueName, AMQShortString exchangeName, AMQShortString routingKey, FieldTable arguments, int ticket); - - QueueDeleteBody createQueueDelete(AMQShortString queueName, boolean ifEmpty, boolean ifUnused, int ticket); - - - // Message Methods - - // In different versions of the protocol we change the class used for message transfer - // abstract this out so the appropriate methods are created - AMQMethodBody createRecover(boolean requeue); - - AMQMethodBody createConsumer(AMQShortString tag, AMQShortString queueName, FieldTable arguments, boolean noAck, boolean exclusive, boolean noLocal, int ticket); - - AMQMethodBody createConsumerCancel(AMQShortString consumerTag); - - AMQMethodBody createAcknowledge(long deliveryTag, boolean multiple); - - AMQMethodBody createRejectBody(long deliveryTag, boolean requeue); - - AMQMethodBody createMessageQos(int prefetchCount, int prefetchSize); - -} +/* + * + * 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.framing; + +import org.apache.mina.common.ByteBuffer; + + +public interface AMQMethodFactory +{ + + // Connection Methods + + ConnectionCloseBody createConnectionClose(); + + // Access Methods + + AccessRequestBody createAccessRequest(boolean active, boolean exclusive, boolean passive, boolean read, AMQShortString realm, boolean write); + + + // Tx Methods + + TxSelectBody createTxSelect(); + + TxCommitBody createTxCommit(); + + TxRollbackBody createTxRollback(); + + // Channel Methods + + ChannelOpenBody createChannelOpen(); + + ChannelCloseBody createChannelClose(int replyCode, AMQShortString replyText); + + ChannelFlowBody createChannelFlow(boolean active); + + + // Exchange Methods + + + ExchangeBoundBody createExchangeBound(AMQShortString exchangeName, + AMQShortString queueName, + AMQShortString routingKey); + + ExchangeDeclareBody createExchangeDeclare(AMQShortString name, AMQShortString type, int ticket); + + + // Queue Methods + + QueueDeclareBody createQueueDeclare(AMQShortString name, FieldTable arguments, boolean autoDelete, boolean durable, boolean exclusive, boolean passive, int ticket); + + QueueBindBody createQueueBind(AMQShortString queueName, AMQShortString exchangeName, AMQShortString routingKey, FieldTable arguments, int ticket); + + QueueDeleteBody createQueueDelete(AMQShortString queueName, boolean ifEmpty, boolean ifUnused, int ticket); + + + // Message Methods + + // In different versions of the protocol we change the class used for message transfer + // abstract this out so the appropriate methods are created + AMQMethodBody createRecover(boolean requeue); + + AMQMethodBody createConsumer(AMQShortString tag, AMQShortString queueName, FieldTable arguments, boolean noAck, boolean exclusive, boolean noLocal, int ticket); + + AMQMethodBody createConsumerCancel(AMQShortString consumerTag); + + AMQMethodBody createAcknowledge(long deliveryTag, boolean multiple); + + AMQMethodBody createRejectBody(long deliveryTag, boolean requeue); + + AMQMethodBody createMessageQos(int prefetchCount, int prefetchSize); + +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQType.java b/java/common/src/main/java/org/apache/qpid/framing/AMQType.java index 2c356d072c..14fb63da03 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQType.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQType.java @@ -1,795 +1,795 @@ -/* - * - * 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.framing; - -import org.apache.mina.common.ByteBuffer; - -import java.math.BigDecimal; - -/** - * AMQType is a type that represents the different possible AMQP field table types. It provides operations for each - * of the types to perform tasks such as calculating the size of an instance of the type, converting types between AMQP - * and Java native types, and reading and writing instances of AMQP types in binary formats to and from byte buffers. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Get the equivalent one byte identifier for a type. - *
Calculate the size of an instance of an AMQP parameter type. {@link EncodingUtils} - *
Convert an instance of an AMQP parameter into a compatable Java object tagged with its AMQP type. - * {@link AMQTypedValue} - *
Write an instance of an AMQP parameter type to a byte buffer. {@link EncodingUtils} - *
Read an instance of an AMQP parameter from a byte buffer. {@link EncodingUtils} - *
- */ -public enum AMQType -{ - LONG_STRING('S') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedLongStringLength((String) value); - } - - public String toNativeValue(Object value) - { - if (value != null) - { - return value.toString(); - } - else - { - throw new NullPointerException("Cannot convert: null to String."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeLongStringBytes(buffer, (String) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readLongString(buffer); - } - }, - - INTEGER('i') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.unsignedIntegerLength(); - } - - public Long toNativeValue(Object value) - { - if (value instanceof Long) - { - return (Long) value; - } - else if (value instanceof Integer) - { - return ((Integer) value).longValue(); - } - else if (value instanceof Short) - { - return ((Short) value).longValue(); - } - else if (value instanceof Byte) - { - return ((Byte) value).longValue(); - } - else if ((value instanceof String) || (value == null)) - { - return Long.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeUnsignedInteger(buffer, (Long) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readUnsignedInteger(buffer); - } - }, - - DECIMAL('D') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedByteLength() + EncodingUtils.encodedIntegerLength(); - } - - public Object toNativeValue(Object value) - { - if (value instanceof BigDecimal) - { - return (BigDecimal) value; - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to BigDecimal."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - BigDecimal bd = (BigDecimal) value; - - byte places = new Integer(bd.scale()).byteValue(); - - int unscaled = bd.intValue(); - - EncodingUtils.writeByte(buffer, places); - - EncodingUtils.writeInteger(buffer, unscaled); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - byte places = EncodingUtils.readByte(buffer); - - int unscaled = EncodingUtils.readInteger(buffer); - - BigDecimal bd = new BigDecimal(unscaled); - - return bd.setScale(places); - } - }, - - TIMESTAMP('T') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedLongLength(); - } - - public Object toNativeValue(Object value) - { - if (value instanceof Long) - { - return (Long) value; - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to timestamp."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeLong(buffer, (Long) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readLong(buffer); - } - }, - - /** - * Implements the field table type. The native value of a field table type will be an instance of - * {@link FieldTable}, which itself may contain name/value pairs encoded as {@link AMQTypedValue}s. - */ - FIELD_TABLE('F') - { - /** - * Calculates the size of an instance of the type in bytes. - * - * @param value An instance of the type. - * - * @return The size of the instance of the type in bytes. - */ - public int getEncodingSize(Object value) - { - // Ensure that the value is a FieldTable. - if (!(value instanceof FieldTable)) - { - throw new IllegalArgumentException("Value is not a FieldTable."); - } - - FieldTable ftValue = (FieldTable) value; - - // Loop over all name/value pairs adding up size of each. FieldTable itself keeps track of its encoded - // size as entries are added, so no need to loop over all explicitly. - // EncodingUtils calculation of the encoded field table lenth, will include 4 bytes for its 'size' field. - return EncodingUtils.encodedFieldTableLength(ftValue); - } - - /** - * Converts an instance of the type to an equivalent Java native representation. - * - * @param value An instance of the type. - * - * @return An equivalent Java native representation. - */ - public Object toNativeValue(Object value) - { - // Ensure that the value is a FieldTable. - if (!(value instanceof FieldTable)) - { - throw new IllegalArgumentException("Value is not a FieldTable."); - } - - return (FieldTable) value; - } - - /** - * Writes an instance of the type to a specified byte buffer. - * - * @param value An instance of the type. - * @param buffer The byte buffer to write it to. - */ - public void writeValueImpl(Object value, ByteBuffer buffer) - { - // Ensure that the value is a FieldTable. - if (!(value instanceof FieldTable)) - { - throw new IllegalArgumentException("Value is not a FieldTable."); - } - - FieldTable ftValue = (FieldTable) value; - - // Loop over all name/values writing out into buffer. - ftValue.writeToBuffer(buffer); - } - - /** - * Reads an instance of the type from a specified byte buffer. - * - * @param buffer The byte buffer to write it to. - * - * @return An instance of the type. - */ - public Object readValueFromBuffer(ByteBuffer buffer) - { - try - { - // Read size of field table then all name/value pairs. - return EncodingUtils.readFieldTable(buffer); - } - catch (AMQFrameDecodingException e) - { - throw new IllegalArgumentException("Unable to read field table from buffer.", e); - } - } - }, - - VOID('V') - { - public int getEncodingSize(Object value) - { - return 0; - } - - public Object toNativeValue(Object value) - { - if (value == null) - { - return null; - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to null String."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return null; - } - }, - - BINARY('x') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedLongstrLength((byte[]) value); - } - - public Object toNativeValue(Object value) - { - if ((value instanceof byte[]) || (value == null)) - { - return value; - } - else - { - throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName() - + ") cannot be converted to byte[]"); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeLongstr(buffer, (byte[]) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readLongstr(buffer); - } - }, - - ASCII_STRING('c') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedLongStringLength((String) value); - } - - public String toNativeValue(Object value) - { - if (value != null) - { - return value.toString(); - } - else - { - throw new NullPointerException("Cannot convert: null to String."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeLongStringBytes(buffer, (String) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readLongString(buffer); - } - }, - - WIDE_STRING('C') - { - public int getEncodingSize(Object value) - { - // FIXME: use proper charset encoder - return EncodingUtils.encodedLongStringLength((String) value); - } - - public String toNativeValue(Object value) - { - if (value != null) - { - return value.toString(); - } - else - { - throw new NullPointerException("Cannot convert: null to String."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeLongStringBytes(buffer, (String) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readLongString(buffer); - } - }, - - BOOLEAN('t') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedBooleanLength(); - } - - public Object toNativeValue(Object value) - { - if (value instanceof Boolean) - { - return (Boolean) value; - } - else if ((value instanceof String) || (value == null)) - { - return Boolean.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to boolean."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeBoolean(buffer, (Boolean) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readBoolean(buffer); - } - }, - - ASCII_CHARACTER('k') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedCharLength(); - } - - public Character toNativeValue(Object value) - { - if (value instanceof Character) - { - return (Character) value; - } - else if (value == null) - { - throw new NullPointerException("Cannot convert null into char"); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to char."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeChar(buffer, (Character) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readChar(buffer); - } - }, - - BYTE('b') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedByteLength(); - } - - public Byte toNativeValue(Object value) - { - if (value instanceof Byte) - { - return (Byte) value; - } - else if ((value instanceof String) || (value == null)) - { - return Byte.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to byte."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeByte(buffer, (Byte) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readByte(buffer); - } - }, - - SHORT('s') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedShortLength(); - } - - public Short toNativeValue(Object value) - { - if (value instanceof Short) - { - return (Short) value; - } - else if (value instanceof Byte) - { - return ((Byte) value).shortValue(); - } - else if ((value instanceof String) || (value == null)) - { - return Short.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to short."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeShort(buffer, (Short) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readShort(buffer); - } - }, - - INT('I') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedIntegerLength(); - } - - public Integer toNativeValue(Object value) - { - if (value instanceof Integer) - { - return (Integer) value; - } - else if (value instanceof Short) - { - return ((Short) value).intValue(); - } - else if (value instanceof Byte) - { - return ((Byte) value).intValue(); - } - else if ((value instanceof String) || (value == null)) - { - return Integer.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeInteger(buffer, (Integer) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readInteger(buffer); - } - }, - - LONG('l') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedLongLength(); - } - - public Object toNativeValue(Object value) - { - if (value instanceof Long) - { - return (Long) value; - } - else if (value instanceof Integer) - { - return ((Integer) value).longValue(); - } - else if (value instanceof Short) - { - return ((Short) value).longValue(); - } - else if (value instanceof Byte) - { - return ((Byte) value).longValue(); - } - else if ((value instanceof String) || (value == null)) - { - return Long.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to long."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeLong(buffer, (Long) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readLong(buffer); - } - }, - - FLOAT('f') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedFloatLength(); - } - - public Float toNativeValue(Object value) - { - if (value instanceof Float) - { - return (Float) value; - } - else if ((value instanceof String) || (value == null)) - { - return Float.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to float."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeFloat(buffer, (Float) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readFloat(buffer); - } - }, - - DOUBLE('d') - { - public int getEncodingSize(Object value) - { - return EncodingUtils.encodedDoubleLength(); - } - - public Double toNativeValue(Object value) - { - if (value instanceof Double) - { - return (Double) value; - } - else if (value instanceof Float) - { - return ((Float) value).doubleValue(); - } - else if ((value instanceof String) || (value == null)) - { - return Double.valueOf((String) value); - } - else - { - throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() - + ") to double."); - } - } - - public void writeValueImpl(Object value, ByteBuffer buffer) - { - EncodingUtils.writeDouble(buffer, (Double) value); - } - - public Object readValueFromBuffer(ByteBuffer buffer) - { - return EncodingUtils.readDouble(buffer); - } - }; - - /** Holds the defined one byte identifier for the type. */ - private final byte _identifier; - - /** - * Creates an instance of an AMQP type from its defined one byte identifier. - * - * @param identifier The one byte identifier for the type. - */ - AMQType(char identifier) - { - _identifier = (byte) identifier; - } - - /** - * Extracts the byte identifier for the typ. - * - * @return The byte identifier for the typ. - */ - public final byte identifier() - { - return _identifier; - } - - /** - * Calculates the size of an instance of the type in bytes. - * - * @param value An instance of the type. - * - * @return The size of the instance of the type in bytes. - */ - public abstract int getEncodingSize(Object value); - - /** - * Converts an instance of the type to an equivalent Java native representation. - * - * @param value An instance of the type. - * - * @return An equivalent Java native representation. - */ - public abstract Object toNativeValue(Object value); - - /** - * Converts an instance of the type to an equivalent Java native representation, packaged as an - * {@link AMQTypedValue} tagged with its AMQP type. - * - * @param value An instance of the type. - * - * @return An equivalent Java native representation, tagged with its AMQP type. - */ - public AMQTypedValue asTypedValue(Object value) - { - return new AMQTypedValue(this, toNativeValue(value)); - } - - /** - * Writes an instance of the type to a specified byte buffer, preceded by its one byte identifier. As the type and - * value are both written, this provides a fully encoded description of a parameters type and value. - * - * @param value An instance of the type. - * @param buffer The byte buffer to write it to. - */ - public void writeToBuffer(Object value, ByteBuffer buffer) - { - buffer.put(identifier()); - writeValueImpl(value, buffer); - } - - /** - * Writes an instance of the type to a specified byte buffer. - * - * @param value An instance of the type. - * @param buffer The byte buffer to write it to. - */ - abstract void writeValueImpl(Object value, ByteBuffer buffer); - - /** - * Reads an instance of the type from a specified byte buffer. - * - * @param buffer The byte buffer to write it to. - * - * @return An instance of the type. - */ - abstract Object readValueFromBuffer(ByteBuffer buffer); -} +/* + * + * 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.framing; + +import org.apache.mina.common.ByteBuffer; + +import java.math.BigDecimal; + +/** + * AMQType is a type that represents the different possible AMQP field table types. It provides operations for each + * of the types to perform tasks such as calculating the size of an instance of the type, converting types between AMQP + * and Java native types, and reading and writing instances of AMQP types in binary formats to and from byte buffers. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Get the equivalent one byte identifier for a type. + *
Calculate the size of an instance of an AMQP parameter type. {@link EncodingUtils} + *
Convert an instance of an AMQP parameter into a compatable Java object tagged with its AMQP type. + * {@link AMQTypedValue} + *
Write an instance of an AMQP parameter type to a byte buffer. {@link EncodingUtils} + *
Read an instance of an AMQP parameter from a byte buffer. {@link EncodingUtils} + *
+ */ +public enum AMQType +{ + LONG_STRING('S') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + INTEGER('i') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.unsignedIntegerLength(); + } + + public Long toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + else if (value instanceof Short) + { + return ((Short) value).longValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Long.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeUnsignedInteger(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readUnsignedInteger(buffer); + } + }, + + DECIMAL('D') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedByteLength() + EncodingUtils.encodedIntegerLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to BigDecimal."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + BigDecimal bd = (BigDecimal) value; + + byte places = new Integer(bd.scale()).byteValue(); + + int unscaled = bd.intValue(); + + EncodingUtils.writeByte(buffer, places); + + EncodingUtils.writeInteger(buffer, unscaled); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + byte places = EncodingUtils.readByte(buffer); + + int unscaled = EncodingUtils.readInteger(buffer); + + BigDecimal bd = new BigDecimal(unscaled); + + return bd.setScale(places); + } + }, + + TIMESTAMP('T') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to timestamp."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLong(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLong(buffer); + } + }, + + /** + * Implements the field table type. The native value of a field table type will be an instance of + * {@link FieldTable}, which itself may contain name/value pairs encoded as {@link AMQTypedValue}s. + */ + FIELD_TABLE('F') + { + /** + * Calculates the size of an instance of the type in bytes. + * + * @param value An instance of the type. + * + * @return The size of the instance of the type in bytes. + */ + public int getEncodingSize(Object value) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + FieldTable ftValue = (FieldTable) value; + + // Loop over all name/value pairs adding up size of each. FieldTable itself keeps track of its encoded + // size as entries are added, so no need to loop over all explicitly. + // EncodingUtils calculation of the encoded field table lenth, will include 4 bytes for its 'size' field. + return EncodingUtils.encodedFieldTableLength(ftValue); + } + + /** + * Converts an instance of the type to an equivalent Java native representation. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation. + */ + public Object toNativeValue(Object value) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + return (FieldTable) value; + } + + /** + * Writes an instance of the type to a specified byte buffer. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + public void writeValueImpl(Object value, ByteBuffer buffer) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + FieldTable ftValue = (FieldTable) value; + + // Loop over all name/values writing out into buffer. + ftValue.writeToBuffer(buffer); + } + + /** + * Reads an instance of the type from a specified byte buffer. + * + * @param buffer The byte buffer to write it to. + * + * @return An instance of the type. + */ + public Object readValueFromBuffer(ByteBuffer buffer) + { + try + { + // Read size of field table then all name/value pairs. + return EncodingUtils.readFieldTable(buffer); + } + catch (AMQFrameDecodingException e) + { + throw new IllegalArgumentException("Unable to read field table from buffer.", e); + } + } + }, + + VOID('V') + { + public int getEncodingSize(Object value) + { + return 0; + } + + public Object toNativeValue(Object value) + { + if (value == null) + { + return null; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to null String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return null; + } + }, + + BINARY('x') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongstrLength((byte[]) value); + } + + public Object toNativeValue(Object value) + { + if ((value instanceof byte[]) || (value == null)) + { + return value; + } + else + { + throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName() + + ") cannot be converted to byte[]"); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongstr(buffer, (byte[]) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongstr(buffer); + } + }, + + ASCII_STRING('c') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + WIDE_STRING('C') + { + public int getEncodingSize(Object value) + { + // FIXME: use proper charset encoder + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + BOOLEAN('t') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedBooleanLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Boolean) + { + return (Boolean) value; + } + else if ((value instanceof String) || (value == null)) + { + return Boolean.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to boolean."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeBoolean(buffer, (Boolean) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readBoolean(buffer); + } + }, + + ASCII_CHARACTER('k') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedCharLength(); + } + + public Character toNativeValue(Object value) + { + if (value instanceof Character) + { + return (Character) value; + } + else if (value == null) + { + throw new NullPointerException("Cannot convert null into char"); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to char."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeChar(buffer, (Character) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readChar(buffer); + } + }, + + BYTE('b') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedByteLength(); + } + + public Byte toNativeValue(Object value) + { + if (value instanceof Byte) + { + return (Byte) value; + } + else if ((value instanceof String) || (value == null)) + { + return Byte.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to byte."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeByte(buffer, (Byte) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readByte(buffer); + } + }, + + SHORT('s') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedShortLength(); + } + + public Short toNativeValue(Object value) + { + if (value instanceof Short) + { + return (Short) value; + } + else if (value instanceof Byte) + { + return ((Byte) value).shortValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Short.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to short."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeShort(buffer, (Short) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + }, + + INT('I') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedIntegerLength(); + } + + public Integer toNativeValue(Object value) + { + if (value instanceof Integer) + { + return (Integer) value; + } + else if (value instanceof Short) + { + return ((Short) value).intValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).intValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Integer.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeInteger(buffer, (Integer) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readInteger(buffer); + } + }, + + LONG('l') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + else if (value instanceof Short) + { + return ((Short) value).longValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Long.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to long."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLong(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLong(buffer); + } + }, + + FLOAT('f') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedFloatLength(); + } + + public Float toNativeValue(Object value) + { + if (value instanceof Float) + { + return (Float) value; + } + else if ((value instanceof String) || (value == null)) + { + return Float.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to float."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeFloat(buffer, (Float) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readFloat(buffer); + } + }, + + DOUBLE('d') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedDoubleLength(); + } + + public Double toNativeValue(Object value) + { + if (value instanceof Double) + { + return (Double) value; + } + else if (value instanceof Float) + { + return ((Float) value).doubleValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Double.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to double."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeDouble(buffer, (Double) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readDouble(buffer); + } + }; + + /** Holds the defined one byte identifier for the type. */ + private final byte _identifier; + + /** + * Creates an instance of an AMQP type from its defined one byte identifier. + * + * @param identifier The one byte identifier for the type. + */ + AMQType(char identifier) + { + _identifier = (byte) identifier; + } + + /** + * Extracts the byte identifier for the typ. + * + * @return The byte identifier for the typ. + */ + public final byte identifier() + { + return _identifier; + } + + /** + * Calculates the size of an instance of the type in bytes. + * + * @param value An instance of the type. + * + * @return The size of the instance of the type in bytes. + */ + public abstract int getEncodingSize(Object value); + + /** + * Converts an instance of the type to an equivalent Java native representation. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation. + */ + public abstract Object toNativeValue(Object value); + + /** + * Converts an instance of the type to an equivalent Java native representation, packaged as an + * {@link AMQTypedValue} tagged with its AMQP type. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation, tagged with its AMQP type. + */ + public AMQTypedValue asTypedValue(Object value) + { + return new AMQTypedValue(this, toNativeValue(value)); + } + + /** + * Writes an instance of the type to a specified byte buffer, preceded by its one byte identifier. As the type and + * value are both written, this provides a fully encoded description of a parameters type and value. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + public void writeToBuffer(Object value, ByteBuffer buffer) + { + buffer.put(identifier()); + writeValueImpl(value, buffer); + } + + /** + * Writes an instance of the type to a specified byte buffer. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + abstract void writeValueImpl(Object value, ByteBuffer buffer); + + /** + * Reads an instance of the type from a specified byte buffer. + * + * @param buffer The byte buffer to write it to. + * + * @return An instance of the type. + */ + abstract Object readValueFromBuffer(ByteBuffer buffer); +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java b/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java index 1419dd75b1..a16e137466 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java @@ -1,48 +1,48 @@ -/* - * - * 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.framing; - -import java.util.HashMap; -import java.util.Map; - -public class AMQTypeMap -{ - public static Map _reverseTypeMap = new HashMap(); - - static - { - for(AMQType type : AMQType.values()) - { - _reverseTypeMap.put(type.identifier(), type); - } - } - - public static AMQType getType(Byte identifier) - { - AMQType result = _reverseTypeMap.get(identifier); - if (result == null) { - throw new IllegalArgumentException - ("no such type code: " + Integer.toHexString(identifier.intValue())); - } - return result; - } - -} +/* + * + * 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.framing; + +import java.util.HashMap; +import java.util.Map; + +public class AMQTypeMap +{ + public static Map _reverseTypeMap = new HashMap(); + + static + { + for(AMQType type : AMQType.values()) + { + _reverseTypeMap.put(type.identifier(), type); + } + } + + public static AMQType getType(Byte identifier) + { + AMQType result = _reverseTypeMap.get(identifier); + if (result == null) { + throw new IllegalArgumentException + ("no such type code: " + Integer.toHexString(identifier.intValue())); + } + return result; + } + +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java index e5b1fad9a8..d6359baa0f 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java @@ -1,96 +1,96 @@ -/* - * - * 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.framing; - -import org.apache.mina.common.ByteBuffer; - -/** - * AMQTypedValue combines together a native Java Object value, and an {@link AMQType}, as a fully typed AMQP parameter - * value. It provides the ability to read and write fully typed parameters to and from byte buffers. It also provides - * the ability to create such parameters from Java native value and a type tag or to extract the native value and type - * from one. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Create a fully typed AMQP value from a native type and a type tag. {@link AMQType} - *
Create a fully typed AMQP value from a binary representation in a byte buffer. {@link AMQType} - *
Write a fully typed AMQP value to a binary representation in a byte buffer. {@link AMQType} - *
Extract the type from a fully typed AMQP value. - *
Extract the value from a fully typed AMQP value. - *
- */ -public class AMQTypedValue -{ - /** The type of the value. */ - private final AMQType _type; - - /** The Java native representation of the AMQP typed value. */ - private final Object _value; - - public AMQTypedValue(AMQType type, Object value) - { - if (type == null) - { - throw new NullPointerException("Cannot create a typed value with null type"); - } - - _type = type; - _value = type.toNativeValue(value); - } - - private AMQTypedValue(AMQType type, ByteBuffer buffer) - { - _type = type; - _value = type.readValueFromBuffer(buffer); - } - - public AMQType getType() - { - return _type; - } - - public Object getValue() - { - return _value; - } - - public void writeToBuffer(ByteBuffer buffer) - { - _type.writeToBuffer(_value, buffer); - } - - public int getEncodingSize() - { - return _type.getEncodingSize(_value); - } - - public static AMQTypedValue readFromBuffer(ByteBuffer buffer) - { - AMQType type = AMQTypeMap.getType(buffer.get()); - - return new AMQTypedValue(type, buffer); - } - - public String toString() - { - return "[" + getType() + ": " + getValue() + "]"; - } -} +/* + * + * 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.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * AMQTypedValue combines together a native Java Object value, and an {@link AMQType}, as a fully typed AMQP parameter + * value. It provides the ability to read and write fully typed parameters to and from byte buffers. It also provides + * the ability to create such parameters from Java native value and a type tag or to extract the native value and type + * from one. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Create a fully typed AMQP value from a native type and a type tag. {@link AMQType} + *
Create a fully typed AMQP value from a binary representation in a byte buffer. {@link AMQType} + *
Write a fully typed AMQP value to a binary representation in a byte buffer. {@link AMQType} + *
Extract the type from a fully typed AMQP value. + *
Extract the value from a fully typed AMQP value. + *
+ */ +public class AMQTypedValue +{ + /** The type of the value. */ + private final AMQType _type; + + /** The Java native representation of the AMQP typed value. */ + private final Object _value; + + public AMQTypedValue(AMQType type, Object value) + { + if (type == null) + { + throw new NullPointerException("Cannot create a typed value with null type"); + } + + _type = type; + _value = type.toNativeValue(value); + } + + private AMQTypedValue(AMQType type, ByteBuffer buffer) + { + _type = type; + _value = type.readValueFromBuffer(buffer); + } + + public AMQType getType() + { + return _type; + } + + public Object getValue() + { + return _value; + } + + public void writeToBuffer(ByteBuffer buffer) + { + _type.writeToBuffer(_value, buffer); + } + + public int getEncodingSize() + { + return _type.getEncodingSize(_value); + } + + public static AMQTypedValue readFromBuffer(ByteBuffer buffer) + { + AMQType type = AMQTypeMap.getType(buffer.get()); + + return new AMQTypedValue(type, buffer); + } + + public String toString() + { + return "[" + getType() + ": " + getValue() + "]"; + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java b/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java index 6a608a8bff..7162c37062 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java +++ b/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java @@ -1,81 +1,81 @@ -/* - * - * 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.framing; - -public interface CommonContentHeaderProperties extends ContentHeaderProperties -{ - AMQShortString getContentType(); - - void setContentType(AMQShortString contentType); - - FieldTable getHeaders(); - - void setHeaders(FieldTable headers); - - byte getDeliveryMode(); - - void setDeliveryMode(byte deliveryMode); - - byte getPriority(); - - void setPriority(byte priority); - - AMQShortString getCorrelationId(); - - void setCorrelationId(AMQShortString correlationId); - - AMQShortString getReplyTo(); - - void setReplyTo(AMQShortString replyTo); - - long getExpiration(); - - void setExpiration(long expiration); - - AMQShortString getMessageId(); - - void setMessageId(AMQShortString messageId); - - long getTimestamp(); - - void setTimestamp(long timestamp); - - AMQShortString getType(); - - void setType(AMQShortString type); - - AMQShortString getUserId(); - - void setUserId(AMQShortString userId); - - AMQShortString getAppId(); - - void setAppId(AMQShortString appId); - - AMQShortString getClusterId(); - - void setClusterId(AMQShortString clusterId); - - AMQShortString getEncoding(); - - void setEncoding(AMQShortString encoding); -} +/* + * + * 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.framing; + +public interface CommonContentHeaderProperties extends ContentHeaderProperties +{ + AMQShortString getContentType(); + + void setContentType(AMQShortString contentType); + + FieldTable getHeaders(); + + void setHeaders(FieldTable headers); + + byte getDeliveryMode(); + + void setDeliveryMode(byte deliveryMode); + + byte getPriority(); + + void setPriority(byte priority); + + AMQShortString getCorrelationId(); + + void setCorrelationId(AMQShortString correlationId); + + AMQShortString getReplyTo(); + + void setReplyTo(AMQShortString replyTo); + + long getExpiration(); + + void setExpiration(long expiration); + + AMQShortString getMessageId(); + + void setMessageId(AMQShortString messageId); + + long getTimestamp(); + + void setTimestamp(long timestamp); + + AMQShortString getType(); + + void setType(AMQShortString type); + + AMQShortString getUserId(); + + void setUserId(AMQShortString userId); + + AMQShortString getAppId(); + + void setAppId(AMQShortString appId); + + AMQShortString getClusterId(); + + void setClusterId(AMQShortString clusterId); + + AMQShortString getEncoding(); + + void setEncoding(AMQShortString encoding); +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java b/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java index f8cf3f3011..bd763599b0 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java +++ b/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java @@ -1,98 +1,98 @@ -/* - * - * 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.framing; - -import org.apache.mina.common.ByteBuffer; - -public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock -{ - private AMQDataBlock _firstFrame; - - private AMQDataBlock _block; - - public SmallCompositeAMQDataBlock(AMQDataBlock block) - { - _block = block; - } - - /** - * The encoded block will be logically first before the AMQDataBlocks which are encoded - * into the buffer afterwards. - * @param encodedBlock already-encoded data - * @param block a block to be encoded. - */ - public SmallCompositeAMQDataBlock(AMQDataBlock encodedBlock, AMQDataBlock block) - { - this(block); - _firstFrame = encodedBlock; - } - - public AMQDataBlock getBlock() - { - return _block; - } - - public AMQDataBlock getFirstFrame() - { - return _firstFrame; - } - - public long getSize() - { - long frameSize = _block.getSize(); - - if (_firstFrame != null) - { - - frameSize += _firstFrame.getSize(); - } - return frameSize; - } - - public void writePayload(ByteBuffer buffer) - { - if (_firstFrame != null) - { - _firstFrame.writePayload(buffer); - } - _block.writePayload(buffer); - - } - - public String toString() - { - if (_block == null) - { - return "No blocks contained in composite frame"; - } - else - { - StringBuilder buf = new StringBuilder(this.getClass().getName()); - buf.append("{encodedBlock=").append(_firstFrame); - - buf.append(" _block=[").append(_block.toString()).append("]"); - - buf.append("}"); - return buf.toString(); - } - } -} +/* + * + * 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.framing; + +import org.apache.mina.common.ByteBuffer; + +public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock +{ + private AMQDataBlock _firstFrame; + + private AMQDataBlock _block; + + public SmallCompositeAMQDataBlock(AMQDataBlock block) + { + _block = block; + } + + /** + * The encoded block will be logically first before the AMQDataBlocks which are encoded + * into the buffer afterwards. + * @param encodedBlock already-encoded data + * @param block a block to be encoded. + */ + public SmallCompositeAMQDataBlock(AMQDataBlock encodedBlock, AMQDataBlock block) + { + this(block); + _firstFrame = encodedBlock; + } + + public AMQDataBlock getBlock() + { + return _block; + } + + public AMQDataBlock getFirstFrame() + { + return _firstFrame; + } + + public long getSize() + { + long frameSize = _block.getSize(); + + if (_firstFrame != null) + { + + frameSize += _firstFrame.getSize(); + } + return frameSize; + } + + public void writePayload(ByteBuffer buffer) + { + if (_firstFrame != null) + { + _firstFrame.writePayload(buffer); + } + _block.writePayload(buffer); + + } + + public String toString() + { + if (_block == null) + { + return "No blocks contained in composite frame"; + } + else + { + StringBuilder buf = new StringBuilder(this.getClass().getName()); + buf.append("{encodedBlock=").append(_firstFrame); + + buf.append(" _block=[").append(_block.toString()).append("]"); + + buf.append("}"); + return buf.toString(); + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java b/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java index 516d0c569c..76c154581d 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java +++ b/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java @@ -1,198 +1,198 @@ -/* - * - * 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.framing; - -import org.apache.mina.common.ByteBuffer; - -import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class VersionSpecificRegistry -{ - private static final Logger _log = LoggerFactory.getLogger(VersionSpecificRegistry.class); - - private final byte _protocolMajorVersion; - private final byte _protocolMinorVersion; - - private static final int DEFAULT_MAX_CLASS_ID = 200; - private static final int DEFAULT_MAX_METHOD_ID = 50; - - private AMQMethodBodyInstanceFactory[][] _registry = new AMQMethodBodyInstanceFactory[DEFAULT_MAX_CLASS_ID][]; - - private ProtocolVersionMethodConverter _protocolVersionConverter; - - public VersionSpecificRegistry(byte major, byte minor) - { - _protocolMajorVersion = major; - _protocolMinorVersion = minor; - - _protocolVersionConverter = loadProtocolVersionConverters(major, minor); - } - - private static ProtocolVersionMethodConverter loadProtocolVersionConverters(byte protocolMajorVersion, - byte protocolMinorVersion) - { - try - { - Class versionMethodConverterClass = - (Class) Class.forName("org.apache.qpid.framing.MethodConverter_" - + protocolMajorVersion + "_" + protocolMinorVersion); - - return versionMethodConverterClass.newInstance(); - - } - catch (ClassNotFoundException e) - { - _log.warn("Could not find protocol conversion classes for " + protocolMajorVersion + "-" + protocolMinorVersion); - if (protocolMinorVersion != 0) - { - protocolMinorVersion--; - - return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); - } - else if (protocolMajorVersion != 0) - { - protocolMajorVersion--; - - return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); - } - else - { - return null; - } - - } - catch (IllegalAccessException e) - { - throw new IllegalStateException("Unable to load protocol version converter: ", e); - } - catch (InstantiationException e) - { - throw new IllegalStateException("Unable to load protocol version converter: ", e); - } - } - - public byte getProtocolMajorVersion() - { - return _protocolMajorVersion; - } - - public byte getProtocolMinorVersion() - { - return _protocolMinorVersion; - } - - public AMQMethodBodyInstanceFactory getMethodBody(final short classID, final short methodID) - { - try - { - return _registry[classID][methodID]; - } - catch (IndexOutOfBoundsException e) - { - return null; - } - catch (NullPointerException e) - { - return null; - } - } - - public void registerMethod(final short classID, final short methodID, final AMQMethodBodyInstanceFactory instanceFactory) - { - if (_registry.length <= classID) - { - AMQMethodBodyInstanceFactory[][] oldRegistry = _registry; - _registry = new AMQMethodBodyInstanceFactory[classID + 1][]; - System.arraycopy(oldRegistry, 0, _registry, 0, oldRegistry.length); - } - - if (_registry[classID] == null) - { - _registry[classID] = - new AMQMethodBodyInstanceFactory[(methodID > DEFAULT_MAX_METHOD_ID) ? (methodID + 1) - : (DEFAULT_MAX_METHOD_ID + 1)]; - } - else if (_registry[classID].length <= methodID) - { - AMQMethodBodyInstanceFactory[] oldMethods = _registry[classID]; - _registry[classID] = new AMQMethodBodyInstanceFactory[methodID + 1]; - System.arraycopy(oldMethods, 0, _registry[classID], 0, oldMethods.length); - } - - _registry[classID][methodID] = instanceFactory; - - } - - public AMQMethodBody get(short classID, short methodID, ByteBuffer in, long size) throws AMQFrameDecodingException - { - AMQMethodBodyInstanceFactory bodyFactory; - try - { - bodyFactory = _registry[classID][methodID]; - } - catch (NullPointerException e) - { - throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " - + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID - + " method " + methodID + ".", e); - } - catch (IndexOutOfBoundsException e) - { - if (classID >= _registry.length) - { - throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " - + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID - + " method " + methodID + ".", e); - - } - else - { - throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " - + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID - + " method " + methodID + ".", e); - - } - } - - if (bodyFactory == null) - { - throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " - + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID - + " method " + methodID + ".", null); - } - - return bodyFactory.newInstance( in, size); - - } - - public ProtocolVersionMethodConverter getProtocolVersionMethodConverter() - { - return _protocolVersionConverter; - } - - public void configure() - { - _protocolVersionConverter.configure(); - } -} +/* + * + * 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.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VersionSpecificRegistry +{ + private static final Logger _log = LoggerFactory.getLogger(VersionSpecificRegistry.class); + + private final byte _protocolMajorVersion; + private final byte _protocolMinorVersion; + + private static final int DEFAULT_MAX_CLASS_ID = 200; + private static final int DEFAULT_MAX_METHOD_ID = 50; + + private AMQMethodBodyInstanceFactory[][] _registry = new AMQMethodBodyInstanceFactory[DEFAULT_MAX_CLASS_ID][]; + + private ProtocolVersionMethodConverter _protocolVersionConverter; + + public VersionSpecificRegistry(byte major, byte minor) + { + _protocolMajorVersion = major; + _protocolMinorVersion = minor; + + _protocolVersionConverter = loadProtocolVersionConverters(major, minor); + } + + private static ProtocolVersionMethodConverter loadProtocolVersionConverters(byte protocolMajorVersion, + byte protocolMinorVersion) + { + try + { + Class versionMethodConverterClass = + (Class) Class.forName("org.apache.qpid.framing.MethodConverter_" + + protocolMajorVersion + "_" + protocolMinorVersion); + + return versionMethodConverterClass.newInstance(); + + } + catch (ClassNotFoundException e) + { + _log.warn("Could not find protocol conversion classes for " + protocolMajorVersion + "-" + protocolMinorVersion); + if (protocolMinorVersion != 0) + { + protocolMinorVersion--; + + return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); + } + else if (protocolMajorVersion != 0) + { + protocolMajorVersion--; + + return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); + } + else + { + return null; + } + + } + catch (IllegalAccessException e) + { + throw new IllegalStateException("Unable to load protocol version converter: ", e); + } + catch (InstantiationException e) + { + throw new IllegalStateException("Unable to load protocol version converter: ", e); + } + } + + public byte getProtocolMajorVersion() + { + return _protocolMajorVersion; + } + + public byte getProtocolMinorVersion() + { + return _protocolMinorVersion; + } + + public AMQMethodBodyInstanceFactory getMethodBody(final short classID, final short methodID) + { + try + { + return _registry[classID][methodID]; + } + catch (IndexOutOfBoundsException e) + { + return null; + } + catch (NullPointerException e) + { + return null; + } + } + + public void registerMethod(final short classID, final short methodID, final AMQMethodBodyInstanceFactory instanceFactory) + { + if (_registry.length <= classID) + { + AMQMethodBodyInstanceFactory[][] oldRegistry = _registry; + _registry = new AMQMethodBodyInstanceFactory[classID + 1][]; + System.arraycopy(oldRegistry, 0, _registry, 0, oldRegistry.length); + } + + if (_registry[classID] == null) + { + _registry[classID] = + new AMQMethodBodyInstanceFactory[(methodID > DEFAULT_MAX_METHOD_ID) ? (methodID + 1) + : (DEFAULT_MAX_METHOD_ID + 1)]; + } + else if (_registry[classID].length <= methodID) + { + AMQMethodBodyInstanceFactory[] oldMethods = _registry[classID]; + _registry[classID] = new AMQMethodBodyInstanceFactory[methodID + 1]; + System.arraycopy(oldMethods, 0, _registry[classID], 0, oldMethods.length); + } + + _registry[classID][methodID] = instanceFactory; + + } + + public AMQMethodBody get(short classID, short methodID, ByteBuffer in, long size) throws AMQFrameDecodingException + { + AMQMethodBodyInstanceFactory bodyFactory; + try + { + bodyFactory = _registry[classID][methodID]; + } + catch (NullPointerException e) + { + throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + } + catch (IndexOutOfBoundsException e) + { + if (classID >= _registry.length) + { + throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + + } + else + { + throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + + } + } + + if (bodyFactory == null) + { + throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", null); + } + + return bodyFactory.newInstance( in, size); + + } + + public ProtocolVersionMethodConverter getProtocolVersionMethodConverter() + { + return _protocolVersionConverter; + } + + public void configure() + { + _protocolVersionConverter.configure(); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java b/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java index 1c335f3036..1d7c05e9cc 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java +++ b/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java @@ -1,47 +1,47 @@ -/* - * - * 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.framing.abstraction; - -public abstract class AbstractMethodConverter implements ProtocolVersionMethodConverter -{ - private final byte _protocolMajorVersion; - - - private final byte _protocolMinorVersion; - - public AbstractMethodConverter(byte major, byte minor) - { - _protocolMajorVersion = major; - _protocolMinorVersion = minor; - } - - - public final byte getProtocolMajorVersion() - { - return _protocolMajorVersion; - } - - public final byte getProtocolMinorVersion() - { - return _protocolMinorVersion; - } -} +/* + * + * 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.framing.abstraction; + +public abstract class AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private final byte _protocolMajorVersion; + + + private final byte _protocolMinorVersion; + + public AbstractMethodConverter(byte major, byte minor) + { + _protocolMajorVersion = major; + _protocolMinorVersion = minor; + } + + + public final byte getProtocolMajorVersion() + { + return _protocolMajorVersion; + } + + public final byte getProtocolMinorVersion() + { + return _protocolMinorVersion; + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java b/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java index 6312e478a8..0695349f76 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java +++ b/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java @@ -1,32 +1,32 @@ -/* - * - * 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.framing.abstraction; - -import org.apache.mina.common.ByteBuffer; - -public interface ContentChunk -{ - int getSize(); - ByteBuffer getData(); - - void reduceToFit(); -} +/* + * + * 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.framing.abstraction; + +import org.apache.mina.common.ByteBuffer; + +public interface ContentChunk +{ + int getSize(); + ByteBuffer getData(); + + void reduceToFit(); +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java b/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java index 49c28bb06b..a96bdcc171 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java +++ b/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java @@ -1,38 +1,38 @@ -/* - * - * 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.framing.abstraction; - -import org.apache.qpid.framing.AMQShortString; - -public interface MessagePublishInfo -{ - - public AMQShortString getExchange(); - - public void setExchange(AMQShortString exchange); - - public boolean isImmediate(); - - public boolean isMandatory(); - - public AMQShortString getRoutingKey(); - -} +/* + * + * 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.framing.abstraction; + +import org.apache.qpid.framing.AMQShortString; + +public interface MessagePublishInfo +{ + + public AMQShortString getExchange(); + + public void setExchange(AMQShortString exchange); + + public boolean isImmediate(); + + public boolean isMandatory(); + + public AMQShortString getRoutingKey(); + +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java b/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java index 42e2f7ad97..01d1a8a17b 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java +++ b/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java @@ -1,32 +1,32 @@ -/* - * - * 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.framing.abstraction; - -import org.apache.qpid.framing.AMQMethodBody; - - -public interface MessagePublishInfoConverter -{ - public MessagePublishInfo convertToInfo(AMQMethodBody body); - public AMQMethodBody convertToBody(MessagePublishInfo info); - -} +/* + * + * 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.framing.abstraction; + +import org.apache.qpid.framing.AMQMethodBody; + + +public interface MessagePublishInfoConverter +{ + public MessagePublishInfo convertToInfo(AMQMethodBody body); + public AMQMethodBody convertToBody(MessagePublishInfo info); + +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java b/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java index 99588a0908..0a1cedc4e6 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java +++ b/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java @@ -1,32 +1,32 @@ -/* - * - * 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.framing.abstraction; - -import org.apache.qpid.framing.AMQBody; - -public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter -{ - AMQBody convertToBody(ContentChunk contentBody); - ContentChunk convertToContentChunk(AMQBody body); - - void configure(); -} +/* + * + * 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.framing.abstraction; + +import org.apache.qpid.framing.AMQBody; + +public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter +{ + AMQBody convertToBody(ContentChunk contentBody); + ContentChunk convertToContentChunk(AMQBody body); + + void configure(); +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java index 3c5cb74773..948f5baaf6 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java @@ -1,209 +1,209 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.framing.amqp_0_9; - -import org.apache.qpid.framing.EncodingUtils; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.AMQFrameDecodingException; -import org.apache.qpid.framing.Content; - -import org.apache.mina.common.ByteBuffer; - -public abstract class AMQMethodBody_0_9 extends org.apache.qpid.framing.AMQMethodBodyImpl -{ - - public byte getMajor() - { - return 0; - } - - public byte getMinor() - { - return 9; - } - - public int getSize() - { - return 2 + 2 + getBodySize(); - } - - public void writePayload(ByteBuffer buffer) - { - EncodingUtils.writeUnsignedShort(buffer, getClazz()); - EncodingUtils.writeUnsignedShort(buffer, getMethod()); - writeMethodPayload(buffer); - } - - - protected byte readByte(ByteBuffer buffer) - { - return buffer.get(); - } - - protected AMQShortString readAMQShortString(ByteBuffer buffer) - { - return EncodingUtils.readAMQShortString(buffer); - } - - protected int getSizeOf(AMQShortString string) - { - return EncodingUtils.encodedShortStringLength(string); - } - - protected void writeByte(ByteBuffer buffer, byte b) - { - buffer.put(b); - } - - protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) - { - EncodingUtils.writeShortStringBytes(buffer, string); - } - - protected int readInt(ByteBuffer buffer) - { - return buffer.getInt(); - } - - protected void writeInt(ByteBuffer buffer, int i) - { - buffer.putInt(i); - } - - protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException - { - return EncodingUtils.readFieldTable(buffer); - } - - protected int getSizeOf(FieldTable table) - { - return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeFieldTable(ByteBuffer buffer, FieldTable table) - { - EncodingUtils.writeFieldTableBytes(buffer, table); - } - - protected long readLong(ByteBuffer buffer) - { - return buffer.getLong(); - } - - protected void writeLong(ByteBuffer buffer, long l) - { - buffer.putLong(l); - } - - protected int getSizeOf(byte[] response) - { - return (response == null) ? 4 :response.length + 4; - } - - protected void writeBytes(ByteBuffer buffer, byte[] data) - { - EncodingUtils.writeBytes(buffer,data); - } - - protected byte[] readBytes(ByteBuffer buffer) - { - return EncodingUtils.readBytes(buffer); - } - - protected short readShort(ByteBuffer buffer) - { - return EncodingUtils.readShort(buffer); - } - - protected void writeShort(ByteBuffer buffer, short s) - { - EncodingUtils.writeShort(buffer, s); - } - - protected Content readContent(ByteBuffer buffer) - { - return null; //To change body of created methods use File | Settings | File Templates. - } - - protected int getSizeOf(Content body) - { - return 0; //To change body of created methods use File | Settings | File Templates. - } - - protected void writeContent(ByteBuffer buffer, Content body) - { - //To change body of created methods use File | Settings | File Templates. - } - - protected byte readBitfield(ByteBuffer buffer) - { - return readByte(buffer); //To change body of created methods use File | Settings | File Templates. - } - - protected int readUnsignedShort(ByteBuffer buffer) - { - return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeBitfield(ByteBuffer buffer, byte bitfield0) - { - buffer.put(bitfield0); - } - - protected void writeUnsignedShort(ByteBuffer buffer, int s) - { - EncodingUtils.writeUnsignedShort(buffer, s); - } - - protected long readUnsignedInteger(ByteBuffer buffer) - { - return buffer.getUnsignedInt(); - } - protected void writeUnsignedInteger(ByteBuffer buffer, long i) - { - EncodingUtils.writeUnsignedInteger(buffer, i); - } - - - protected short readUnsignedByte(ByteBuffer buffer) - { - return buffer.getUnsigned(); - } - - protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) - { - EncodingUtils.writeUnsignedByte(buffer, unsignedByte); - } - - protected long readTimestamp(ByteBuffer buffer) - { - return EncodingUtils.readTimestamp(buffer); - } - - protected void writeTimestamp(ByteBuffer buffer, long t) - { - EncodingUtils.writeTimestamp(buffer, t); - } - - -} +/* + * + * 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.framing.amqp_0_9; + +import org.apache.qpid.framing.EncodingUtils; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.Content; + +import org.apache.mina.common.ByteBuffer; + +public abstract class AMQMethodBody_0_9 extends org.apache.qpid.framing.AMQMethodBodyImpl +{ + + public byte getMajor() + { + return 0; + } + + public byte getMinor() + { + return 9; + } + + public int getSize() + { + return 2 + 2 + getBodySize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + + protected byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + protected AMQShortString readAMQShortString(ByteBuffer buffer) + { + return EncodingUtils.readAMQShortString(buffer); + } + + protected int getSizeOf(AMQShortString string) + { + return EncodingUtils.encodedShortStringLength(string); + } + + protected void writeByte(ByteBuffer buffer, byte b) + { + buffer.put(b); + } + + protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) + { + EncodingUtils.writeShortStringBytes(buffer, string); + } + + protected int readInt(ByteBuffer buffer) + { + return buffer.getInt(); + } + + protected void writeInt(ByteBuffer buffer, int i) + { + buffer.putInt(i); + } + + protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + return EncodingUtils.readFieldTable(buffer); + } + + protected int getSizeOf(FieldTable table) + { + return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeFieldTable(ByteBuffer buffer, FieldTable table) + { + EncodingUtils.writeFieldTableBytes(buffer, table); + } + + protected long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + protected void writeLong(ByteBuffer buffer, long l) + { + buffer.putLong(l); + } + + protected int getSizeOf(byte[] response) + { + return (response == null) ? 4 :response.length + 4; + } + + protected void writeBytes(ByteBuffer buffer, byte[] data) + { + EncodingUtils.writeBytes(buffer,data); + } + + protected byte[] readBytes(ByteBuffer buffer) + { + return EncodingUtils.readBytes(buffer); + } + + protected short readShort(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + + protected void writeShort(ByteBuffer buffer, short s) + { + EncodingUtils.writeShort(buffer, s); + } + + protected Content readContent(ByteBuffer buffer) + { + return null; //To change body of created methods use File | Settings | File Templates. + } + + protected int getSizeOf(Content body) + { + return 0; //To change body of created methods use File | Settings | File Templates. + } + + protected void writeContent(ByteBuffer buffer, Content body) + { + //To change body of created methods use File | Settings | File Templates. + } + + protected byte readBitfield(ByteBuffer buffer) + { + return readByte(buffer); //To change body of created methods use File | Settings | File Templates. + } + + protected int readUnsignedShort(ByteBuffer buffer) + { + return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeBitfield(ByteBuffer buffer, byte bitfield0) + { + buffer.put(bitfield0); + } + + protected void writeUnsignedShort(ByteBuffer buffer, int s) + { + EncodingUtils.writeUnsignedShort(buffer, s); + } + + protected long readUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + protected void writeUnsignedInteger(ByteBuffer buffer, long i) + { + EncodingUtils.writeUnsignedInteger(buffer, i); + } + + + protected short readUnsignedByte(ByteBuffer buffer) + { + return buffer.getUnsigned(); + } + + protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) + { + EncodingUtils.writeUnsignedByte(buffer, unsignedByte); + } + + protected long readTimestamp(ByteBuffer buffer) + { + return EncodingUtils.readTimestamp(buffer); + } + + protected void writeTimestamp(ByteBuffer buffer, long t) + { + EncodingUtils.writeTimestamp(buffer, t); + } + + +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java index 2fd4f70138..fd5195eb6b 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java @@ -1,172 +1,172 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.framing.amqp_0_9; - -import org.apache.mina.common.ByteBuffer; - -import org.apache.qpid.framing.abstraction.AbstractMethodConverter; -import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.amqp_0_9.*; -import org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl; - -public class MethodConverter_0_9 extends AbstractMethodConverter implements ProtocolVersionMethodConverter -{ - private int _basicPublishClassId; - private int _basicPublishMethodId; - - public MethodConverter_0_9() - { - super((byte)0,(byte)9); - - - } - - public AMQBody convertToBody(ContentChunk contentChunk) - { - if(contentChunk instanceof ContentChunk_0_9) - { - return ((ContentChunk_0_9)contentChunk).toBody(); - } - else - { - return new ContentBody(contentChunk.getData()); - } - } - - public ContentChunk convertToContentChunk(AMQBody body) - { - final ContentBody contentBodyChunk = (ContentBody) body; - - return new ContentChunk_0_9(contentBodyChunk); - - } - - public void configure() - { - - _basicPublishClassId = org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl.CLASS_ID; - _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; - - } - - public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) - { - final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); - - final AMQShortString exchange = publishBody.getExchange(); - final AMQShortString routingKey = publishBody.getRoutingKey(); - - return new MethodConverter_0_9.MessagePublishInfoImpl(exchange, - publishBody.getImmediate(), - publishBody.getMandatory(), - routingKey); - - } - - public AMQMethodBody convertToBody(MessagePublishInfo info) - { - - return new BasicPublishBodyImpl(0, - info.getExchange(), - info.getRoutingKey(), - info.isMandatory(), - info.isImmediate()) ; - - } - - private static class MessagePublishInfoImpl implements MessagePublishInfo - { - private AMQShortString _exchange; - private final boolean _immediate; - private final boolean _mandatory; - private final AMQShortString _routingKey; - - public MessagePublishInfoImpl(final AMQShortString exchange, - final boolean immediate, - final boolean mandatory, - final AMQShortString routingKey) - { - _exchange = exchange; - _immediate = immediate; - _mandatory = mandatory; - _routingKey = routingKey; - } - - public AMQShortString getExchange() - { - return _exchange; - } - - public void setExchange(AMQShortString exchange) - { - _exchange = exchange; - } - - public boolean isImmediate() - { - return _immediate; - } - - public boolean isMandatory() - { - return _mandatory; - } - - public AMQShortString getRoutingKey() - { - return _routingKey; - } - } - - private static class ContentChunk_0_9 implements ContentChunk - { - private final ContentBody _contentBodyChunk; - - public ContentChunk_0_9(final ContentBody contentBodyChunk) - { - _contentBodyChunk = contentBodyChunk; - } - - public int getSize() - { - return _contentBodyChunk.getSize(); - } - - public ByteBuffer getData() - { - return _contentBodyChunk.payload; - } - - public void reduceToFit() - { - _contentBodyChunk.reduceBufferToFit(); - } - - public AMQBody toBody() - { - return _contentBodyChunk; - } - } -} +/* + * + * 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.framing.amqp_0_9; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.framing.abstraction.AbstractMethodConverter; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.*; +import org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl; + +public class MethodConverter_0_9 extends AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private int _basicPublishClassId; + private int _basicPublishMethodId; + + public MethodConverter_0_9() + { + super((byte)0,(byte)9); + + + } + + public AMQBody convertToBody(ContentChunk contentChunk) + { + if(contentChunk instanceof ContentChunk_0_9) + { + return ((ContentChunk_0_9)contentChunk).toBody(); + } + else + { + return new ContentBody(contentChunk.getData()); + } + } + + public ContentChunk convertToContentChunk(AMQBody body) + { + final ContentBody contentBodyChunk = (ContentBody) body; + + return new ContentChunk_0_9(contentBodyChunk); + + } + + public void configure() + { + + _basicPublishClassId = org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl.CLASS_ID; + _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; + + } + + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) + { + final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); + + final AMQShortString exchange = publishBody.getExchange(); + final AMQShortString routingKey = publishBody.getRoutingKey(); + + return new MethodConverter_0_9.MessagePublishInfoImpl(exchange, + publishBody.getImmediate(), + publishBody.getMandatory(), + routingKey); + + } + + public AMQMethodBody convertToBody(MessagePublishInfo info) + { + + return new BasicPublishBodyImpl(0, + info.getExchange(), + info.getRoutingKey(), + info.isMandatory(), + info.isImmediate()) ; + + } + + private static class MessagePublishInfoImpl implements MessagePublishInfo + { + private AMQShortString _exchange; + private final boolean _immediate; + private final boolean _mandatory; + private final AMQShortString _routingKey; + + public MessagePublishInfoImpl(final AMQShortString exchange, + final boolean immediate, + final boolean mandatory, + final AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange; + } + + public void setExchange(AMQShortString exchange) + { + _exchange = exchange; + } + + public boolean isImmediate() + { + return _immediate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + } + + private static class ContentChunk_0_9 implements ContentChunk + { + private final ContentBody _contentBodyChunk; + + public ContentChunk_0_9(final ContentBody contentBodyChunk) + { + _contentBodyChunk = contentBodyChunk; + } + + public int getSize() + { + return _contentBodyChunk.getSize(); + } + + public ByteBuffer getData() + { + return _contentBodyChunk.payload; + } + + public void reduceToFit() + { + _contentBodyChunk.reduceBufferToFit(); + } + + public AMQBody toBody() + { + return _contentBodyChunk; + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java index 2b7c9534a9..e9b4447140 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java @@ -1,209 +1,209 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.framing.amqp_8_0; - -import org.apache.qpid.framing.EncodingUtils; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.AMQFrameDecodingException; -import org.apache.qpid.framing.Content; - -import org.apache.mina.common.ByteBuffer; - -public abstract class AMQMethodBody_8_0 extends org.apache.qpid.framing.AMQMethodBodyImpl -{ - - public byte getMajor() - { - return 8; - } - - public byte getMinor() - { - return 0; - } - - public int getSize() - { - return 2 + 2 + getBodySize(); - } - - public void writePayload(ByteBuffer buffer) - { - EncodingUtils.writeUnsignedShort(buffer, getClazz()); - EncodingUtils.writeUnsignedShort(buffer, getMethod()); - writeMethodPayload(buffer); - } - - - protected byte readByte(ByteBuffer buffer) - { - return buffer.get(); - } - - protected AMQShortString readAMQShortString(ByteBuffer buffer) - { - return EncodingUtils.readAMQShortString(buffer); - } - - protected int getSizeOf(AMQShortString string) - { - return EncodingUtils.encodedShortStringLength(string); - } - - protected void writeByte(ByteBuffer buffer, byte b) - { - buffer.put(b); - } - - protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) - { - EncodingUtils.writeShortStringBytes(buffer, string); - } - - protected int readInt(ByteBuffer buffer) - { - return buffer.getInt(); - } - - protected void writeInt(ByteBuffer buffer, int i) - { - buffer.putInt(i); - } - - protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException - { - return EncodingUtils.readFieldTable(buffer); - } - - protected int getSizeOf(FieldTable table) - { - return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeFieldTable(ByteBuffer buffer, FieldTable table) - { - EncodingUtils.writeFieldTableBytes(buffer, table); - } - - protected long readLong(ByteBuffer buffer) - { - return buffer.getLong(); - } - - protected void writeLong(ByteBuffer buffer, long l) - { - buffer.putLong(l); - } - - protected int getSizeOf(byte[] response) - { - return (response == null) ? 4 : response.length + 4; - } - - protected void writeBytes(ByteBuffer buffer, byte[] data) - { - EncodingUtils.writeBytes(buffer,data); - } - - protected byte[] readBytes(ByteBuffer buffer) - { - return EncodingUtils.readBytes(buffer); - } - - protected short readShort(ByteBuffer buffer) - { - return EncodingUtils.readShort(buffer); - } - - protected void writeShort(ByteBuffer buffer, short s) - { - EncodingUtils.writeShort(buffer, s); - } - - protected Content readContent(ByteBuffer buffer) - { - return null; //To change body of created methods use File | Settings | File Templates. - } - - protected int getSizeOf(Content body) - { - return 0; //To change body of created methods use File | Settings | File Templates. - } - - protected void writeContent(ByteBuffer buffer, Content body) - { - //To change body of created methods use File | Settings | File Templates. - } - - protected byte readBitfield(ByteBuffer buffer) - { - return readByte(buffer); //To change body of created methods use File | Settings | File Templates. - } - - protected int readUnsignedShort(ByteBuffer buffer) - { - return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. - } - - protected void writeBitfield(ByteBuffer buffer, byte bitfield0) - { - buffer.put(bitfield0); - } - - protected void writeUnsignedShort(ByteBuffer buffer, int s) - { - EncodingUtils.writeUnsignedShort(buffer, s); - } - - protected long readUnsignedInteger(ByteBuffer buffer) - { - return buffer.getUnsignedInt(); - } - protected void writeUnsignedInteger(ByteBuffer buffer, long i) - { - EncodingUtils.writeUnsignedInteger(buffer, i); - } - - - protected short readUnsignedByte(ByteBuffer buffer) - { - return buffer.getUnsigned(); - } - - protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) - { - EncodingUtils.writeUnsignedByte(buffer, unsignedByte); - } - - protected long readTimestamp(ByteBuffer buffer) - { - return EncodingUtils.readTimestamp(buffer); - } - - protected void writeTimestamp(ByteBuffer buffer, long t) - { - EncodingUtils.writeTimestamp(buffer, t); - } - - -} +/* + * + * 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.framing.amqp_8_0; + +import org.apache.qpid.framing.EncodingUtils; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.Content; + +import org.apache.mina.common.ByteBuffer; + +public abstract class AMQMethodBody_8_0 extends org.apache.qpid.framing.AMQMethodBodyImpl +{ + + public byte getMajor() + { + return 8; + } + + public byte getMinor() + { + return 0; + } + + public int getSize() + { + return 2 + 2 + getBodySize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + + protected byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + protected AMQShortString readAMQShortString(ByteBuffer buffer) + { + return EncodingUtils.readAMQShortString(buffer); + } + + protected int getSizeOf(AMQShortString string) + { + return EncodingUtils.encodedShortStringLength(string); + } + + protected void writeByte(ByteBuffer buffer, byte b) + { + buffer.put(b); + } + + protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) + { + EncodingUtils.writeShortStringBytes(buffer, string); + } + + protected int readInt(ByteBuffer buffer) + { + return buffer.getInt(); + } + + protected void writeInt(ByteBuffer buffer, int i) + { + buffer.putInt(i); + } + + protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + return EncodingUtils.readFieldTable(buffer); + } + + protected int getSizeOf(FieldTable table) + { + return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeFieldTable(ByteBuffer buffer, FieldTable table) + { + EncodingUtils.writeFieldTableBytes(buffer, table); + } + + protected long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + protected void writeLong(ByteBuffer buffer, long l) + { + buffer.putLong(l); + } + + protected int getSizeOf(byte[] response) + { + return (response == null) ? 4 : response.length + 4; + } + + protected void writeBytes(ByteBuffer buffer, byte[] data) + { + EncodingUtils.writeBytes(buffer,data); + } + + protected byte[] readBytes(ByteBuffer buffer) + { + return EncodingUtils.readBytes(buffer); + } + + protected short readShort(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + + protected void writeShort(ByteBuffer buffer, short s) + { + EncodingUtils.writeShort(buffer, s); + } + + protected Content readContent(ByteBuffer buffer) + { + return null; //To change body of created methods use File | Settings | File Templates. + } + + protected int getSizeOf(Content body) + { + return 0; //To change body of created methods use File | Settings | File Templates. + } + + protected void writeContent(ByteBuffer buffer, Content body) + { + //To change body of created methods use File | Settings | File Templates. + } + + protected byte readBitfield(ByteBuffer buffer) + { + return readByte(buffer); //To change body of created methods use File | Settings | File Templates. + } + + protected int readUnsignedShort(ByteBuffer buffer) + { + return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeBitfield(ByteBuffer buffer, byte bitfield0) + { + buffer.put(bitfield0); + } + + protected void writeUnsignedShort(ByteBuffer buffer, int s) + { + EncodingUtils.writeUnsignedShort(buffer, s); + } + + protected long readUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + protected void writeUnsignedInteger(ByteBuffer buffer, long i) + { + EncodingUtils.writeUnsignedInteger(buffer, i); + } + + + protected short readUnsignedByte(ByteBuffer buffer) + { + return buffer.getUnsigned(); + } + + protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) + { + EncodingUtils.writeUnsignedByte(buffer, unsignedByte); + } + + protected long readTimestamp(ByteBuffer buffer) + { + return EncodingUtils.readTimestamp(buffer); + } + + protected void writeTimestamp(ByteBuffer buffer, long t) + { + EncodingUtils.writeTimestamp(buffer, t); + } + + +} diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java index b1be49a350..299c655698 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java +++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java @@ -1,151 +1,151 @@ -/* - * - * 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.framing.amqp_8_0; - -import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.framing.abstraction.AbstractMethodConverter; -import org.apache.qpid.framing.amqp_8_0.BasicPublishBodyImpl; -import org.apache.qpid.framing.*; - -import org.apache.mina.common.ByteBuffer; - -public class MethodConverter_8_0 extends AbstractMethodConverter implements ProtocolVersionMethodConverter -{ - private int _basicPublishClassId; - private int _basicPublishMethodId; - - public MethodConverter_8_0() - { - super((byte)8,(byte)0); - - - } - - public AMQBody convertToBody(ContentChunk contentChunk) - { - return new ContentBody(contentChunk.getData()); - } - - public ContentChunk convertToContentChunk(AMQBody body) - { - final ContentBody contentBodyChunk = (ContentBody) body; - - return new ContentChunk() - { - - public int getSize() - { - return contentBodyChunk.getSize(); - } - - public ByteBuffer getData() - { - return contentBodyChunk.payload; - } - - public void reduceToFit() - { - contentBodyChunk.reduceBufferToFit(); - } - }; - - } - - public void configure() - { - - _basicPublishClassId = BasicPublishBodyImpl.CLASS_ID; - _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; - - } - - public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) - { - final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); - - final AMQShortString exchange = publishBody.getExchange(); - final AMQShortString routingKey = publishBody.getRoutingKey(); - - return new MessagePublishInfoImpl(exchange == null ? null : exchange.intern(), - publishBody.getImmediate(), - publishBody.getMandatory(), - routingKey == null ? null : routingKey.intern()); - - } - - public AMQMethodBody convertToBody(MessagePublishInfo info) - { - - return new BasicPublishBodyImpl(0, - info.getExchange(), - info.getRoutingKey(), - info.isMandatory(), - info.isImmediate()) ; - - } - - private static class MessagePublishInfoImpl implements MessagePublishInfo - { - private AMQShortString _exchange; - private final boolean _immediate; - private final boolean _mandatory; - private final AMQShortString _routingKey; - - public MessagePublishInfoImpl(final AMQShortString exchange, - final boolean immediate, - final boolean mandatory, - final AMQShortString routingKey) - { - _exchange = exchange; - _immediate = immediate; - _mandatory = mandatory; - _routingKey = routingKey; - } - - public AMQShortString getExchange() - { - return _exchange; - } - - public void setExchange(AMQShortString exchange) - { - _exchange = exchange; - } - - public boolean isImmediate() - { - return _immediate; - } - - public boolean isMandatory() - { - return _mandatory; - } - - public AMQShortString getRoutingKey() - { - return _routingKey; - } - } -} +/* + * + * 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.framing.amqp_8_0; + +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.AbstractMethodConverter; +import org.apache.qpid.framing.amqp_8_0.BasicPublishBodyImpl; +import org.apache.qpid.framing.*; + +import org.apache.mina.common.ByteBuffer; + +public class MethodConverter_8_0 extends AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private int _basicPublishClassId; + private int _basicPublishMethodId; + + public MethodConverter_8_0() + { + super((byte)8,(byte)0); + + + } + + public AMQBody convertToBody(ContentChunk contentChunk) + { + return new ContentBody(contentChunk.getData()); + } + + public ContentChunk convertToContentChunk(AMQBody body) + { + final ContentBody contentBodyChunk = (ContentBody) body; + + return new ContentChunk() + { + + public int getSize() + { + return contentBodyChunk.getSize(); + } + + public ByteBuffer getData() + { + return contentBodyChunk.payload; + } + + public void reduceToFit() + { + contentBodyChunk.reduceBufferToFit(); + } + }; + + } + + public void configure() + { + + _basicPublishClassId = BasicPublishBodyImpl.CLASS_ID; + _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; + + } + + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) + { + final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); + + final AMQShortString exchange = publishBody.getExchange(); + final AMQShortString routingKey = publishBody.getRoutingKey(); + + return new MessagePublishInfoImpl(exchange == null ? null : exchange.intern(), + publishBody.getImmediate(), + publishBody.getMandatory(), + routingKey == null ? null : routingKey.intern()); + + } + + public AMQMethodBody convertToBody(MessagePublishInfo info) + { + + return new BasicPublishBodyImpl(0, + info.getExchange(), + info.getRoutingKey(), + info.isMandatory(), + info.isImmediate()) ; + + } + + private static class MessagePublishInfoImpl implements MessagePublishInfo + { + private AMQShortString _exchange; + private final boolean _immediate; + private final boolean _mandatory; + private final AMQShortString _routingKey; + + public MessagePublishInfoImpl(final AMQShortString exchange, + final boolean immediate, + final boolean mandatory, + final AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange; + } + + public void setExchange(AMQShortString exchange) + { + _exchange = exchange; + } + + public boolean isImmediate() + { + return _immediate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java b/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java index b56a05f725..59003225b7 100644 --- a/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java +++ b/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java @@ -1,57 +1,57 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.protocol; - -import org.apache.qpid.framing.*; -import org.apache.qpid.AMQException; - -/** - * AMQVersionAwareProtocolSession is implemented by all AMQP session classes, that need to provide an awareness to - * callers of the version of the AMQP protocol that they are able to work with. - * - *

- *
CRC Card
Responsibilities - *
Provide the method registry for a specific version of the AMQP. - *
- * - * @todo Why is this a seperate interface to {@link ProtocolVersionAware}, could they be combined into a single - * interface and one of them eliminated? Move getRegistry method to ProtocolVersionAware, make the sessions - * implement AMQProtocolWriter directly and drop this interface. - */ -public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, ProtocolVersionAware -{ - /** - * Gets the method registry for a specific version of the AMQP. - * - * @return The method registry for a specific version of the AMQP. - */ -// public VersionSpecificRegistry getRegistry(); - - MethodRegistry getMethodRegistry(); - - - public void methodFrameReceived(int channelId, AMQMethodBody body) throws AMQException; - public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException; - public void contentBodyReceived(int channelId, ContentBody body) throws AMQException; - public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException; - - -} +/* + * + * 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.protocol; + +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; + +/** + * AMQVersionAwareProtocolSession is implemented by all AMQP session classes, that need to provide an awareness to + * callers of the version of the AMQP protocol that they are able to work with. + * + *

+ *
CRC Card
Responsibilities + *
Provide the method registry for a specific version of the AMQP. + *
+ * + * @todo Why is this a seperate interface to {@link ProtocolVersionAware}, could they be combined into a single + * interface and one of them eliminated? Move getRegistry method to ProtocolVersionAware, make the sessions + * implement AMQProtocolWriter directly and drop this interface. + */ +public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, ProtocolVersionAware +{ + /** + * Gets the method registry for a specific version of the AMQP. + * + * @return The method registry for a specific version of the AMQP. + */ +// public VersionSpecificRegistry getRegistry(); + + MethodRegistry getMethodRegistry(); + + + public void methodFrameReceived(int channelId, AMQMethodBody body) throws AMQException; + public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException; + public void contentBodyReceived(int channelId, ContentBody body) throws AMQException; + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException; + + +} diff --git a/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java b/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java index dea80cdcf4..56f950dd85 100644 --- a/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java +++ b/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java @@ -1,53 +1,53 @@ -/* - * - * 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.protocol; - -import org.apache.qpid.framing.ProtocolVersion; - -/** - * ProtocolVersionAware is implemented by all AMQP handling classes, that need to provide an awareness to callers of - * the version of the AMQP protocol that they are able to handle. - * - *

- *
CRC Card
Responsibilities - *
Report the major and minor AMQP version handled. - *
- */ -public interface ProtocolVersionAware -{ - /** - * @deprecated - * Reports the AMQP minor version, that the implementer can handle. - * - * @return The AMQP minor version. - */ - public byte getProtocolMinorVersion(); - - /** - * @deprecated - * Reports the AMQP major version, that the implementer can handle. - * - * @return The AMQP major version. - */ - public byte getProtocolMajorVersion(); - - public ProtocolVersion getProtocolVersion(); -} +/* + * + * 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.protocol; + +import org.apache.qpid.framing.ProtocolVersion; + +/** + * ProtocolVersionAware is implemented by all AMQP handling classes, that need to provide an awareness to callers of + * the version of the AMQP protocol that they are able to handle. + * + *

+ *
CRC Card
Responsibilities + *
Report the major and minor AMQP version handled. + *
+ */ +public interface ProtocolVersionAware +{ + /** + * @deprecated + * Reports the AMQP minor version, that the implementer can handle. + * + * @return The AMQP minor version. + */ + public byte getProtocolMinorVersion(); + + /** + * @deprecated + * Reports the AMQP major version, that the implementer can handle. + * + * @return The AMQP major version. + */ + public byte getProtocolMajorVersion(); + + public ProtocolVersion getProtocolVersion(); +} diff --git a/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java b/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java index dc73bce28f..baa68f87f9 100644 --- a/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java +++ b/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java @@ -1,689 +1,689 @@ -/* - * - * 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.util; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.regex.*; - -/** - * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure - * that they fit their specified format. A command line is made up of flags and options, both may be refered to as - * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not - * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so - * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify - * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified. - * - *

Some example command lines are: - * - *

    - *
  • This one has two options that expect arguments: - *
    - * cruisecontrol -configfile cruisecontrol.xml -port 9000
    - * 
    - *
  • This has one no-arg flag and two 'free' arguments: - *
    - * zip -r project.zip project/*
    - * 
    - *
  • This one concatenates multiple flags into a single block with only one '-': - *
    - * jar -tvf mytar.tar
    - * 
    - * - *

    The parsing rules are: - * - *

      - *
    1. Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter - * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own. - *
    2. Options expecting arguments must always be on their own. - *
    3. The argument to an option may be seperated from it by whitespace or appended directly onto the option. - *
    4. The argument to an option may never begin with a '-' character. - *
    5. All other arguments not beginning with a '-' character are free arguments that do not belong to any option. - *
    6. The second or later of a set of duplicate or repeated flags are ignored. - *
    7. Options are matched up to the shortest matching option. This is because of the possibility of having no space - * between an option and its argument. This rules out the possibility of using two options where one is an opening - * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because - * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with - * the "bar" argument. - *
    - * - *

    By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed - * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method. - * - *

    - *
    CRC Card
    Responsibilities Collaborations - *
    Accept a command line specification. - *
    Parse a command line into properties, validating it against its specification. - *
    Report all errors between a command line and its specification. - *
    Provide a formatted usage string for a command line. - *
    Provide a formatted options in force string for a command line. - *
    Allow errors on unknowns behaviour to be turned on or off. - *
    - */ -public class CommandLineParser -{ - /** Holds a mapping from command line option names to detailed information about those options. */ - private Map optionMap = new HashMap(); - - /** Holds a list of parsing errors. */ - private List parsingErrors = new ArrayList(); - - /** Holds the regular expression matcher to match command line options with. */ - private Matcher optionMatcher = null; - - /** Holds the parsed command line properties after parsing. */ - private Properties parsedProperties = null; - - /** Flag used to indicate that errors should be created for unknown options. False by default. */ - private boolean errorsOnUnknowns = false; - - /** - * Creates a command line options parser from a command line specification. This is passed to this constructor - * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static - * array may therefore easily be used to configure the command line parser in a single method call with an easily - * readable format. - * - *

    Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they - * are assumed to be null. The elements specify the following parameters: - *

      - *
    1. The name of the option without the leading '-'. For example, "file". To specify the format of the 'free' - * arguments use the option names "1", "2", ... and so on. - *
    2. The option comment. A line of text describing the usage of the option. For example, "The file to be processed." - *
    3. The options argument. This is a very short description of the argument to the option, often a single word - * or a reminder as to the arguments format. When this element is null the option is a flag and does not - * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified - * is only used to print in the usage message to remind the user of the usage of the option. - *
    4. The mandatory flag. When set to "true" an option must always be specified. Any other value, including null, - * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so - * this is ignored for flags. - *
    5. A regular expression describing the format that the argument must take. Ignored if null. - *
    - *

    An example call to this constructor is: - * - *

    -     * CommandLineParser commandLine = new CommandLineParser(
    -     *     new String[][] {{"file", "The file to be processed. ", "filename", "true"},
    -     *                     {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
    -     *                     {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
    -     *                     {"v", "Verbose mode. Prints information about the processing as it goes."},
    -     *                     {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
    -     * 
    - * - * @param config The configuration as an array of arrays of strings. - */ - public CommandLineParser(String[][] config) - { - // Loop through all the command line option specifications creating details for each in the options map. - for (int i = 0; i < config.length; i++) - { - String[] nextOptionSpec = config[i]; - - addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null, - (nextOptionSpec.length > 3) ? ("true".equals(nextOptionSpec[3]) ? true : false) : false, - (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null); - } - } - - /** - * Lists all the parsing errors from the most recent parsing in a string. - * - * @return All the parsing errors from the most recent parsing. - */ - public String getErrors() - { - // Return the empty string if there are no errors. - if (parsingErrors.isEmpty()) - { - return ""; - } - - // Concatenate all the parsing errors together. - String result = ""; - - for (String s : parsingErrors) - { - result += s; - } - - return result; - } - - /** - * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet. - * - * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet. - */ - public String getOptionsInForce() - { - // Check if there are no properties to report and return and empty string if so. - if (parsedProperties == null) - { - return ""; - } - - // List all the properties. - String result = "Options in force:\n"; - - for (Map.Entry property : parsedProperties.entrySet()) - { - result += property.getKey() + " = " + property.getValue() + "\n"; - } - - return result; - } - - /** - * Generates a usage string consisting of the name of each option and each options argument description and - * comment. - * - * @return A usage string for all the options. - */ - public String getUsage() - { - String result = "Options:\n"; - - // Print usage on each of the command line options. - for (CommandLineOption optionInfo : optionMap.values()) - { - result += - optionInfo.option + " " + ((optionInfo.argument != null) ? (optionInfo.argument + " ") : "") - + optionInfo.comment + "\n"; - } - - return result; - } - - /** - * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options - * as errors. When turned off, all unknowns are simply ignored. - * - * @param errors The setting of the errors on unkown flag. True to turn it on. - */ - public void setErrorsOnUnknowns(boolean errors) - { - errorsOnUnknowns = errors; - } - - /** - * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments - * are keyed by integers as strings starting at "1" and then "2", ... and so on. - * - *

    See the class level comment for a description of the parsing rules. - * - * @param args The command line arguments. - * - * @return The arguments as a set of properties. - * - * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception - * is thrown a call to {@link #getErrors} will provide a diagnostic of the command - * line errors. - */ - public Properties parseCommandLine(String[] args) throws IllegalArgumentException - { - Properties options = new Properties(); - - // Used to keep count of the current 'free' argument. - int free = 1; - - // Used to indicate that the most recently parsed option is expecting arguments. - boolean expectingArgs = false; - - // The option that is expecting arguments from the next element of the command line. - String optionExpectingArgs = null; - - // Used to indicate that the most recently parsed option is a duplicate and should be ignored. - boolean ignore = false; - - // Create the regular expression matcher for the command line options. - String regexp = "^("; - int optionsAdded = 0; - - for (Iterator i = optionMap.keySet().iterator(); i.hasNext();) - { - String nextOption = i.next(); - - // Check that the option is not a free argument definition. - boolean notFree = false; - - try - { - Integer.parseInt(nextOption); - } - catch (NumberFormatException e) - { - notFree = true; - } - - // Add the option to the regular expression matcher if it is not a free argument definition. - if (notFree) - { - regexp += nextOption + (i.hasNext() ? "|" : ""); - optionsAdded++; - } - } - - // There has to be more that one option in the regular expression or else the compiler complains that the close - // cannot be nullable if the '?' token is used to make the matched option string optional. - regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)"; - Pattern pattern = Pattern.compile(regexp); - - // Loop through all the command line arguments. - for (int i = 0; i < args.length; i++) - { - // Check if the next command line argument begins with a '-' character and is therefore the start of - // an option. - if (args[i].startsWith("-")) - { - // Extract the value of the option without the leading '-'. - String arg = args[i].substring(1); - - // Match up to the longest matching option. - optionMatcher = pattern.matcher(arg); - optionMatcher.matches(); - - String matchedOption = optionMatcher.group(1); - - // Match any argument directly appended onto the longest matching option. - String matchedArg = optionMatcher.group(2); - - // Check that a known option was matched. - if ((matchedOption != null) && !"".equals(matchedOption)) - { - // Get the command line option information for the matched option. - CommandLineOption optionInfo = optionMap.get(matchedOption); - - // Check if this option is expecting arguments. - if (optionInfo.expectsArgs) - { - // The option is expecting arguments so swallow the next command line argument as an - // argument to this option. - expectingArgs = true; - optionExpectingArgs = matchedOption; - - // In the mean time set this options argument to the empty string in case no argument is ever - // supplied. - // options.put(matchedOption, ""); - } - - // Check if the option was matched on its own and is a flag in which case set that flag. - if ("".equals(matchedArg) && !optionInfo.expectsArgs) - { - options.put(matchedOption, "true"); - } - // The option was matched as a substring with its argument appended to it or is a flag that is - // condensed together with other flags. - else if (!"".equals(matchedArg)) - { - // Check if the option is a flag and therefore is allowed to be condensed together - // with other flags. - if (!optionInfo.expectsArgs) - { - // Set the first matched flag. - options.put(matchedOption, "true"); - - // Repeat the longest matching process on the remainder but ensure that the remainder - // consists only of flags as only flags may be condensed together in this fashion. - do - { - // Match the remainder against the options. - optionMatcher = pattern.matcher(matchedArg); - optionMatcher.matches(); - - matchedOption = optionMatcher.group(1); - matchedArg = optionMatcher.group(2); - - // Check that an option was matched. - if (matchedOption != null) - { - // Get the command line option information for the next matched option. - optionInfo = optionMap.get(matchedOption); - - // Ensure that the next option is a flag or raise an error if not. - if (optionInfo.expectsArgs == true) - { - parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n"); - } - - options.put(matchedOption, "true"); - } - // The remainder could not be matched against a flag it is either an unknown flag - // or an illegal argument to a flag. - else - { - parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n"); - - break; - } - } - // Continue until the remainder of the argument has all been matched with flags. - while (!"".equals(matchedArg)); - } - // The option is expecting an argument, so store the unmatched portion against it - // as its argument. - else - { - // Check the arguments format is correct against any specified format. - checkArgumentFormat(optionInfo, matchedArg); - - // Store the argument against its option (regardless of its format). - options.put(matchedOption, matchedArg); - - // The argument to this flag has already been supplied to it. Do not swallow the - // next command line argument as an argument to this flag. - expectingArgs = false; - } - } - } - else // No matching option was found. - { - // Add this to the list of parsing errors if errors on unkowns is being used. - if (errorsOnUnknowns) - { - parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n"); - } - } - } - // The command line argument did not being with a '-' so it is an argument to the previous flag or it - // is a free argument. - else - { - // Check if a previous flag is expecting to swallow this next argument as its argument. - if (expectingArgs) - { - // Get the option info for the option waiting for arguments. - CommandLineOption optionInfo = optionMap.get(optionExpectingArgs); - - // Check the arguments format is correct against any specified format. - checkArgumentFormat(optionInfo, args[i]); - - // Store the argument against its option (regardless of its format). - options.put(optionExpectingArgs, args[i]); - - // Clear the expecting args flag now that the argument has been swallowed. - expectingArgs = false; - optionExpectingArgs = null; - } - // This command line option is not an argument to any option. Add it to the set of 'free' options. - else - { - // Get the option info for the free option, if there is any. - CommandLineOption optionInfo = optionMap.get(Integer.toString(free)); - - if (optionInfo != null) - { - // Check the arguments format is correct against any specified format. - checkArgumentFormat(optionInfo, args[i]); - } - - // Add to the list of free options. - options.put(Integer.toString(free), args[i]); - - // Move on to the next free argument. - free++; - } - } - } - - // Scan through all the specified options to check that all mandatory options have been set and that all flags - // that were not set are set to false in the set of properties. - for (CommandLineOption optionInfo : optionMap.values()) - { - // Check if this is a flag. - if (!optionInfo.expectsArgs) - { - // Check if the flag is not set in the properties and set it to false if so. - if (!options.containsKey(optionInfo.option)) - { - options.put(optionInfo.option, "false"); - } - } - // Check if this is a mandatory option and was not set. - else if (optionInfo.mandatory && !options.containsKey(optionInfo.option)) - { - // Create an error for the missing option. - parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n"); - } - } - - // Check if there were any errors. - if (!parsingErrors.isEmpty()) - { - // Throw an illegal argument exception to signify that there were parsing errors. - throw new IllegalArgumentException(); - } - - // Convert any name/value pairs in the free arguments into properties in the parsed options. - options = takeFreeArgsAsProperties(options, 1); - - parsedProperties = options; - - return options; - } - - /** - * If a command line has been parsed, calling this method sets all of its parsed options into the specified properties. - */ - public void addCommandLineToProperties(Properties properties) - { - if (parsedProperties != null) - { - for (Object propKey : parsedProperties.keySet()) - { - String name = (String) propKey; - String value = parsedProperties.getProperty(name); - - properties.setProperty(name, value); - } - } - } - - /** - * Resets this command line parser after it has been used to parse a command line. This method will only need - * to be called to use this parser a second time which is not likely seeing as a command line is usually only - * specified once. However, it is exposed as a public method for the rare case where this may be done. - * - *

    Cleans the internal state of this parser, removing all stored errors and information about the options in - * force. - */ - public void reset() - { - parsingErrors = new ArrayList(); - parsedProperties = null; - } - - /** - * Adds the option to list of available command line options. - * - * @param option The option to add as an available command line option. - * @param comment A comment for the option. - * @param argument The text that appears after the option in the usage string. - * @param mandatory When true, indicates that this option is mandatory. - * @param formatRegexp The format that the argument must take, defined as a regular expression. - */ - protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp) - { - // Check if usage text has been set in which case this option is expecting arguments. - boolean expectsArgs = ((argument == null) || argument.equals("")) ? false : true; - - // Add the option to the map of command line options. - CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp); - optionMap.put(option, opt); - } - - /** - * Converts the free arguments into property declarations. After parsing the command line the free arguments - * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method - * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value - * 'value'. - * - *

    For example the comand line: - *

    -     * ... debug=true
    -     * 
    - * - *

    After parsing has properties: - *

    [[1, debug=true]]
    - * - *

    After applying this method the properties are: - *

    [[1, debug=true], [debug, true]]
    - * - * @param properties The parsed command line properties. - * @param from The free argument index to convert to properties from. - * - * @return The parsed command line properties, with free argument name value pairs too. - */ - private Properties takeFreeArgsAsProperties(Properties properties, int from) - { - for (int i = from; true; i++) - { - String nextFreeArg = properties.getProperty(Integer.toString(i)); - - // Terminate the loop once all free arguments have been consumed. - if (nextFreeArg == null) - { - break; - } - - // Split it on the =, strip any whitespace and set it as a system property. - String[] nameValuePair = nextFreeArg.split("="); - - if (nameValuePair.length == 2) - { - properties.setProperty(nameValuePair[0], nameValuePair[1]); - } - } - - return properties; - } - - /** - * Checks the format of an argument to an option against its specified regular expression format if one has - * been set. Any errors are added to the list of parsing errors. - * - * @param optionInfo The command line option information for the option which is havings its argument checked. - * @param matchedArg The string argument to the option. - */ - private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg) - { - // Check if this option enforces a format for its argument. - if (optionInfo.argumentFormatRegexp != null) - { - Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp); - Matcher argumentMatcher = pattern.matcher(matchedArg); - - // Check if the argument does not meet its required format. - if (!argumentMatcher.matches()) - { - // Create an error for this badly formed argument. - parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n"); - } - } - } - - /** - * Extracts all name=value pairs from the command line, sets them all as system properties and also returns - * a map of properties containing them. - * - * @param args The command line. - * @param commandLine The command line parser. - * @param properties The properties object to inject all parsed properties into (optional may be null). - * - * @return A set of properties containing all name=value pairs from the command line. - */ - public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties) - { - // Capture the command line arguments or display errors and correct usage and then exit. - Properties options = null; - - try - { - options = commandLine.parseCommandLine(args); - - // Add all the trailing command line options (name=value pairs) to system properties. They may be picked up - // from there. - commandLine.addCommandLineToProperties(properties); - } - catch (IllegalArgumentException e) - { - System.out.println(commandLine.getErrors()); - System.out.println(commandLine.getUsage()); - System.exit(1); - } - - return options; - } - - /** - * Holds information about a command line options. This includes what its name is, whether or not it is a flag, - * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its - * regular expression format is. - * - *

    - *
    CRC Card
    Responsibilities Collaborations - *
    Hold details of a command line option. - *
    - */ - protected class CommandLineOption - { - /** Holds the text for the flag to match this argument with. */ - public String option = null; - - /** Holds a string describing how to use this command line argument. */ - public String argument = null; - - /** Flag that determines whether or not this command line argument can take arguments. */ - public boolean expectsArgs = false; - - /** Holds a short comment describing what this command line argument is for. */ - public String comment = null; - - /** Flag that determines whether or not this is an mandatory command line argument. */ - public boolean mandatory = false; - - /** A regular expression describing what format the argument to this option muist have. */ - public String argumentFormatRegexp = null; - - /** - * Create a command line option object that holds specific information about a command line option. - * - * @param option The text that matches the option. - * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false. - * @param comment A comment explaining how to use this option. - * @param argument A short reminder of the format of the argument to this option/ - * @param mandatory Set to true if this option is mandatory. - * @param formatRegexp The regular expression that the argument to this option must meet to be valid. - */ - public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory, - String formatRegexp) - { - this.option = option; - this.expectsArgs = expectsArgs; - this.comment = comment; - this.argument = argument; - this.mandatory = mandatory; - this.argumentFormatRegexp = formatRegexp; - } - } -} +/* + * + * 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.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.*; + +/** + * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure + * that they fit their specified format. A command line is made up of flags and options, both may be refered to as + * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not + * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so + * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify + * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified. + * + *

    Some example command lines are: + * + *

      + *
    • This one has two options that expect arguments: + *
      + * cruisecontrol -configfile cruisecontrol.xml -port 9000
      + * 
      + *
    • This has one no-arg flag and two 'free' arguments: + *
      + * zip -r project.zip project/*
      + * 
      + *
    • This one concatenates multiple flags into a single block with only one '-': + *
      + * jar -tvf mytar.tar
      + * 
      + * + *

      The parsing rules are: + * + *

        + *
      1. Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter + * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own. + *
      2. Options expecting arguments must always be on their own. + *
      3. The argument to an option may be seperated from it by whitespace or appended directly onto the option. + *
      4. The argument to an option may never begin with a '-' character. + *
      5. All other arguments not beginning with a '-' character are free arguments that do not belong to any option. + *
      6. The second or later of a set of duplicate or repeated flags are ignored. + *
      7. Options are matched up to the shortest matching option. This is because of the possibility of having no space + * between an option and its argument. This rules out the possibility of using two options where one is an opening + * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because + * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with + * the "bar" argument. + *
      + * + *

      By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed + * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Accept a command line specification. + *
      Parse a command line into properties, validating it against its specification. + *
      Report all errors between a command line and its specification. + *
      Provide a formatted usage string for a command line. + *
      Provide a formatted options in force string for a command line. + *
      Allow errors on unknowns behaviour to be turned on or off. + *
      + */ +public class CommandLineParser +{ + /** Holds a mapping from command line option names to detailed information about those options. */ + private Map optionMap = new HashMap(); + + /** Holds a list of parsing errors. */ + private List parsingErrors = new ArrayList(); + + /** Holds the regular expression matcher to match command line options with. */ + private Matcher optionMatcher = null; + + /** Holds the parsed command line properties after parsing. */ + private Properties parsedProperties = null; + + /** Flag used to indicate that errors should be created for unknown options. False by default. */ + private boolean errorsOnUnknowns = false; + + /** + * Creates a command line options parser from a command line specification. This is passed to this constructor + * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static + * array may therefore easily be used to configure the command line parser in a single method call with an easily + * readable format. + * + *

      Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they + * are assumed to be null. The elements specify the following parameters: + *

        + *
      1. The name of the option without the leading '-'. For example, "file". To specify the format of the 'free' + * arguments use the option names "1", "2", ... and so on. + *
      2. The option comment. A line of text describing the usage of the option. For example, "The file to be processed." + *
      3. The options argument. This is a very short description of the argument to the option, often a single word + * or a reminder as to the arguments format. When this element is null the option is a flag and does not + * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified + * is only used to print in the usage message to remind the user of the usage of the option. + *
      4. The mandatory flag. When set to "true" an option must always be specified. Any other value, including null, + * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so + * this is ignored for flags. + *
      5. A regular expression describing the format that the argument must take. Ignored if null. + *
      + *

      An example call to this constructor is: + * + *

      +     * CommandLineParser commandLine = new CommandLineParser(
      +     *     new String[][] {{"file", "The file to be processed. ", "filename", "true"},
      +     *                     {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
      +     *                     {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
      +     *                     {"v", "Verbose mode. Prints information about the processing as it goes."},
      +     *                     {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
      +     * 
      + * + * @param config The configuration as an array of arrays of strings. + */ + public CommandLineParser(String[][] config) + { + // Loop through all the command line option specifications creating details for each in the options map. + for (int i = 0; i < config.length; i++) + { + String[] nextOptionSpec = config[i]; + + addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null, + (nextOptionSpec.length > 3) ? ("true".equals(nextOptionSpec[3]) ? true : false) : false, + (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null); + } + } + + /** + * Lists all the parsing errors from the most recent parsing in a string. + * + * @return All the parsing errors from the most recent parsing. + */ + public String getErrors() + { + // Return the empty string if there are no errors. + if (parsingErrors.isEmpty()) + { + return ""; + } + + // Concatenate all the parsing errors together. + String result = ""; + + for (String s : parsingErrors) + { + result += s; + } + + return result; + } + + /** + * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet. + * + * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet. + */ + public String getOptionsInForce() + { + // Check if there are no properties to report and return and empty string if so. + if (parsedProperties == null) + { + return ""; + } + + // List all the properties. + String result = "Options in force:\n"; + + for (Map.Entry property : parsedProperties.entrySet()) + { + result += property.getKey() + " = " + property.getValue() + "\n"; + } + + return result; + } + + /** + * Generates a usage string consisting of the name of each option and each options argument description and + * comment. + * + * @return A usage string for all the options. + */ + public String getUsage() + { + String result = "Options:\n"; + + // Print usage on each of the command line options. + for (CommandLineOption optionInfo : optionMap.values()) + { + result += + optionInfo.option + " " + ((optionInfo.argument != null) ? (optionInfo.argument + " ") : "") + + optionInfo.comment + "\n"; + } + + return result; + } + + /** + * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options + * as errors. When turned off, all unknowns are simply ignored. + * + * @param errors The setting of the errors on unkown flag. True to turn it on. + */ + public void setErrorsOnUnknowns(boolean errors) + { + errorsOnUnknowns = errors; + } + + /** + * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments + * are keyed by integers as strings starting at "1" and then "2", ... and so on. + * + *

      See the class level comment for a description of the parsing rules. + * + * @param args The command line arguments. + * + * @return The arguments as a set of properties. + * + * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception + * is thrown a call to {@link #getErrors} will provide a diagnostic of the command + * line errors. + */ + public Properties parseCommandLine(String[] args) throws IllegalArgumentException + { + Properties options = new Properties(); + + // Used to keep count of the current 'free' argument. + int free = 1; + + // Used to indicate that the most recently parsed option is expecting arguments. + boolean expectingArgs = false; + + // The option that is expecting arguments from the next element of the command line. + String optionExpectingArgs = null; + + // Used to indicate that the most recently parsed option is a duplicate and should be ignored. + boolean ignore = false; + + // Create the regular expression matcher for the command line options. + String regexp = "^("; + int optionsAdded = 0; + + for (Iterator i = optionMap.keySet().iterator(); i.hasNext();) + { + String nextOption = i.next(); + + // Check that the option is not a free argument definition. + boolean notFree = false; + + try + { + Integer.parseInt(nextOption); + } + catch (NumberFormatException e) + { + notFree = true; + } + + // Add the option to the regular expression matcher if it is not a free argument definition. + if (notFree) + { + regexp += nextOption + (i.hasNext() ? "|" : ""); + optionsAdded++; + } + } + + // There has to be more that one option in the regular expression or else the compiler complains that the close + // cannot be nullable if the '?' token is used to make the matched option string optional. + regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)"; + Pattern pattern = Pattern.compile(regexp); + + // Loop through all the command line arguments. + for (int i = 0; i < args.length; i++) + { + // Check if the next command line argument begins with a '-' character and is therefore the start of + // an option. + if (args[i].startsWith("-")) + { + // Extract the value of the option without the leading '-'. + String arg = args[i].substring(1); + + // Match up to the longest matching option. + optionMatcher = pattern.matcher(arg); + optionMatcher.matches(); + + String matchedOption = optionMatcher.group(1); + + // Match any argument directly appended onto the longest matching option. + String matchedArg = optionMatcher.group(2); + + // Check that a known option was matched. + if ((matchedOption != null) && !"".equals(matchedOption)) + { + // Get the command line option information for the matched option. + CommandLineOption optionInfo = optionMap.get(matchedOption); + + // Check if this option is expecting arguments. + if (optionInfo.expectsArgs) + { + // The option is expecting arguments so swallow the next command line argument as an + // argument to this option. + expectingArgs = true; + optionExpectingArgs = matchedOption; + + // In the mean time set this options argument to the empty string in case no argument is ever + // supplied. + // options.put(matchedOption, ""); + } + + // Check if the option was matched on its own and is a flag in which case set that flag. + if ("".equals(matchedArg) && !optionInfo.expectsArgs) + { + options.put(matchedOption, "true"); + } + // The option was matched as a substring with its argument appended to it or is a flag that is + // condensed together with other flags. + else if (!"".equals(matchedArg)) + { + // Check if the option is a flag and therefore is allowed to be condensed together + // with other flags. + if (!optionInfo.expectsArgs) + { + // Set the first matched flag. + options.put(matchedOption, "true"); + + // Repeat the longest matching process on the remainder but ensure that the remainder + // consists only of flags as only flags may be condensed together in this fashion. + do + { + // Match the remainder against the options. + optionMatcher = pattern.matcher(matchedArg); + optionMatcher.matches(); + + matchedOption = optionMatcher.group(1); + matchedArg = optionMatcher.group(2); + + // Check that an option was matched. + if (matchedOption != null) + { + // Get the command line option information for the next matched option. + optionInfo = optionMap.get(matchedOption); + + // Ensure that the next option is a flag or raise an error if not. + if (optionInfo.expectsArgs == true) + { + parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n"); + } + + options.put(matchedOption, "true"); + } + // The remainder could not be matched against a flag it is either an unknown flag + // or an illegal argument to a flag. + else + { + parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n"); + + break; + } + } + // Continue until the remainder of the argument has all been matched with flags. + while (!"".equals(matchedArg)); + } + // The option is expecting an argument, so store the unmatched portion against it + // as its argument. + else + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, matchedArg); + + // Store the argument against its option (regardless of its format). + options.put(matchedOption, matchedArg); + + // The argument to this flag has already been supplied to it. Do not swallow the + // next command line argument as an argument to this flag. + expectingArgs = false; + } + } + } + else // No matching option was found. + { + // Add this to the list of parsing errors if errors on unkowns is being used. + if (errorsOnUnknowns) + { + parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n"); + } + } + } + // The command line argument did not being with a '-' so it is an argument to the previous flag or it + // is a free argument. + else + { + // Check if a previous flag is expecting to swallow this next argument as its argument. + if (expectingArgs) + { + // Get the option info for the option waiting for arguments. + CommandLineOption optionInfo = optionMap.get(optionExpectingArgs); + + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, args[i]); + + // Store the argument against its option (regardless of its format). + options.put(optionExpectingArgs, args[i]); + + // Clear the expecting args flag now that the argument has been swallowed. + expectingArgs = false; + optionExpectingArgs = null; + } + // This command line option is not an argument to any option. Add it to the set of 'free' options. + else + { + // Get the option info for the free option, if there is any. + CommandLineOption optionInfo = optionMap.get(Integer.toString(free)); + + if (optionInfo != null) + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, args[i]); + } + + // Add to the list of free options. + options.put(Integer.toString(free), args[i]); + + // Move on to the next free argument. + free++; + } + } + } + + // Scan through all the specified options to check that all mandatory options have been set and that all flags + // that were not set are set to false in the set of properties. + for (CommandLineOption optionInfo : optionMap.values()) + { + // Check if this is a flag. + if (!optionInfo.expectsArgs) + { + // Check if the flag is not set in the properties and set it to false if so. + if (!options.containsKey(optionInfo.option)) + { + options.put(optionInfo.option, "false"); + } + } + // Check if this is a mandatory option and was not set. + else if (optionInfo.mandatory && !options.containsKey(optionInfo.option)) + { + // Create an error for the missing option. + parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n"); + } + } + + // Check if there were any errors. + if (!parsingErrors.isEmpty()) + { + // Throw an illegal argument exception to signify that there were parsing errors. + throw new IllegalArgumentException(); + } + + // Convert any name/value pairs in the free arguments into properties in the parsed options. + options = takeFreeArgsAsProperties(options, 1); + + parsedProperties = options; + + return options; + } + + /** + * If a command line has been parsed, calling this method sets all of its parsed options into the specified properties. + */ + public void addCommandLineToProperties(Properties properties) + { + if (parsedProperties != null) + { + for (Object propKey : parsedProperties.keySet()) + { + String name = (String) propKey; + String value = parsedProperties.getProperty(name); + + properties.setProperty(name, value); + } + } + } + + /** + * Resets this command line parser after it has been used to parse a command line. This method will only need + * to be called to use this parser a second time which is not likely seeing as a command line is usually only + * specified once. However, it is exposed as a public method for the rare case where this may be done. + * + *

      Cleans the internal state of this parser, removing all stored errors and information about the options in + * force. + */ + public void reset() + { + parsingErrors = new ArrayList(); + parsedProperties = null; + } + + /** + * Adds the option to list of available command line options. + * + * @param option The option to add as an available command line option. + * @param comment A comment for the option. + * @param argument The text that appears after the option in the usage string. + * @param mandatory When true, indicates that this option is mandatory. + * @param formatRegexp The format that the argument must take, defined as a regular expression. + */ + protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp) + { + // Check if usage text has been set in which case this option is expecting arguments. + boolean expectsArgs = ((argument == null) || argument.equals("")) ? false : true; + + // Add the option to the map of command line options. + CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp); + optionMap.put(option, opt); + } + + /** + * Converts the free arguments into property declarations. After parsing the command line the free arguments + * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method + * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value + * 'value'. + * + *

      For example the comand line: + *

      +     * ... debug=true
      +     * 
      + * + *

      After parsing has properties: + *

      [[1, debug=true]]
      + * + *

      After applying this method the properties are: + *

      [[1, debug=true], [debug, true]]
      + * + * @param properties The parsed command line properties. + * @param from The free argument index to convert to properties from. + * + * @return The parsed command line properties, with free argument name value pairs too. + */ + private Properties takeFreeArgsAsProperties(Properties properties, int from) + { + for (int i = from; true; i++) + { + String nextFreeArg = properties.getProperty(Integer.toString(i)); + + // Terminate the loop once all free arguments have been consumed. + if (nextFreeArg == null) + { + break; + } + + // Split it on the =, strip any whitespace and set it as a system property. + String[] nameValuePair = nextFreeArg.split("="); + + if (nameValuePair.length == 2) + { + properties.setProperty(nameValuePair[0], nameValuePair[1]); + } + } + + return properties; + } + + /** + * Checks the format of an argument to an option against its specified regular expression format if one has + * been set. Any errors are added to the list of parsing errors. + * + * @param optionInfo The command line option information for the option which is havings its argument checked. + * @param matchedArg The string argument to the option. + */ + private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg) + { + // Check if this option enforces a format for its argument. + if (optionInfo.argumentFormatRegexp != null) + { + Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp); + Matcher argumentMatcher = pattern.matcher(matchedArg); + + // Check if the argument does not meet its required format. + if (!argumentMatcher.matches()) + { + // Create an error for this badly formed argument. + parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n"); + } + } + } + + /** + * Extracts all name=value pairs from the command line, sets them all as system properties and also returns + * a map of properties containing them. + * + * @param args The command line. + * @param commandLine The command line parser. + * @param properties The properties object to inject all parsed properties into (optional may be null). + * + * @return A set of properties containing all name=value pairs from the command line. + */ + public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties) + { + // Capture the command line arguments or display errors and correct usage and then exit. + Properties options = null; + + try + { + options = commandLine.parseCommandLine(args); + + // Add all the trailing command line options (name=value pairs) to system properties. They may be picked up + // from there. + commandLine.addCommandLineToProperties(properties); + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(1); + } + + return options; + } + + /** + * Holds information about a command line options. This includes what its name is, whether or not it is a flag, + * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its + * regular expression format is. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Hold details of a command line option. + *
      + */ + protected class CommandLineOption + { + /** Holds the text for the flag to match this argument with. */ + public String option = null; + + /** Holds a string describing how to use this command line argument. */ + public String argument = null; + + /** Flag that determines whether or not this command line argument can take arguments. */ + public boolean expectsArgs = false; + + /** Holds a short comment describing what this command line argument is for. */ + public String comment = null; + + /** Flag that determines whether or not this is an mandatory command line argument. */ + public boolean mandatory = false; + + /** A regular expression describing what format the argument to this option muist have. */ + public String argumentFormatRegexp = null; + + /** + * Create a command line option object that holds specific information about a command line option. + * + * @param option The text that matches the option. + * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false. + * @param comment A comment explaining how to use this option. + * @param argument A short reminder of the format of the argument to this option/ + * @param mandatory Set to true if this option is mandatory. + * @param formatRegexp The regular expression that the argument to this option must meet to be valid. + */ + public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory, + String formatRegexp) + { + this.option = option; + this.expectsArgs = expectsArgs; + this.comment = comment; + this.argument = argument; + this.mandatory = mandatory; + this.argumentFormatRegexp = formatRegexp; + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/FileUtils.java b/java/common/src/main/java/org/apache/qpid/util/FileUtils.java index 3b8ebc1666..7494745457 100644 --- a/java/common/src/main/java/org/apache/qpid/util/FileUtils.java +++ b/java/common/src/main/java/org/apache/qpid/util/FileUtils.java @@ -1,195 +1,195 @@ -/* - * - * 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.util; - -import java.io.*; - -/** - * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all - * checked exceptions as runtimes, so code using these methods is free of try-catch blocks but does not expect to - * recover from errors. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Read a text file as a string. - *
      Open a file or default resource as an input stream. - *
      - */ -public class FileUtils -{ - /** - * Reads a text file as a string. - * - * @param filename The name of the file. - * - * @return The contents of the file. - */ - public static String readFileAsString(String filename) - { - BufferedInputStream is = null; - - try - { - is = new BufferedInputStream(new FileInputStream(filename)); - } - catch (FileNotFoundException e) - { - throw new RuntimeException(e); - } - - return readStreamAsString(is); - } - - /** - * Reads a text file as a string. - * - * @param file The file. - * - * @return The contents of the file. - */ - public static String readFileAsString(File file) - { - BufferedInputStream is = null; - - try - { - is = new BufferedInputStream(new FileInputStream(file)); - } - catch (FileNotFoundException e) - { - throw new RuntimeException(e); - } - - return readStreamAsString(is); - } - - /** - * Reads the contents of a reader, one line at a time until the end of stream is encountered, and returns all - * together as a string. - * - * @param is The reader. - * - * @return The contents of the reader. - */ - private static String readStreamAsString(BufferedInputStream is) - { - try - { - byte[] data = new byte[4096]; - - StringBuffer inBuffer = new StringBuffer(); - - String line; - int read; - - while ((read = is.read(data)) != -1) - { - String s = new String(data, 0, read); - inBuffer.append(s); - } - - return inBuffer.toString(); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - } - - /** - * Either opens the specified filename as an input stream, or uses the default resource loaded using the - * specified class loader, if opening the file fails or no file name is specified. - * - * @param filename The name of the file to open. - * @param defaultResource The name of the default resource on the classpath if the file cannot be opened. - * @param cl The classloader to load the default resource with. - * - * @return An input stream for the file or resource, or null if one could not be opened. - */ - public static InputStream openFileOrDefaultResource(String filename, String defaultResource, ClassLoader cl) - { - InputStream is = null; - - // Flag to indicate whether the default resource should be used. By default this is true, so that the default - // is used when opening the file fails. - boolean useDefault = true; - - // Try to open the file if one was specified. - if (filename != null) - { - try - { - is = new BufferedInputStream(new FileInputStream(new File(filename))); - - // Clear the default flag because the file was succesfully opened. - useDefault = false; - } - catch (FileNotFoundException e) - { - // Ignore this exception, the default will be used instead. - } - } - - // Load the default resource if a file was not specified, or if opening the file failed. - if (useDefault) - { - is = cl.getResourceAsStream(defaultResource); - } - - return is; - } - - /** - * Copies the specified source file to the specified destintaion file. If the destinationst file does not exist, - * it is created. - * - * @param src The source file name. - * @param dst The destination file name. - */ - public static void copy(File src, File dst) - { - try - { - InputStream in = new FileInputStream(src); - if (!dst.exists()) - { - dst.createNewFile(); - } - - OutputStream out = new FileOutputStream(dst); - - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) - { - out.write(buf, 0, len); - } - - in.close(); - out.close(); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - } -} +/* + * + * 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.util; + +import java.io.*; + +/** + * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all + * checked exceptions as runtimes, so code using these methods is free of try-catch blocks but does not expect to + * recover from errors. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Read a text file as a string. + *
      Open a file or default resource as an input stream. + *
      + */ +public class FileUtils +{ + /** + * Reads a text file as a string. + * + * @param filename The name of the file. + * + * @return The contents of the file. + */ + public static String readFileAsString(String filename) + { + BufferedInputStream is = null; + + try + { + is = new BufferedInputStream(new FileInputStream(filename)); + } + catch (FileNotFoundException e) + { + throw new RuntimeException(e); + } + + return readStreamAsString(is); + } + + /** + * Reads a text file as a string. + * + * @param file The file. + * + * @return The contents of the file. + */ + public static String readFileAsString(File file) + { + BufferedInputStream is = null; + + try + { + is = new BufferedInputStream(new FileInputStream(file)); + } + catch (FileNotFoundException e) + { + throw new RuntimeException(e); + } + + return readStreamAsString(is); + } + + /** + * Reads the contents of a reader, one line at a time until the end of stream is encountered, and returns all + * together as a string. + * + * @param is The reader. + * + * @return The contents of the reader. + */ + private static String readStreamAsString(BufferedInputStream is) + { + try + { + byte[] data = new byte[4096]; + + StringBuffer inBuffer = new StringBuffer(); + + String line; + int read; + + while ((read = is.read(data)) != -1) + { + String s = new String(data, 0, read); + inBuffer.append(s); + } + + return inBuffer.toString(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + /** + * Either opens the specified filename as an input stream, or uses the default resource loaded using the + * specified class loader, if opening the file fails or no file name is specified. + * + * @param filename The name of the file to open. + * @param defaultResource The name of the default resource on the classpath if the file cannot be opened. + * @param cl The classloader to load the default resource with. + * + * @return An input stream for the file or resource, or null if one could not be opened. + */ + public static InputStream openFileOrDefaultResource(String filename, String defaultResource, ClassLoader cl) + { + InputStream is = null; + + // Flag to indicate whether the default resource should be used. By default this is true, so that the default + // is used when opening the file fails. + boolean useDefault = true; + + // Try to open the file if one was specified. + if (filename != null) + { + try + { + is = new BufferedInputStream(new FileInputStream(new File(filename))); + + // Clear the default flag because the file was succesfully opened. + useDefault = false; + } + catch (FileNotFoundException e) + { + // Ignore this exception, the default will be used instead. + } + } + + // Load the default resource if a file was not specified, or if opening the file failed. + if (useDefault) + { + is = cl.getResourceAsStream(defaultResource); + } + + return is; + } + + /** + * Copies the specified source file to the specified destintaion file. If the destinationst file does not exist, + * it is created. + * + * @param src The source file name. + * @param dst The destination file name. + */ + public static void copy(File src, File dst) + { + try + { + InputStream in = new FileInputStream(src); + if (!dst.exists()) + { + dst.createNewFile(); + } + + OutputStream out = new FileOutputStream(dst); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) + { + out.write(buf, 0, len); + } + + in.close(); + out.close(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java b/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java index 10f6a27293..4677713dc9 100644 --- a/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java +++ b/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java @@ -1,75 +1,75 @@ -/* - * - * 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.util; - -/** - * Contains pretty printing convenienve methods for producing formatted logging output, mostly for debugging purposes. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - * - * @todo Drop this. There are already array pretty printing methods it java.utils.Arrays. - */ -public class PrettyPrintingUtils -{ - /** - * Pretty prints an array of ints as a string. - * - * @param array The array to pretty print. - * - * @return The pretty printed string. - */ - public static String printArray(int[] array) - { - String result = "["; - for (int i = 0; i < array.length; i++) - { - result += array[i]; - result += (i < (array.length - 1)) ? ", " : ""; - } - - result += "]"; - - return result; - } - - /** - * Pretty prints an array of strings as a string. - * - * @param array The array to pretty print. - * - * @return The pretty printed string. - */ - public static String printArray(String[] array) - { - String result = "["; - for (int i = 0; i < array.length; i++) - { - result += array[i]; - result += (i < (array.length - 1)) ? ", " : ""; - } - - result += "]"; - - return result; - } -} +/* + * + * 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.util; + +/** + * Contains pretty printing convenienve methods for producing formatted logging output, mostly for debugging purposes. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + * + * @todo Drop this. There are already array pretty printing methods it java.utils.Arrays. + */ +public class PrettyPrintingUtils +{ + /** + * Pretty prints an array of ints as a string. + * + * @param array The array to pretty print. + * + * @return The pretty printed string. + */ + public static String printArray(int[] array) + { + String result = "["; + for (int i = 0; i < array.length; i++) + { + result += array[i]; + result += (i < (array.length - 1)) ? ", " : ""; + } + + result += "]"; + + return result; + } + + /** + * Pretty prints an array of strings as a string. + * + * @param array The array to pretty print. + * + * @return The pretty printed string. + */ + public static String printArray(String[] array) + { + String result = "["; + for (int i = 0; i < array.length; i++) + { + result += array[i]; + result += (i < (array.length - 1)) ? ", " : ""; + } + + result += "]"; + + return result; + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java b/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java index 63cf6f252b..d90e3b1a17 100644 --- a/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java +++ b/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java @@ -1,200 +1,200 @@ -/* - * - * 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.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Iterator; -import java.util.Properties; - -/** - * PropertiesHelper defines some static methods which are useful when working with properties - * files. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Read properties from an input stream - *
      Read properties from a file - *
      Read properties from a URL - *
      Read properties given a path to a file - *
      Trim any whitespace from property values - *
      - */ -public class PropertiesUtils -{ - /** Used for logging. */ - private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); - - /** - * Get properties from an input stream. - * - * @param is The input stream. - * - * @return The properties loaded from the input stream. - * - * @throws IOException If the is an I/O error reading from the stream. - */ - public static Properties getProperties(InputStream is) throws IOException - { - log.debug("getProperties(InputStream): called"); - - // Create properties object laoded from input stream - Properties properties = new Properties(); - - properties.load(is); - - return properties; - } - - /** - * Get properties from a file. - * - * @param file The file. - * - * @return The properties loaded from the file. - * - * @throws IOException If there is an I/O error reading from the file. - */ - public static Properties getProperties(File file) throws IOException - { - log.debug("getProperties(File): called"); - - // Open the file as an input stream - InputStream is = new FileInputStream(file); - - // Create properties object loaded from the stream - Properties properties = getProperties(is); - - // Close the file - is.close(); - - return properties; - } - - /** - * Get properties from a url. - * - * @param url The URL. - * - * @return The properties loaded from the url. - * - * @throws IOException If there is an I/O error reading from the URL. - */ - public static Properties getProperties(URL url) throws IOException - { - log.debug("getProperties(URL): called"); - - // Open the URL as an input stream - InputStream is = url.openStream(); - - // Create properties object loaded from the stream - Properties properties = getProperties(is); - - // Close the url - is.close(); - - return properties; - } - - /** - * Get properties from a path name. The path name may refer to either a file or a URL. - * - * @param pathname The path name. - * - * @return The properties loaded from the file or URL. - * - * @throws IOException If there is an I/O error reading from the URL or file named by the path. - */ - public static Properties getProperties(String pathname) throws IOException - { - log.debug("getProperties(String): called"); - - // Check that the path is not null - if (pathname == null) - { - return null; - } - - // Check if the path is a URL - if (isURL(pathname)) - { - // The path is a URL - return getProperties(new URL(pathname)); - } - else - { - // Assume the path is a file name - return getProperties(new File(pathname)); - } - } - - /** - * Trims whitespace from property values. This method returns a new set of properties - * the same as the properties specified as an argument but with any white space removed by - * the {@link java.lang.String#trim} method. - * - * @param properties The properties to trim whitespace from. - * - * @return The white space trimmed properties. - */ - public static Properties trim(Properties properties) - { - Properties trimmedProperties = new Properties(); - - // Loop over all the properties - for (Iterator i = properties.keySet().iterator(); i.hasNext();) - { - String next = (String) i.next(); - String nextValue = properties.getProperty(next); - - // Trim the value if it is not null - if (nextValue != null) - { - nextValue.trim(); - } - - // Store the trimmed value in the trimmed properties - trimmedProperties.setProperty(next, nextValue); - } - - return trimmedProperties; - } - - /** - * Helper method. Guesses whether a string is a URL or not. A String is considered to be a url if it begins with - * http:, ftp:, or uucp:. - * - * @param name The string to test for being a URL. - * - * @return True if the string is a URL and false if not. - */ - private static boolean isURL(String name) - { - return (name.toLowerCase().startsWith("http:") || name.toLowerCase().startsWith("ftp:") - || name.toLowerCase().startsWith("uucp:")); - } -} +/* + * + * 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.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Iterator; +import java.util.Properties; + +/** + * PropertiesHelper defines some static methods which are useful when working with properties + * files. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Read properties from an input stream + *
      Read properties from a file + *
      Read properties from a URL + *
      Read properties given a path to a file + *
      Trim any whitespace from property values + *
      + */ +public class PropertiesUtils +{ + /** Used for logging. */ + private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); + + /** + * Get properties from an input stream. + * + * @param is The input stream. + * + * @return The properties loaded from the input stream. + * + * @throws IOException If the is an I/O error reading from the stream. + */ + public static Properties getProperties(InputStream is) throws IOException + { + log.debug("getProperties(InputStream): called"); + + // Create properties object laoded from input stream + Properties properties = new Properties(); + + properties.load(is); + + return properties; + } + + /** + * Get properties from a file. + * + * @param file The file. + * + * @return The properties loaded from the file. + * + * @throws IOException If there is an I/O error reading from the file. + */ + public static Properties getProperties(File file) throws IOException + { + log.debug("getProperties(File): called"); + + // Open the file as an input stream + InputStream is = new FileInputStream(file); + + // Create properties object loaded from the stream + Properties properties = getProperties(is); + + // Close the file + is.close(); + + return properties; + } + + /** + * Get properties from a url. + * + * @param url The URL. + * + * @return The properties loaded from the url. + * + * @throws IOException If there is an I/O error reading from the URL. + */ + public static Properties getProperties(URL url) throws IOException + { + log.debug("getProperties(URL): called"); + + // Open the URL as an input stream + InputStream is = url.openStream(); + + // Create properties object loaded from the stream + Properties properties = getProperties(is); + + // Close the url + is.close(); + + return properties; + } + + /** + * Get properties from a path name. The path name may refer to either a file or a URL. + * + * @param pathname The path name. + * + * @return The properties loaded from the file or URL. + * + * @throws IOException If there is an I/O error reading from the URL or file named by the path. + */ + public static Properties getProperties(String pathname) throws IOException + { + log.debug("getProperties(String): called"); + + // Check that the path is not null + if (pathname == null) + { + return null; + } + + // Check if the path is a URL + if (isURL(pathname)) + { + // The path is a URL + return getProperties(new URL(pathname)); + } + else + { + // Assume the path is a file name + return getProperties(new File(pathname)); + } + } + + /** + * Trims whitespace from property values. This method returns a new set of properties + * the same as the properties specified as an argument but with any white space removed by + * the {@link java.lang.String#trim} method. + * + * @param properties The properties to trim whitespace from. + * + * @return The white space trimmed properties. + */ + public static Properties trim(Properties properties) + { + Properties trimmedProperties = new Properties(); + + // Loop over all the properties + for (Iterator i = properties.keySet().iterator(); i.hasNext();) + { + String next = (String) i.next(); + String nextValue = properties.getProperty(next); + + // Trim the value if it is not null + if (nextValue != null) + { + nextValue.trim(); + } + + // Store the trimmed value in the trimmed properties + trimmedProperties.setProperty(next, nextValue); + } + + return trimmedProperties; + } + + /** + * Helper method. Guesses whether a string is a URL or not. A String is considered to be a url if it begins with + * http:, ftp:, or uucp:. + * + * @param name The string to test for being a URL. + * + * @return True if the string is a URL and false if not. + */ + private static boolean isURL(String name) + { + return (name.toLowerCase().startsWith("http:") || name.toLowerCase().startsWith("ftp:") + || name.toLowerCase().startsWith("uucp:")); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java b/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java index 495918911a..28fb2e0c8a 100644 --- a/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java +++ b/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java @@ -1,228 +1,228 @@ -/* - * - * 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.util; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * Provides helper methods for operating on classes and methods using reflection. Reflection methods tend to return - * a lot of checked exception so writing code to use them can be tedious and harder to read, especially when such errors - * are not expected to occur. This class always works with {@link ReflectionUtilsException}, which is a runtime exception, - * to wrap the checked exceptions raised by the standard Java reflection methods. Code using it does not normally - * expect these errors to occur, usually does not have a recovery mechanism for them when they do, but is cleaner, - * quicker to write and easier to read in the majority of cases. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Look up Classes by name. - *
      Instantiate Classes by no-arg constructor. - *
      - */ -public class ReflectionUtils -{ - /** - * Gets the Class object for a named class. - * - * @param className The class to get the Class object for. - * - * @return The Class object for the named class. - */ - public static Class forName(String className) - { - try - { - return Class.forName(className); - } - catch (ClassNotFoundException e) - { - throw new ReflectionUtilsException("ClassNotFoundException whilst finding class.", e); - } - } - - /** - * Creates an instance of a Class, instantiated through its no-args constructor. - * - * @param cls The Class to instantiate. - * @param The Class type. - * - * @return An instance of the class. - */ - public static T newInstance(Class cls) - { - try - { - return cls.newInstance(); - } - catch (InstantiationException e) - { - throw new ReflectionUtilsException("InstantiationException whilst instantiating class.", e); - } - catch (IllegalAccessException e) - { - throw new ReflectionUtilsException("IllegalAccessException whilst instantiating class.", e); - } - } - - /** - * Calls a named method on an object with a specified set of parameters, any Java access modifier are overridden. - * - * @param o The object to call. - * @param method The method name to call. - * @param params The parameters to pass. - * @param paramClasses The argument types. - * - * @return The return value from the method call. - */ - public static Object callMethodOverridingIllegalAccess(Object o, String method, Object[] params, Class[] paramClasses) - { - // Get the objects class. - Class cls = o.getClass(); - - // Get the classes of the parameters. - /*Class[] paramClasses = new Class[params.length]; - - for (int i = 0; i < params.length; i++) - { - paramClasses[i] = params[i].getClass(); - }*/ - - try - { - // Try to find the matching method on the class. - Method m = cls.getDeclaredMethod(method, paramClasses); - - // Make it accessible. - m.setAccessible(true); - - // Invoke it with the parameters. - return m.invoke(o, params); - } - catch (NoSuchMethodException e) - { - throw new ReflectionUtilsException("NoSuchMethodException.", e); - } - catch (IllegalAccessException e) - { - throw new ReflectionUtilsException("IllegalAccessException.", e); - } - catch (InvocationTargetException e) - { - throw new ReflectionUtilsException("InvocationTargetException", e); - } - } - - /** - * Calls a named method on an object with a specified set of parameters. - * - * @param o The object to call. - * @param method The method name to call. - * @param params The parameters to pass. - * - * @return The return value from the method call. - */ - public static Object callMethod(Object o, String method, Object[] params) - { - // Get the objects class. - Class cls = o.getClass(); - - // Get the classes of the parameters. - Class[] paramClasses = new Class[params.length]; - - for (int i = 0; i < params.length; i++) - { - paramClasses[i] = params[i].getClass(); - } - - try - { - // Try to find the matching method on the class. - Method m = cls.getMethod(method, paramClasses); - - // Invoke it with the parameters. - return m.invoke(o, params); - } - catch (NoSuchMethodException e) - { - throw new ReflectionUtilsException("NoSuchMethodException.", e); - } - catch (IllegalAccessException e) - { - throw new ReflectionUtilsException("IllegalAccessException", e); - } - catch (InvocationTargetException e) - { - throw new ReflectionUtilsException("InvocationTargetException", e); - } - } - - /** - * Calls a constuctor witht the specified arguments. - * - * @param constructor The constructor. - * @param args The arguments. - * @param The Class type. - * - * @return An instance of the class that the constructor is for. - */ - public static T newInstance(Constructor constructor, Object[] args) - { - try - { - return constructor.newInstance(args); - } - catch (InstantiationException e) - { - throw new ReflectionUtilsException("InstantiationException", e); - } - catch (IllegalAccessException e) - { - throw new ReflectionUtilsException("IllegalAccessException", e); - } - catch (InvocationTargetException e) - { - throw new ReflectionUtilsException("InvocationTargetException", e); - } - } - - /** - * Gets the constructor of a class that takes the specified set of arguments if any matches. If no matching - * constructor is found then a runtime exception is raised. - * - * @param cls The class to get a constructor from. - * @param args The arguments to match. - * @param The class type. - * - * @return The constructor. - */ - public static Constructor getConstructor(Class cls, Class[] args) - { - try - { - return cls.getConstructor(args); - } - catch (NoSuchMethodException e) - { - throw new ReflectionUtilsException("NoSuchMethodException", e); - } - } -} +/* + * + * 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.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Provides helper methods for operating on classes and methods using reflection. Reflection methods tend to return + * a lot of checked exception so writing code to use them can be tedious and harder to read, especially when such errors + * are not expected to occur. This class always works with {@link ReflectionUtilsException}, which is a runtime exception, + * to wrap the checked exceptions raised by the standard Java reflection methods. Code using it does not normally + * expect these errors to occur, usually does not have a recovery mechanism for them when they do, but is cleaner, + * quicker to write and easier to read in the majority of cases. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Look up Classes by name. + *
      Instantiate Classes by no-arg constructor. + *
      + */ +public class ReflectionUtils +{ + /** + * Gets the Class object for a named class. + * + * @param className The class to get the Class object for. + * + * @return The Class object for the named class. + */ + public static Class forName(String className) + { + try + { + return Class.forName(className); + } + catch (ClassNotFoundException e) + { + throw new ReflectionUtilsException("ClassNotFoundException whilst finding class.", e); + } + } + + /** + * Creates an instance of a Class, instantiated through its no-args constructor. + * + * @param cls The Class to instantiate. + * @param The Class type. + * + * @return An instance of the class. + */ + public static T newInstance(Class cls) + { + try + { + return cls.newInstance(); + } + catch (InstantiationException e) + { + throw new ReflectionUtilsException("InstantiationException whilst instantiating class.", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException whilst instantiating class.", e); + } + } + + /** + * Calls a named method on an object with a specified set of parameters, any Java access modifier are overridden. + * + * @param o The object to call. + * @param method The method name to call. + * @param params The parameters to pass. + * @param paramClasses The argument types. + * + * @return The return value from the method call. + */ + public static Object callMethodOverridingIllegalAccess(Object o, String method, Object[] params, Class[] paramClasses) + { + // Get the objects class. + Class cls = o.getClass(); + + // Get the classes of the parameters. + /*Class[] paramClasses = new Class[params.length]; + + for (int i = 0; i < params.length; i++) + { + paramClasses[i] = params[i].getClass(); + }*/ + + try + { + // Try to find the matching method on the class. + Method m = cls.getDeclaredMethod(method, paramClasses); + + // Make it accessible. + m.setAccessible(true); + + // Invoke it with the parameters. + return m.invoke(o, params); + } + catch (NoSuchMethodException e) + { + throw new ReflectionUtilsException("NoSuchMethodException.", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException.", e); + } + catch (InvocationTargetException e) + { + throw new ReflectionUtilsException("InvocationTargetException", e); + } + } + + /** + * Calls a named method on an object with a specified set of parameters. + * + * @param o The object to call. + * @param method The method name to call. + * @param params The parameters to pass. + * + * @return The return value from the method call. + */ + public static Object callMethod(Object o, String method, Object[] params) + { + // Get the objects class. + Class cls = o.getClass(); + + // Get the classes of the parameters. + Class[] paramClasses = new Class[params.length]; + + for (int i = 0; i < params.length; i++) + { + paramClasses[i] = params[i].getClass(); + } + + try + { + // Try to find the matching method on the class. + Method m = cls.getMethod(method, paramClasses); + + // Invoke it with the parameters. + return m.invoke(o, params); + } + catch (NoSuchMethodException e) + { + throw new ReflectionUtilsException("NoSuchMethodException.", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException", e); + } + catch (InvocationTargetException e) + { + throw new ReflectionUtilsException("InvocationTargetException", e); + } + } + + /** + * Calls a constuctor witht the specified arguments. + * + * @param constructor The constructor. + * @param args The arguments. + * @param The Class type. + * + * @return An instance of the class that the constructor is for. + */ + public static T newInstance(Constructor constructor, Object[] args) + { + try + { + return constructor.newInstance(args); + } + catch (InstantiationException e) + { + throw new ReflectionUtilsException("InstantiationException", e); + } + catch (IllegalAccessException e) + { + throw new ReflectionUtilsException("IllegalAccessException", e); + } + catch (InvocationTargetException e) + { + throw new ReflectionUtilsException("InvocationTargetException", e); + } + } + + /** + * Gets the constructor of a class that takes the specified set of arguments if any matches. If no matching + * constructor is found then a runtime exception is raised. + * + * @param cls The class to get a constructor from. + * @param args The arguments to match. + * @param The class type. + * + * @return The constructor. + */ + public static Constructor getConstructor(Class cls, Class[] args) + { + try + { + return cls.getConstructor(args); + } + catch (NoSuchMethodException e) + { + throw new ReflectionUtilsException("NoSuchMethodException", e); + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java b/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java index 20499641ac..c2862a755b 100644 --- a/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java +++ b/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java @@ -1,44 +1,44 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.util; - -/** - * Wraps a checked exception that occurs when {@link ReflectionUtils} encounters checked exceptions using standard - * Java reflection methods. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Wrap a checked reflection exception. - *
      - */ -public class ReflectionUtilsException extends RuntimeException -{ - /** - * Creates a runtime reflection exception, from a checked one. - * - * @param message The message. - * @param cause The causing exception. - */ - public ReflectionUtilsException(String message, Throwable cause) - { - super(message, cause); - } -} +/* + * + * 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.util; + +/** + * Wraps a checked exception that occurs when {@link ReflectionUtils} encounters checked exceptions using standard + * Java reflection methods. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Wrap a checked reflection exception. + *
      + */ +public class ReflectionUtilsException extends RuntimeException +{ + /** + * Creates a runtime reflection exception, from a checked one. + * + * @param message The message. + * @param cause The causing exception. + */ + public ReflectionUtilsException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java index 4acea0e2ec..ef43d1c8a8 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java @@ -1,13 +1,13 @@ -package org.apache.qpid.util.concurrent; - -/** - * Used to signal that a data element and its producer cannot be requeued or sent an error message when using a - * {@link BatchSynchQueue} because the producer has already been unblocked by an unblocking take on the queue. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Signal that an unblocking take has already occurred. - *
      - */ -public class AlreadyUnblockedException extends RuntimeException -{ } +package org.apache.qpid.util.concurrent; + +/** + * Used to signal that a data element and its producer cannot be requeued or sent an error message when using a + * {@link BatchSynchQueue} because the producer has already been unblocked by an unblocking take on the queue. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Signal that an unblocking take has already occurred. + *
      + */ +public class AlreadyUnblockedException extends RuntimeException +{ } diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java index cf2abfffb8..261eecb561 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java @@ -1,101 +1,101 @@ -package org.apache.qpid.util.concurrent; - -import java.util.Collection; -import java.util.concurrent.BlockingQueue; - -/** - * BatchSynchQueue is an abstraction of the classic producer/consumer buffer pattern for thread interaction. In this - * pattern threads can deposit data onto a buffer whilst other threads take data from the buffer and perform usefull - * work with it. A BatchSynchQueue adds to this the possibility that producers can be blocked until their data is - * consumed or until a consumer chooses to release the producer some time after consuming the data from the queue. - * - *

      There are a number of possible advantages to using this technique when compared with having the producers - * processing their own data: - * - *

        - *
      • Data may be deposited asynchronously in the buffer allowing the producers to continue running.
      • - *
      • Data may be deposited synchronously in the buffer so that producers wait until their data has been processed - * before being allowed to continue.
      • - *
      • Variable rates of production/consumption can be smoothed over by the buffer as it provides space in memory to - * hold data between production and consumption.
      • - *
      • Consumers may be able to batch data as they consume it leading to more efficient consumption over - * individual data item consumption where latency associated with the consume operation can be ammortized. - * For example, it may be possibly to ammortize the cost of a disk seek over many producers.
      • - *
      • Data from seperate threads can be combined together in the buffer, providing a convenient way of spreading work - * amongst many workers and gathering the results together again.
      • - *
      • Different types of queue can be used to hold the buffer, resulting in different processing orders. For example, - * lifo, fifo, priority heap, etc.
      • - *
      - * - *

      The asynchronous type of producer/consumer buffers is already well supported by the java.util.concurrent package - * (in Java 5) and there is also a synchronous queue implementation available there too. This interface extends the - * blocking queue with some more methods for controlling a synchronous blocking queue. In particular it adds additional - * take methods that can be used to take data from a queue without releasing producers, so that consumers have an - * opportunity to confirm correct processing of the data before producers are released. It also adds a put method with - * exceptions so that consumers can signal exception cases back to producers where there are errors in the data. - * - *

      This type of queue is usefull in situations where consumers can obtain an efficiency gain by batching data - * from many threads but where synchronous handling of that data is neccessary because producers need to know that - * their data has been processed before they continue. For example, sending a bundle of messages together, or writing - * many records to disk at once, may result in improved performance but the originators of the messages or disk records - * need confirmation that their data has really been sent or saved to disk. - * - *

      The consumer can put an element back onto the queue or send an error message to the elements producer using the - * {@link SynchRecord} interface. - * - *

      The {@link #take()}, {@link #drainTo(java.util.Collection)} and - * {@link #drainTo(java.util.Collection, int)} methods from {@link BlockingQueue} should behave as if they - * have been called with unblock set to false. That is they take elements from the queue but leave the producers - * blocked. These methods do not return collections of {@link SynchRecord}s so they do not supply an interface through - * which errors or re-queuings can be applied. If these methods are used then the consumer must succesfully process - * all the records it takes. - * - *

      The {@link #put} method should silently swallow any exceptions that consumers attempt to return to the caller. - * In order to handle exceptions the {@link #tryPut} method must be used. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Handle synchronous puts, with possible exceptions. - *
      Allow consumers to take many records from a queue in a batch. - *
      Allow consumers to decide when to unblock synchronous producers. - *
      - */ -public interface BatchSynchQueue extends BlockingQueue -{ - /** - * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the - * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}. - * - * @param e The data element to put into the queue. - * - * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting - * on its entry in the queue being consumed. - * @throws SynchException If a consumer encounters an error whilst processing the data element. - */ - public void tryPut(E e) throws InterruptedException, SynchException; - - /** - * Takes all available data items from the queue or blocks until some become available. The returned items - * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their - * producers, where the producers are still blocked. - * - * @param c The collection to drain the data items into. - * @param unblock If set to true the producers for the taken items will be immediately unblocked. - * - * @return A count of the number of elements that were drained from the queue. - */ - public SynchRef drainTo(Collection> c, boolean unblock); - - /** - * Takes up to maxElements available data items from the queue or blocks until some become available. The returned - * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their - * producers, where the producers are still blocked. - * - * @param c The collection to drain the data items into. - * @param maxElements The maximum number of elements to drain. - * @param unblock If set to true the producers for the taken items will be immediately unblocked. - * - * @return A count of the number of elements that were drained from the queue. - */ - public SynchRef drainTo(Collection> c, int maxElements, boolean unblock); -} +package org.apache.qpid.util.concurrent; + +import java.util.Collection; +import java.util.concurrent.BlockingQueue; + +/** + * BatchSynchQueue is an abstraction of the classic producer/consumer buffer pattern for thread interaction. In this + * pattern threads can deposit data onto a buffer whilst other threads take data from the buffer and perform usefull + * work with it. A BatchSynchQueue adds to this the possibility that producers can be blocked until their data is + * consumed or until a consumer chooses to release the producer some time after consuming the data from the queue. + * + *

      There are a number of possible advantages to using this technique when compared with having the producers + * processing their own data: + * + *

        + *
      • Data may be deposited asynchronously in the buffer allowing the producers to continue running.
      • + *
      • Data may be deposited synchronously in the buffer so that producers wait until their data has been processed + * before being allowed to continue.
      • + *
      • Variable rates of production/consumption can be smoothed over by the buffer as it provides space in memory to + * hold data between production and consumption.
      • + *
      • Consumers may be able to batch data as they consume it leading to more efficient consumption over + * individual data item consumption where latency associated with the consume operation can be ammortized. + * For example, it may be possibly to ammortize the cost of a disk seek over many producers.
      • + *
      • Data from seperate threads can be combined together in the buffer, providing a convenient way of spreading work + * amongst many workers and gathering the results together again.
      • + *
      • Different types of queue can be used to hold the buffer, resulting in different processing orders. For example, + * lifo, fifo, priority heap, etc.
      • + *
      + * + *

      The asynchronous type of producer/consumer buffers is already well supported by the java.util.concurrent package + * (in Java 5) and there is also a synchronous queue implementation available there too. This interface extends the + * blocking queue with some more methods for controlling a synchronous blocking queue. In particular it adds additional + * take methods that can be used to take data from a queue without releasing producers, so that consumers have an + * opportunity to confirm correct processing of the data before producers are released. It also adds a put method with + * exceptions so that consumers can signal exception cases back to producers where there are errors in the data. + * + *

      This type of queue is usefull in situations where consumers can obtain an efficiency gain by batching data + * from many threads but where synchronous handling of that data is neccessary because producers need to know that + * their data has been processed before they continue. For example, sending a bundle of messages together, or writing + * many records to disk at once, may result in improved performance but the originators of the messages or disk records + * need confirmation that their data has really been sent or saved to disk. + * + *

      The consumer can put an element back onto the queue or send an error message to the elements producer using the + * {@link SynchRecord} interface. + * + *

      The {@link #take()}, {@link #drainTo(java.util.Collection)} and + * {@link #drainTo(java.util.Collection, int)} methods from {@link BlockingQueue} should behave as if they + * have been called with unblock set to false. That is they take elements from the queue but leave the producers + * blocked. These methods do not return collections of {@link SynchRecord}s so they do not supply an interface through + * which errors or re-queuings can be applied. If these methods are used then the consumer must succesfully process + * all the records it takes. + * + *

      The {@link #put} method should silently swallow any exceptions that consumers attempt to return to the caller. + * In order to handle exceptions the {@link #tryPut} method must be used. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Handle synchronous puts, with possible exceptions. + *
      Allow consumers to take many records from a queue in a batch. + *
      Allow consumers to decide when to unblock synchronous producers. + *
      + */ +public interface BatchSynchQueue extends BlockingQueue +{ + /** + * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the + * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}. + * + * @param e The data element to put into the queue. + * + * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting + * on its entry in the queue being consumed. + * @throws SynchException If a consumer encounters an error whilst processing the data element. + */ + public void tryPut(E e) throws InterruptedException, SynchException; + + /** + * Takes all available data items from the queue or blocks until some become available. The returned items + * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param c The collection to drain the data items into. + * @param unblock If set to true the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection> c, boolean unblock); + + /** + * Takes up to maxElements available data items from the queue or blocks until some become available. The returned + * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param c The collection to drain the data items into. + * @param maxElements The maximum number of elements to drain. + * @param unblock If set to true the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection> c, int maxElements, boolean unblock); +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java index 47eff4be19..d1c1abd285 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java @@ -1,813 +1,813 @@ -package org.apache.qpid.util.concurrent; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Synchronous/Asynchronous puts. Asynchronous is easiest, just wait till can write to queue and deposit data. - * Synchronous is harder. Deposit data, but then must wait until deposited element/elements are taken before being - * allowed to unblock and continue. Consumer needs some options here too. Can just get the data from the buffer and - * allow any producers unblocked as a result to continue, or can get data but continue blocking while the data is - * processed before sending a message to do the unblocking. Synch/Asynch mode to be controlled by a switch. - * Unblocking/not unblocking during consumer processing to be controlled by the consumers calls. - * - *

      Implementing sub-classes only need to supply an implementation of a queue to produce a valid concrete - * implementation of this. This queue is only accessed through the methods {@link #insert}, {@link #extract}, - * {@link #getBufferCapacity()}, {@link #peekAtBufferHead()}. An implementation can override these methods to implement - * the buffer other than by a queue, for example, by using an array. - * - *

      Normal queue methods to work asynchronously. - *

      Put, take and drain methods from the BlockingQueue interface work synchronously but unblock producers immediately - * when their data is taken. - *

      The additional put, take and drain methods from the BatchSynchQueue interface work synchronously and provide the - * option to keep producers blocked until the consumer decides to release them. - * - *

      Removed take method that keeps producers blocked as it is pointless. Essentially it reduces this class to - * synchronous processing of individual data items, which negates the point of the hand-off design. The efficiency - * gain of the hand off design comes in being able to batch consume requests, ammortizing latency (such as caused by io) - * accross many producers. The only advantage of the single blocking take method is that it did take advantage of the - * queue ordering, which ma be usefull, for example to apply a priority ordering amongst producers. This is also an - * advantage over the java.util.concurrent.SynchronousQueue which doesn't have a backing queue which can be used to - * apply orderings. If a single item take is really needed can just use the drainTo method with a maximum of one item. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - */ -public abstract class BatchSynchQueueBase extends AbstractQueue implements BatchSynchQueue -{ - /** Used for logging. */ - private static final Logger log = LoggerFactory.getLogger(BatchSynchQueueBase.class); - - /** Holds a reference to the queue implementation that holds the buffer. */ - Queue> buffer; - - /** Holds the number of items in the queue */ - private int count; - - /** Main lock guarding all access */ - private ReentrantLock lock; - - /** Condition for waiting takes */ - private Condition notEmpty; - - /** Condition for waiting puts */ - private Condition notFull; - - /** - * Creates a batch synch queue without fair thread scheduling. - */ - public BatchSynchQueueBase() - { - this(false); - } - - /** - * Ensures that the underlying buffer implementation is created. - * - * @param fair true if fairness is to be applied to threads waiting to access the buffer. - */ - public BatchSynchQueueBase(boolean fair) - { - buffer = this.createQueue(); - - // Create the buffer lock with the fairness flag set accordingly. - lock = new ReentrantLock(fair); - - // Create the non-empty and non-full condition monitors on the buffer lock. - notEmpty = lock.newCondition(); - notFull = lock.newCondition(); - } - - /** - * Returns an iterator over the elements contained in this collection. - * - * @return An iterator over the elements contained in this collection. - */ - public Iterator iterator() - { - throw new RuntimeException("Not implemented."); - } - - /** - * Returns the number of elements in this collection. If the collection contains more than - * Integer.MAX_VALUE elements, returns Integer.MAX_VALUE. - * - * @return The number of elements in this collection. - */ - public int size() - { - final ReentrantLock lock = this.lock; - lock.lock(); - - try - { - return count; - } - finally - { - lock.unlock(); - } - } - - /** - * Inserts the specified element into this queue, if possible. When using queues that may impose insertion - * restrictions (for example capacity bounds), method offer is generally preferable to method - * {@link java.util.Collection#add}, which can fail to insert an element only by throwing an exception. - * - * @param e The element to insert. - * - * @return true if it was possible to add the element to this queue, else false - */ - public boolean offer(E e) - { - if (e == null) - { - throw new NullPointerException(); - } - - final ReentrantLock lock = this.lock; - lock.lock(); - - try - { - return insert(e, false); - } - finally - { - lock.unlock(); - } - } - - /** - * Inserts the specified element into this queue, waiting if necessary up to the specified wait time for space to - * become available. - * - * @param e The element to add. - * @param timeout How long to wait before giving up, in units of unit - * @param unit A TimeUnit determining how to interpret the timeout parameter. - * - * @return true if successful, or false if the specified waiting time elapses before space is - * available. - * - * @throws InterruptedException If interrupted while waiting. - * @throws NullPointerException If the specified element is null. - */ - public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException - { - if (e == null) - { - throw new NullPointerException(); - } - - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - - long nanos = unit.toNanos(timeout); - - try - { - do - { - if (insert(e, false)) - { - return true; - } - - try - { - nanos = notFull.awaitNanos(nanos); - } - catch (InterruptedException ie) - { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } - } - while (nanos > 0); - - return false; - } - finally - { - lock.unlock(); - } - } - - /** - * Retrieves and removes the head of this queue, or null if this queue is empty. - * - * @return The head of this queue, or null if this queue is empty. - */ - public E poll() - { - final ReentrantLock lock = this.lock; - - lock.lock(); - try - { - if (count == 0) - { - return null; - } - - E x = extract(true, true).getElement(); - - return x; - } - finally - { - lock.unlock(); - } - } - - /** - * Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time if no elements - * are present on this queue. - * - * @param timeout How long to wait before giving up, in units of unit. - * @param unit A TimeUnit determining how to interpret the timeout parameter. - * - * @return The head of this queue, or null if the specified waiting time elapses before an element is present. - * - * @throws InterruptedException If interrupted while waiting. - */ - public E poll(long timeout, TimeUnit unit) throws InterruptedException - { - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - try - { - long nanos = unit.toNanos(timeout); - - do - { - if (count != 0) - { - E x = extract(true, true).getElement(); - - return x; - } - - try - { - nanos = notEmpty.awaitNanos(nanos); - } - catch (InterruptedException ie) - { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - } - while (nanos > 0); - - return null; - } - finally - { - lock.unlock(); - } - } - - /** - * Retrieves, but does not remove, the head of this queue, returning null if this queue is empty. - * - * @return The head of this queue, or null if this queue is empty. - */ - public E peek() - { - final ReentrantLock lock = this.lock; - lock.lock(); - - try - { - return peekAtBufferHead(); - } - finally - { - lock.unlock(); - } - } - - /** - * Returns the number of elements that this queue can ideally (in the absence of memory or resource constraints) - * accept without blocking, or Integer.MAX_VALUE if there is no intrinsic limit. - * - *

      Note that you cannot always tell if an attempt to add an element will succeed by - * inspecting remainingCapacity because it may be the case that another thread is about to put - * or take an element. - * - * @return The remaining capacity. - */ - public int remainingCapacity() - { - final ReentrantLock lock = this.lock; - lock.lock(); - - try - { - return getBufferCapacity() - count; - } - finally - { - lock.unlock(); - } - } - - /** - * Adds the specified element to this queue, waiting if necessary for space to become available. - * - *

      This method delegated to {@link #tryPut} which can raise {@link SynchException}s. If any are raised - * this method silently ignores them. Use the {@link #tryPut} method directly if you want to catch these - * exceptions. - * - * @param e The element to add. - * - * @throws InterruptedException If interrupted while waiting. - */ - public void put(E e) throws InterruptedException - { - try - { - tryPut(e); - } - catch (SynchException ex) - { - // This exception is deliberately ignored. See the method comment for information about this. - } - } - - /** - * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the - * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}. - * - * @param e The data element to put into the queue. Cannot be null. - * - * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting - * on its entry in the queue being consumed. - * @throws SynchException If a consumer encounters an error whilst processing the data element. - */ - public void tryPut(E e) throws InterruptedException, SynchException - { - if (e == null) - { - throw new NullPointerException(); - } - - // final Queue items = this.buffer; - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - - try - { - while (count == getBufferCapacity()) - { - // Release the lock and wait until the queue is not full. - notFull.await(); - } - } - catch (InterruptedException ie) - { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } - - // There is room in the queue so insert must succeed. Insert into the queu, release the lock and block - // the producer until its data is taken. - insert(e, true); - } - - /** - * Retrieves and removes the head of this queue, waiting if no elements are present on this queue. - * Any producer that has its data element taken by this call will be immediately unblocked. To keep the - * producer blocked whilst taking just a single item, use the - * {@link #drainTo(java.util.Collection>, int, boolean)} - * method. There is no take method to do that because there is not usually any advantage in a synchronous hand - * off design that consumes data one item at a time. It is normal to consume data in chunks to ammortize consumption - * latencies accross many producers where possible. - * - * @return The head of this queue. - * - * @throws InterruptedException if interrupted while waiting. - */ - public E take() throws InterruptedException - { - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - - try - { - try - { - while (count == 0) - { - // Release the lock and wait until the queue becomes non-empty. - notEmpty.await(); - } - } - catch (InterruptedException ie) - { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - - // There is data in the queue so extraction must succeed. Notify any waiting threads that the queue is - // not full, and unblock the producer that owns the data item that is taken. - E x = extract(true, true).getElement(); - - return x; - } - finally - { - lock.unlock(); - } - } - - /** - * Removes all available elements from this queue and adds them into the given collection. This operation may be - * more efficient than repeatedly polling this queue. A failure encountered while attempting to add elements - * to collection c may result in elements being in neither, either or both collections when the associated - * exception is thrown. Attempts to drain a queue to itself result in IllegalArgumentException. Further, - * the behavior of this operation is undefined if the specified collection is modified while the operation is in - * progress. - * - * @param objects The collection to transfer elements into. - * - * @return The number of elements transferred. - * - * @throws NullPointerException If objects is null. - * @throws IllegalArgumentException If objects is this queue. - */ - public int drainTo(Collection objects) - { - return drainTo(objects, -1); - } - - /** - * Removes at most the given number of available elements from this queue and adds them into the given collection. - * A failure encountered while attempting to add elements to collection c may result in elements - * being in neither, either or both collections when the associated exception is thrown. Attempts to drain a queue - * to itself result in IllegalArgumentException. Further, the behavior of this operation is undefined if - * the specified collection is modified while the operation is in progress. - * - * @param objects The collection to transfer elements into. - * @param maxElements The maximum number of elements to transfer. If this is -1 then that is interpreted as meaning - * all elements. - * - * @return The number of elements transferred. - * - * @throws NullPointerException If c is null. - * @throws IllegalArgumentException If c is this queue. - */ - public int drainTo(Collection objects, int maxElements) - { - if (objects == null) - { - throw new NullPointerException(); - } - - if (objects == this) - { - throw new IllegalArgumentException(); - } - - // final Queue items = this.buffer; - final ReentrantLock lock = this.lock; - lock.lock(); - - try - { - int n = 0; - - for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++) - { - // Take items from the queue, do unblock the producers, but don't send not full signals yet. - objects.add(extract(true, false).getElement()); - } - - if (n > 0) - { - // count -= n; - notFull.signalAll(); - } - - return n; - } - finally - { - lock.unlock(); - } - } - - /** - * Takes all available data items from the queue or blocks until some become available. The returned items - * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their - * producers, where the producers are still blocked. - * - * @param c The collection to drain the data items into. - * @param unblock If set to true the producers for the taken items will be immediately unblocked. - * - * @return A count of the number of elements that were drained from the queue. - */ - public SynchRef drainTo(Collection> c, boolean unblock) - { - return drainTo(c, -1, unblock); - } - - /** - * Takes up to maxElements available data items from the queue or blocks until some become available. The returned - * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their - * producers, where the producers are still blocked. - * - * @param coll The collection to drain the data items into. - * @param maxElements The maximum number of elements to drain. - * @param unblock If set to true the producers for the taken items will be immediately unblocked. - * - * @return A count of the number of elements that were drained from the queue. - */ - public SynchRef drainTo(Collection> coll, int maxElements, boolean unblock) - { - if (coll == null) - { - throw new NullPointerException(); - } - - // final Queue items = this.buffer; - final ReentrantLock lock = this.lock; - lock.lock(); - - try - { - int n = 0; - - for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++) - { - // Extract the next record from the queue, don't signall the not full condition yet and release - // producers depending on whether the caller wants to or not. - coll.add(extract(false, unblock)); - } - - if (n > 0) - { - // count -= n; - notFull.signalAll(); - } - - return new SynchRefImpl(n, coll); - } - finally - { - lock.unlock(); - } - } - - /** - * This abstract method should be overriden to return an empty queue. Different implementations of producer - * consumer buffers can control the order in which data is accessed using different queue implementations. - * This method allows the type of queue to be abstracted out of this class and to be supplied by concrete - * implementations. - * - * @return An empty queue. - */ - protected abstract Queue createQueue(); - - /** - * Insert element into the queue, then possibly signal that the queue is not empty and block the producer - * on the element until permission to procede is given. - * - *

      If the producer is to be blocked then the lock must be released first, otherwise no other process - * will be able to get access to the queue. Hence, unlock and block are always set together. - * - *

      Call only when holding the global lock. - * - * @param unlockAndBlock trueIf the global queue lock should be released and the producer should be blocked. - * - * @return true if the operation succeeded, false otherwise. If the result is true this - * method may not return straight away, but only after the producer is unblocked by having its data - * consumed if the unlockAndBlock flag is set. In the false case the method will return straight away, no - * matter what value the unlockAndBlock flag has, leaving the global lock on. - */ - protected boolean insert(E x, boolean unlockAndBlock) - { - // Create a new record for the data item. - SynchRecordImpl record = new SynchRecordImpl(x); - - boolean result = buffer.offer(record); - - if (result) - { - count++; - - // Tell any waiting consumers that the queue is not empty. - notEmpty.signal(); - - if (unlockAndBlock) - { - // Allow other threads to read/write the queue. - lock.unlock(); - - // Wait until a consumer takes this data item. - record.waitForConsumer(); - } - - return true; - } - else - { - return false; - } - } - - /** - * Extract element at current take position, advance, and signal. - * - *

      Call only when holding lock. - */ - protected SynchRecordImpl extract(boolean unblock, boolean signal) - { - SynchRecordImpl result = buffer.remove(); - count--; - - if (signal) - { - notFull.signal(); - } - - if (unblock) - { - result.releaseImmediately(); - } - - return result; - } - - /** - * Get the capacity of the buffer. If the buffer has no maximum capacity then Integer.MAX_VALUE is returned. - * - *

      Call only when holding lock. - * - * @return The maximum capacity of the buffer. - */ - protected int getBufferCapacity() - { - if (buffer instanceof Capacity) - { - return ((Capacity) buffer).getCapacity(); - } - else - { - return Integer.MAX_VALUE; - } - } - - /** - * Return the head element from the buffer. - * - *

      Call only when holding lock. - * - * @return The head element from the buffer. - */ - protected E peekAtBufferHead() - { - return buffer.peek().getElement(); - } - - public class SynchRefImpl implements SynchRef - { - /** Holds the number of synch records associated with this reference. */ - int numRecords; - - /** Holds a reference to the collection of synch records managed by this. */ - Collection> records; - - public SynchRefImpl(int n, Collection> records) - { - this.numRecords = n; - this.records = records; - } - - public int getNumRecords() - { - return numRecords; - } - - /** - * Any producers that have had their data elements taken from the queue but have not been unblocked are unblocked - * when this method is called. The exception to this is producers that have had their data put back onto the queue - * by a consumer. Producers that have had exceptions for their data items registered by consumers will be unblocked - * but will not return from their put call normally, but with an exception instead. - */ - public void unblockProducers() - { - log.debug("public void unblockProducers(): called"); - - if (records != null) - { - for (SynchRecord record : records) - { - // This call takes account of items that have already been released, are to be requeued or are in - // error. - record.releaseImmediately(); - } - } - - records = null; - } - } - - /** - * A SynchRecordImpl is used by a {@link BatchSynchQueue} to pair together a producer with its data. This allows - * the producer of data to be identified so that it can be unblocked when its data is consumed or sent errors when - * its data cannot be consumed. - */ - public class SynchRecordImpl implements SynchRecord - { - /** A boolean latch that determines when the producer for this data item will be allowed to continue. */ - BooleanLatch latch = new BooleanLatch(); - - /** The data element associated with this item. */ - E element; - - /** - * Create a new synch record. - * - * @param e The data element that the record encapsulates. - */ - public SynchRecordImpl(E e) - { - // Keep the data element. - element = e; - } - - /** - * Waits until the producer is given permission to proceded by a consumer. - */ - public void waitForConsumer() - { - latch.await(); - } - - /** - * Gets the data element contained by this record. - * - * @return The data element contained by this record. - */ - public E getElement() - { - return element; - } - - /** - * Immediately releases the producer of this data record. Consumers can bring the synchronization time of - * producers to a minimum by using this method to release them at the earliest possible moment when batch - * consuming records from sychronized producers. - */ - public void releaseImmediately() - { - // Check that the record has not already been released, is in error or is to be requeued. - latch.signal(); - - // Propagate errors to the producer. - - // Requeue items to be requeued. - } - - /** - * Tells the synch queue to put this element back onto the queue instead of releasing its producer. - * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method or - * the {@link #releaseImmediately()} method. - * - *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this - * element has already been unblocked. - */ - public void reQueue() - { - throw new RuntimeException("Not implemented."); - } - - /** - * Tells the synch queue to raise an exception with this elements producer. The exception is not raised - * immediately but upon calling the {@link SynchRef#unblockProducers()} method or the - * {@link #releaseImmediately()} method. The exception will be wrapped in a {@link SynchException} before it is - * raised on the producer. - * - *

      This method is unusual in that it accepts an exception as an argument. This is non-standard but is used - * because the exception is to be passed onto a different thread. - * - *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this - * element has already been unblocked. - * - * @param e The exception to raise on the producer. - */ - public void inError(Exception e) - { - throw new RuntimeException("Not implemented."); - } - } -} +package org.apache.qpid.util.concurrent; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Synchronous/Asynchronous puts. Asynchronous is easiest, just wait till can write to queue and deposit data. + * Synchronous is harder. Deposit data, but then must wait until deposited element/elements are taken before being + * allowed to unblock and continue. Consumer needs some options here too. Can just get the data from the buffer and + * allow any producers unblocked as a result to continue, or can get data but continue blocking while the data is + * processed before sending a message to do the unblocking. Synch/Asynch mode to be controlled by a switch. + * Unblocking/not unblocking during consumer processing to be controlled by the consumers calls. + * + *

      Implementing sub-classes only need to supply an implementation of a queue to produce a valid concrete + * implementation of this. This queue is only accessed through the methods {@link #insert}, {@link #extract}, + * {@link #getBufferCapacity()}, {@link #peekAtBufferHead()}. An implementation can override these methods to implement + * the buffer other than by a queue, for example, by using an array. + * + *

      Normal queue methods to work asynchronously. + *

      Put, take and drain methods from the BlockingQueue interface work synchronously but unblock producers immediately + * when their data is taken. + *

      The additional put, take and drain methods from the BatchSynchQueue interface work synchronously and provide the + * option to keep producers blocked until the consumer decides to release them. + * + *

      Removed take method that keeps producers blocked as it is pointless. Essentially it reduces this class to + * synchronous processing of individual data items, which negates the point of the hand-off design. The efficiency + * gain of the hand off design comes in being able to batch consume requests, ammortizing latency (such as caused by io) + * accross many producers. The only advantage of the single blocking take method is that it did take advantage of the + * queue ordering, which ma be usefull, for example to apply a priority ordering amongst producers. This is also an + * advantage over the java.util.concurrent.SynchronousQueue which doesn't have a backing queue which can be used to + * apply orderings. If a single item take is really needed can just use the drainTo method with a maximum of one item. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + */ +public abstract class BatchSynchQueueBase extends AbstractQueue implements BatchSynchQueue +{ + /** Used for logging. */ + private static final Logger log = LoggerFactory.getLogger(BatchSynchQueueBase.class); + + /** Holds a reference to the queue implementation that holds the buffer. */ + Queue> buffer; + + /** Holds the number of items in the queue */ + private int count; + + /** Main lock guarding all access */ + private ReentrantLock lock; + + /** Condition for waiting takes */ + private Condition notEmpty; + + /** Condition for waiting puts */ + private Condition notFull; + + /** + * Creates a batch synch queue without fair thread scheduling. + */ + public BatchSynchQueueBase() + { + this(false); + } + + /** + * Ensures that the underlying buffer implementation is created. + * + * @param fair true if fairness is to be applied to threads waiting to access the buffer. + */ + public BatchSynchQueueBase(boolean fair) + { + buffer = this.createQueue(); + + // Create the buffer lock with the fairness flag set accordingly. + lock = new ReentrantLock(fair); + + // Create the non-empty and non-full condition monitors on the buffer lock. + notEmpty = lock.newCondition(); + notFull = lock.newCondition(); + } + + /** + * Returns an iterator over the elements contained in this collection. + * + * @return An iterator over the elements contained in this collection. + */ + public Iterator iterator() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Returns the number of elements in this collection. If the collection contains more than + * Integer.MAX_VALUE elements, returns Integer.MAX_VALUE. + * + * @return The number of elements in this collection. + */ + public int size() + { + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return count; + } + finally + { + lock.unlock(); + } + } + + /** + * Inserts the specified element into this queue, if possible. When using queues that may impose insertion + * restrictions (for example capacity bounds), method offer is generally preferable to method + * {@link java.util.Collection#add}, which can fail to insert an element only by throwing an exception. + * + * @param e The element to insert. + * + * @return true if it was possible to add the element to this queue, else false + */ + public boolean offer(E e) + { + if (e == null) + { + throw new NullPointerException(); + } + + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return insert(e, false); + } + finally + { + lock.unlock(); + } + } + + /** + * Inserts the specified element into this queue, waiting if necessary up to the specified wait time for space to + * become available. + * + * @param e The element to add. + * @param timeout How long to wait before giving up, in units of unit + * @param unit A TimeUnit determining how to interpret the timeout parameter. + * + * @return true if successful, or false if the specified waiting time elapses before space is + * available. + * + * @throws InterruptedException If interrupted while waiting. + * @throws NullPointerException If the specified element is null. + */ + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException + { + if (e == null) + { + throw new NullPointerException(); + } + + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + + long nanos = unit.toNanos(timeout); + + try + { + do + { + if (insert(e, false)) + { + return true; + } + + try + { + nanos = notFull.awaitNanos(nanos); + } + catch (InterruptedException ie) + { + notFull.signal(); // propagate to non-interrupted thread + throw ie; + } + } + while (nanos > 0); + + return false; + } + finally + { + lock.unlock(); + } + } + + /** + * Retrieves and removes the head of this queue, or null if this queue is empty. + * + * @return The head of this queue, or null if this queue is empty. + */ + public E poll() + { + final ReentrantLock lock = this.lock; + + lock.lock(); + try + { + if (count == 0) + { + return null; + } + + E x = extract(true, true).getElement(); + + return x; + } + finally + { + lock.unlock(); + } + } + + /** + * Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time if no elements + * are present on this queue. + * + * @param timeout How long to wait before giving up, in units of unit. + * @param unit A TimeUnit determining how to interpret the timeout parameter. + * + * @return The head of this queue, or null if the specified waiting time elapses before an element is present. + * + * @throws InterruptedException If interrupted while waiting. + */ + public E poll(long timeout, TimeUnit unit) throws InterruptedException + { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try + { + long nanos = unit.toNanos(timeout); + + do + { + if (count != 0) + { + E x = extract(true, true).getElement(); + + return x; + } + + try + { + nanos = notEmpty.awaitNanos(nanos); + } + catch (InterruptedException ie) + { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + } + while (nanos > 0); + + return null; + } + finally + { + lock.unlock(); + } + } + + /** + * Retrieves, but does not remove, the head of this queue, returning null if this queue is empty. + * + * @return The head of this queue, or null if this queue is empty. + */ + public E peek() + { + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return peekAtBufferHead(); + } + finally + { + lock.unlock(); + } + } + + /** + * Returns the number of elements that this queue can ideally (in the absence of memory or resource constraints) + * accept without blocking, or Integer.MAX_VALUE if there is no intrinsic limit. + * + *

      Note that you cannot always tell if an attempt to add an element will succeed by + * inspecting remainingCapacity because it may be the case that another thread is about to put + * or take an element. + * + * @return The remaining capacity. + */ + public int remainingCapacity() + { + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + return getBufferCapacity() - count; + } + finally + { + lock.unlock(); + } + } + + /** + * Adds the specified element to this queue, waiting if necessary for space to become available. + * + *

      This method delegated to {@link #tryPut} which can raise {@link SynchException}s. If any are raised + * this method silently ignores them. Use the {@link #tryPut} method directly if you want to catch these + * exceptions. + * + * @param e The element to add. + * + * @throws InterruptedException If interrupted while waiting. + */ + public void put(E e) throws InterruptedException + { + try + { + tryPut(e); + } + catch (SynchException ex) + { + // This exception is deliberately ignored. See the method comment for information about this. + } + } + + /** + * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the + * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}. + * + * @param e The data element to put into the queue. Cannot be null. + * + * @throws InterruptedException If the thread is interrupted whilst waiting to write to the queue or whilst waiting + * on its entry in the queue being consumed. + * @throws SynchException If a consumer encounters an error whilst processing the data element. + */ + public void tryPut(E e) throws InterruptedException, SynchException + { + if (e == null) + { + throw new NullPointerException(); + } + + // final Queue items = this.buffer; + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + + try + { + while (count == getBufferCapacity()) + { + // Release the lock and wait until the queue is not full. + notFull.await(); + } + } + catch (InterruptedException ie) + { + notFull.signal(); // propagate to non-interrupted thread + throw ie; + } + + // There is room in the queue so insert must succeed. Insert into the queu, release the lock and block + // the producer until its data is taken. + insert(e, true); + } + + /** + * Retrieves and removes the head of this queue, waiting if no elements are present on this queue. + * Any producer that has its data element taken by this call will be immediately unblocked. To keep the + * producer blocked whilst taking just a single item, use the + * {@link #drainTo(java.util.Collection>, int, boolean)} + * method. There is no take method to do that because there is not usually any advantage in a synchronous hand + * off design that consumes data one item at a time. It is normal to consume data in chunks to ammortize consumption + * latencies accross many producers where possible. + * + * @return The head of this queue. + * + * @throws InterruptedException if interrupted while waiting. + */ + public E take() throws InterruptedException + { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + + try + { + try + { + while (count == 0) + { + // Release the lock and wait until the queue becomes non-empty. + notEmpty.await(); + } + } + catch (InterruptedException ie) + { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + + // There is data in the queue so extraction must succeed. Notify any waiting threads that the queue is + // not full, and unblock the producer that owns the data item that is taken. + E x = extract(true, true).getElement(); + + return x; + } + finally + { + lock.unlock(); + } + } + + /** + * Removes all available elements from this queue and adds them into the given collection. This operation may be + * more efficient than repeatedly polling this queue. A failure encountered while attempting to add elements + * to collection c may result in elements being in neither, either or both collections when the associated + * exception is thrown. Attempts to drain a queue to itself result in IllegalArgumentException. Further, + * the behavior of this operation is undefined if the specified collection is modified while the operation is in + * progress. + * + * @param objects The collection to transfer elements into. + * + * @return The number of elements transferred. + * + * @throws NullPointerException If objects is null. + * @throws IllegalArgumentException If objects is this queue. + */ + public int drainTo(Collection objects) + { + return drainTo(objects, -1); + } + + /** + * Removes at most the given number of available elements from this queue and adds them into the given collection. + * A failure encountered while attempting to add elements to collection c may result in elements + * being in neither, either or both collections when the associated exception is thrown. Attempts to drain a queue + * to itself result in IllegalArgumentException. Further, the behavior of this operation is undefined if + * the specified collection is modified while the operation is in progress. + * + * @param objects The collection to transfer elements into. + * @param maxElements The maximum number of elements to transfer. If this is -1 then that is interpreted as meaning + * all elements. + * + * @return The number of elements transferred. + * + * @throws NullPointerException If c is null. + * @throws IllegalArgumentException If c is this queue. + */ + public int drainTo(Collection objects, int maxElements) + { + if (objects == null) + { + throw new NullPointerException(); + } + + if (objects == this) + { + throw new IllegalArgumentException(); + } + + // final Queue items = this.buffer; + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + int n = 0; + + for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++) + { + // Take items from the queue, do unblock the producers, but don't send not full signals yet. + objects.add(extract(true, false).getElement()); + } + + if (n > 0) + { + // count -= n; + notFull.signalAll(); + } + + return n; + } + finally + { + lock.unlock(); + } + } + + /** + * Takes all available data items from the queue or blocks until some become available. The returned items + * are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param c The collection to drain the data items into. + * @param unblock If set to true the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection> c, boolean unblock) + { + return drainTo(c, -1, unblock); + } + + /** + * Takes up to maxElements available data items from the queue or blocks until some become available. The returned + * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their + * producers, where the producers are still blocked. + * + * @param coll The collection to drain the data items into. + * @param maxElements The maximum number of elements to drain. + * @param unblock If set to true the producers for the taken items will be immediately unblocked. + * + * @return A count of the number of elements that were drained from the queue. + */ + public SynchRef drainTo(Collection> coll, int maxElements, boolean unblock) + { + if (coll == null) + { + throw new NullPointerException(); + } + + // final Queue items = this.buffer; + final ReentrantLock lock = this.lock; + lock.lock(); + + try + { + int n = 0; + + for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++) + { + // Extract the next record from the queue, don't signall the not full condition yet and release + // producers depending on whether the caller wants to or not. + coll.add(extract(false, unblock)); + } + + if (n > 0) + { + // count -= n; + notFull.signalAll(); + } + + return new SynchRefImpl(n, coll); + } + finally + { + lock.unlock(); + } + } + + /** + * This abstract method should be overriden to return an empty queue. Different implementations of producer + * consumer buffers can control the order in which data is accessed using different queue implementations. + * This method allows the type of queue to be abstracted out of this class and to be supplied by concrete + * implementations. + * + * @return An empty queue. + */ + protected abstract Queue createQueue(); + + /** + * Insert element into the queue, then possibly signal that the queue is not empty and block the producer + * on the element until permission to procede is given. + * + *

      If the producer is to be blocked then the lock must be released first, otherwise no other process + * will be able to get access to the queue. Hence, unlock and block are always set together. + * + *

      Call only when holding the global lock. + * + * @param unlockAndBlock trueIf the global queue lock should be released and the producer should be blocked. + * + * @return true if the operation succeeded, false otherwise. If the result is true this + * method may not return straight away, but only after the producer is unblocked by having its data + * consumed if the unlockAndBlock flag is set. In the false case the method will return straight away, no + * matter what value the unlockAndBlock flag has, leaving the global lock on. + */ + protected boolean insert(E x, boolean unlockAndBlock) + { + // Create a new record for the data item. + SynchRecordImpl record = new SynchRecordImpl(x); + + boolean result = buffer.offer(record); + + if (result) + { + count++; + + // Tell any waiting consumers that the queue is not empty. + notEmpty.signal(); + + if (unlockAndBlock) + { + // Allow other threads to read/write the queue. + lock.unlock(); + + // Wait until a consumer takes this data item. + record.waitForConsumer(); + } + + return true; + } + else + { + return false; + } + } + + /** + * Extract element at current take position, advance, and signal. + * + *

      Call only when holding lock. + */ + protected SynchRecordImpl extract(boolean unblock, boolean signal) + { + SynchRecordImpl result = buffer.remove(); + count--; + + if (signal) + { + notFull.signal(); + } + + if (unblock) + { + result.releaseImmediately(); + } + + return result; + } + + /** + * Get the capacity of the buffer. If the buffer has no maximum capacity then Integer.MAX_VALUE is returned. + * + *

      Call only when holding lock. + * + * @return The maximum capacity of the buffer. + */ + protected int getBufferCapacity() + { + if (buffer instanceof Capacity) + { + return ((Capacity) buffer).getCapacity(); + } + else + { + return Integer.MAX_VALUE; + } + } + + /** + * Return the head element from the buffer. + * + *

      Call only when holding lock. + * + * @return The head element from the buffer. + */ + protected E peekAtBufferHead() + { + return buffer.peek().getElement(); + } + + public class SynchRefImpl implements SynchRef + { + /** Holds the number of synch records associated with this reference. */ + int numRecords; + + /** Holds a reference to the collection of synch records managed by this. */ + Collection> records; + + public SynchRefImpl(int n, Collection> records) + { + this.numRecords = n; + this.records = records; + } + + public int getNumRecords() + { + return numRecords; + } + + /** + * Any producers that have had their data elements taken from the queue but have not been unblocked are unblocked + * when this method is called. The exception to this is producers that have had their data put back onto the queue + * by a consumer. Producers that have had exceptions for their data items registered by consumers will be unblocked + * but will not return from their put call normally, but with an exception instead. + */ + public void unblockProducers() + { + log.debug("public void unblockProducers(): called"); + + if (records != null) + { + for (SynchRecord record : records) + { + // This call takes account of items that have already been released, are to be requeued or are in + // error. + record.releaseImmediately(); + } + } + + records = null; + } + } + + /** + * A SynchRecordImpl is used by a {@link BatchSynchQueue} to pair together a producer with its data. This allows + * the producer of data to be identified so that it can be unblocked when its data is consumed or sent errors when + * its data cannot be consumed. + */ + public class SynchRecordImpl implements SynchRecord + { + /** A boolean latch that determines when the producer for this data item will be allowed to continue. */ + BooleanLatch latch = new BooleanLatch(); + + /** The data element associated with this item. */ + E element; + + /** + * Create a new synch record. + * + * @param e The data element that the record encapsulates. + */ + public SynchRecordImpl(E e) + { + // Keep the data element. + element = e; + } + + /** + * Waits until the producer is given permission to proceded by a consumer. + */ + public void waitForConsumer() + { + latch.await(); + } + + /** + * Gets the data element contained by this record. + * + * @return The data element contained by this record. + */ + public E getElement() + { + return element; + } + + /** + * Immediately releases the producer of this data record. Consumers can bring the synchronization time of + * producers to a minimum by using this method to release them at the earliest possible moment when batch + * consuming records from sychronized producers. + */ + public void releaseImmediately() + { + // Check that the record has not already been released, is in error or is to be requeued. + latch.signal(); + + // Propagate errors to the producer. + + // Requeue items to be requeued. + } + + /** + * Tells the synch queue to put this element back onto the queue instead of releasing its producer. + * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method or + * the {@link #releaseImmediately()} method. + * + *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this + * element has already been unblocked. + */ + public void reQueue() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Tells the synch queue to raise an exception with this elements producer. The exception is not raised + * immediately but upon calling the {@link SynchRef#unblockProducers()} method or the + * {@link #releaseImmediately()} method. The exception will be wrapped in a {@link SynchException} before it is + * raised on the producer. + * + *

      This method is unusual in that it accepts an exception as an argument. This is non-standard but is used + * because the exception is to be passed onto a different thread. + * + *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this + * element has already been unblocked. + * + * @param e The exception to raise on the producer. + */ + public void inError(Exception e) + { + throw new RuntimeException("Not implemented."); + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java index 2a5b0d0c3e..391ca686c9 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java @@ -1,107 +1,107 @@ -package org.apache.qpid.util.concurrent; - -import java.util.concurrent.locks.AbstractQueuedSynchronizer; - -/** - * A BooleanLatch is like a set of traffic lights, where threads can wait at a red light until another thread gives - * the green light. When threads arrive at the latch it is initially red. They queue up until the green signal is - * given, at which point they can all acquire the latch in shared mode and continue to run concurrently. Once the latch - * is signalled it cannot be reset to red again. - * - *

      The latch uses a {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} to implement its synchronization. - * This has two internal states, 0 which means that the latch is blocked, and 1 which means that the latch is open. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Block threads until a go signal is given. - *
      - * - * @todo Might be better to use a countdown latch to count down from 1. Its await method can throw interrupted - * exception which makes the possibility of interruption more explicit, and provides a reminder to recheck the - * latch condition before continuing. - */ -public class BooleanLatch -{ - /** Holds the synchronizer that provides the thread queueing synchronization. */ - private final Sync sync = new Sync(); - - /** - * Tests whether or not the latch has been signalled, that is to say that, the light is green. - * - *

      This method is non-blocking. - * - * @return true if the latch may be acquired; the light is green. - */ - public boolean isSignalled() - { - return sync.isSignalled(); - } - - /** - * Waits on the latch until the signal is given and the light is green. If the light is already green then the - * latch will be acquired and the thread will not have to wait. - * - *

      This method will block until the go signal is given or the thread is otherwise interrupted. Before carrying - * out any processing threads that return from this method should confirm that the go signal has really been given - * on this latch by calling the {@link #isSignalled()} method. - */ - public void await() - { - sync.acquireShared(1); - } - - /** - * Releases any threads currently waiting on the latch. This flips the light to green allowing any threads that - * were waiting for this condition to now run. - * - *

      This method is non-blocking. - */ - public void signal() - { - sync.releaseShared(1); - } - - /** - * Implements a thread queued synchronizer. The internal state 0 means that the queue is blocked and the internl - * state 1 means that the queue is released and that all waiting threads can acquire the synchronizer in shared - * mode. - */ - private static class Sync extends AbstractQueuedSynchronizer - { - /** - * Attempts to acquire this synchronizer in shared mode. It may be acquired once it has been released. - * - * @param ignore This parameter is ignored. - * - * @return 1 if the shared acquisition succeeds and -1 if it fails. - */ - protected int tryAcquireShared(int ignore) - { - return isSignalled() ? 1 : -1; - } - - /** - * Releases the synchronizer, setting its internal state to 1. - * - * @param ignore This parameter is ignored. - * - * @return true always. - */ - protected boolean tryReleaseShared(int ignore) - { - setState(1); - - return true; - } - - /** - * Tests if the synchronizer is signalled. It is signalled when its internal state it 1. - * - * @return true if the internal state is 1, false otherwise. - */ - boolean isSignalled() - { - return getState() != 0; - } - } -} +package org.apache.qpid.util.concurrent; + +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +/** + * A BooleanLatch is like a set of traffic lights, where threads can wait at a red light until another thread gives + * the green light. When threads arrive at the latch it is initially red. They queue up until the green signal is + * given, at which point they can all acquire the latch in shared mode and continue to run concurrently. Once the latch + * is signalled it cannot be reset to red again. + * + *

      The latch uses a {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} to implement its synchronization. + * This has two internal states, 0 which means that the latch is blocked, and 1 which means that the latch is open. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Block threads until a go signal is given. + *
      + * + * @todo Might be better to use a countdown latch to count down from 1. Its await method can throw interrupted + * exception which makes the possibility of interruption more explicit, and provides a reminder to recheck the + * latch condition before continuing. + */ +public class BooleanLatch +{ + /** Holds the synchronizer that provides the thread queueing synchronization. */ + private final Sync sync = new Sync(); + + /** + * Tests whether or not the latch has been signalled, that is to say that, the light is green. + * + *

      This method is non-blocking. + * + * @return true if the latch may be acquired; the light is green. + */ + public boolean isSignalled() + { + return sync.isSignalled(); + } + + /** + * Waits on the latch until the signal is given and the light is green. If the light is already green then the + * latch will be acquired and the thread will not have to wait. + * + *

      This method will block until the go signal is given or the thread is otherwise interrupted. Before carrying + * out any processing threads that return from this method should confirm that the go signal has really been given + * on this latch by calling the {@link #isSignalled()} method. + */ + public void await() + { + sync.acquireShared(1); + } + + /** + * Releases any threads currently waiting on the latch. This flips the light to green allowing any threads that + * were waiting for this condition to now run. + * + *

      This method is non-blocking. + */ + public void signal() + { + sync.releaseShared(1); + } + + /** + * Implements a thread queued synchronizer. The internal state 0 means that the queue is blocked and the internl + * state 1 means that the queue is released and that all waiting threads can acquire the synchronizer in shared + * mode. + */ + private static class Sync extends AbstractQueuedSynchronizer + { + /** + * Attempts to acquire this synchronizer in shared mode. It may be acquired once it has been released. + * + * @param ignore This parameter is ignored. + * + * @return 1 if the shared acquisition succeeds and -1 if it fails. + */ + protected int tryAcquireShared(int ignore) + { + return isSignalled() ? 1 : -1; + } + + /** + * Releases the synchronizer, setting its internal state to 1. + * + * @param ignore This parameter is ignored. + * + * @return true always. + */ + protected boolean tryReleaseShared(int ignore) + { + setState(1); + + return true; + } + + /** + * Tests if the synchronizer is signalled. It is signalled when its internal state it 1. + * + * @return true if the internal state is 1, false otherwise. + */ + boolean isSignalled() + { + return getState() != 0; + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java index 2b4a5f28a9..e317c84971 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java @@ -1,14 +1,14 @@ -package org.apache.qpid.util.concurrent; - -/** - * An interface exposed by data structures that have a maximum capacity. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Report the maximum capacity. - *
      - */ -public interface Capacity -{ - public int getCapacity(); -} +package org.apache.qpid.util.concurrent; + +/** + * An interface exposed by data structures that have a maximum capacity. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Report the maximum capacity. + *
      + */ +public interface Capacity +{ + public int getCapacity(); +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java index 8f682ec462..49020803d7 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java @@ -1,29 +1,29 @@ -package org.apache.qpid.util.concurrent; - -import java.util.Queue; - -/** - * SynchBuffer completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying - * queue as an array. This uses FIFO ordering for the queue but restricts the maximum size of the queue to a fixed - * amount. It also has the advantage that, as the buffer does not grow and shrink dynamically, memory for the buffer - * is allocated up front and does not create garbage during the operation of the queue. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Provide array based FIFO queue to create a batch synched queue around. - *
      - * - * @todo Write an array based buffer implementation that implements Queue. - */ -public class SynchBuffer extends BatchSynchQueueBase -{ - /** - * Returns an empty queue, implemented as an array. - * - * @return An empty queue, implemented as an array. - */ - protected Queue createQueue() - { - throw new RuntimeException("Not implemented."); - } -} +package org.apache.qpid.util.concurrent; + +import java.util.Queue; + +/** + * SynchBuffer completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying + * queue as an array. This uses FIFO ordering for the queue but restricts the maximum size of the queue to a fixed + * amount. It also has the advantage that, as the buffer does not grow and shrink dynamically, memory for the buffer + * is allocated up front and does not create garbage during the operation of the queue. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Provide array based FIFO queue to create a batch synched queue around. + *
      + * + * @todo Write an array based buffer implementation that implements Queue. + */ +public class SynchBuffer extends BatchSynchQueueBase +{ + /** + * Returns an empty queue, implemented as an array. + * + * @return An empty queue, implemented as an array. + */ + protected Queue createQueue() + { + throw new RuntimeException("Not implemented."); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java index c6edff6320..77b60f2b72 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java @@ -1,31 +1,31 @@ -package org.apache.qpid.util.concurrent; - -/** - * SynchException is used to encapsulate exceptions with the data elements that caused them in order to send exceptions - * back from the consumers of a {@link BatchSynchQueue} to producers. The underlying exception should be retrieved from - * the {@link #getCause} method. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Encapsulate a data element and exception. - *
      - */ -public class SynchException extends Exception -{ - /** Holds the data element that is in error. */ - Object element; - - /** - * Creates a new BaseApplicationException object. - * - * @param message The exception message. - * @param cause The underlying throwable cause. This may be null. - */ - public SynchException(String message, Throwable cause, Object element) - { - super(message, cause); - - // Keep the data element that was in error. - this.element = element; - } -} +package org.apache.qpid.util.concurrent; + +/** + * SynchException is used to encapsulate exceptions with the data elements that caused them in order to send exceptions + * back from the consumers of a {@link BatchSynchQueue} to producers. The underlying exception should be retrieved from + * the {@link #getCause} method. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Encapsulate a data element and exception. + *
      + */ +public class SynchException extends Exception +{ + /** Holds the data element that is in error. */ + Object element; + + /** + * Creates a new BaseApplicationException object. + * + * @param message The exception message. + * @param cause The underlying throwable cause. This may be null. + */ + public SynchException(String message, Throwable cause, Object element) + { + super(message, cause); + + // Keep the data element that was in error. + this.element = element; + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java index df3f2b849a..9d15c211f6 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java @@ -1,27 +1,27 @@ -package org.apache.qpid.util.concurrent; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * SynchQueue completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying - * queue as a linked list. This uses FIFO ordering for the queue and allows the queue to grow to accomodate more - * elements as needed. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Provide linked list FIFO queue to create a batch synched queue around. - *
      - */ -public class SynchQueue extends BatchSynchQueueBase -{ - /** - * Returns an empty queue, implemented as a linked list. - * - * @return An empty queue, implemented as a linked list. - */ - protected Queue createQueue() - { - return new LinkedList(); - } -} +package org.apache.qpid.util.concurrent; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * SynchQueue completes the {@link BatchSynchQueueBase} abstract class by providing an implementation of the underlying + * queue as a linked list. This uses FIFO ordering for the queue and allows the queue to grow to accomodate more + * elements as needed. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Provide linked list FIFO queue to create a batch synched queue around. + *
      + */ +public class SynchQueue extends BatchSynchQueueBase +{ + /** + * Returns an empty queue, implemented as a linked list. + * + * @return An empty queue, implemented as a linked list. + */ + protected Queue createQueue() + { + return new LinkedList(); + } +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java index cacef472d6..5e002100c2 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java @@ -1,53 +1,53 @@ -package org.apache.qpid.util.concurrent; - -/** - * SynchRecord associates a data item from a {@link BatchSynchQueue} with its producer. This enables the data item data - * item to be put back on the queue without unblocking its producer, or to send exceptions to the producer. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Get the underlying data element. - *
      Put the data element back on the queue without unblocking its producer. - *
      Send and exception to the data elements producer. - *
      - */ -public interface SynchRecord -{ - /** - * Gets the data element contained by this record. - * - * @return The data element contained by this record. - */ - public E getElement(); - - /** - * Tells the synch queue to put this element back onto the queue instead of releasing its producer. - * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method. - * - *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element - * has already been unblocked. - */ - public void reQueue(); - - /** - * Immediately releases the producer of this data record. Consumers can bring the synchronization time of - * producers to a minimum by using this method to release them at the earliest possible moment when batch - * consuming records from sychronized producers. - */ - public void releaseImmediately(); - - /** - * Tells the synch queue to raise an exception with this elements producer. The exception is not raised immediately - * but upon calling the {@link SynchRef#unblockProducers()} method. The exception will be wrapped in a - * {@link SynchException} before it is raised on the producer. - * - *

      This method is unusual in that it accepts an exception as an argument. This is non-standard but is used - * because the exception is to be passed onto a different thread. - * - *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element - * has already been unblocked. - * - * @param e The exception to raise on the producer. - */ - public void inError(Exception e); -} +package org.apache.qpid.util.concurrent; + +/** + * SynchRecord associates a data item from a {@link BatchSynchQueue} with its producer. This enables the data item data + * item to be put back on the queue without unblocking its producer, or to send exceptions to the producer. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Get the underlying data element. + *
      Put the data element back on the queue without unblocking its producer. + *
      Send and exception to the data elements producer. + *
      + */ +public interface SynchRecord +{ + /** + * Gets the data element contained by this record. + * + * @return The data element contained by this record. + */ + public E getElement(); + + /** + * Tells the synch queue to put this element back onto the queue instead of releasing its producer. + * The element is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method. + * + *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element + * has already been unblocked. + */ + public void reQueue(); + + /** + * Immediately releases the producer of this data record. Consumers can bring the synchronization time of + * producers to a minimum by using this method to release them at the earliest possible moment when batch + * consuming records from sychronized producers. + */ + public void releaseImmediately(); + + /** + * Tells the synch queue to raise an exception with this elements producer. The exception is not raised immediately + * but upon calling the {@link SynchRef#unblockProducers()} method. The exception will be wrapped in a + * {@link SynchException} before it is raised on the producer. + * + *

      This method is unusual in that it accepts an exception as an argument. This is non-standard but is used + * because the exception is to be passed onto a different thread. + * + *

      This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this element + * has already been unblocked. + * + * @param e The exception to raise on the producer. + */ + public void inError(Exception e); +} diff --git a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java index c044ed0a60..a75f7b766d 100644 --- a/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java +++ b/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java @@ -1,30 +1,30 @@ -package org.apache.qpid.util.concurrent; - -/** - * A SynchRef is an interface which is returned from the synchronous take and drain methods of {@link BatchSynchQueue}, - * allowing call-backs to be made against the synchronizing strucutre. It allows the consumer to communicate when it - * wants producers that have their data taken to be unblocked. - * - *

      - *
      CRC Card
      Responsibilities - *
      Report number of records returned by a taking operation. - *
      Provide call-back to release producers of taken records. - *
      - */ -public interface SynchRef -{ - /** - * Reports the number of records taken by the take or drain operation. - * - * @return The number of records taken by the take or drain operation. - */ - public int getNumRecords(); - - /** - * Any producers that have had their data elements taken from the queue but have not been unblocked are - * unblocked when this method is called. The exception to this is producers that have had their data put back - * onto the queue by a consumer. Producers that have had exceptions for their data items registered by consumers - * will be unblocked but will not return from their put call normally, but with an exception instead. - */ - public void unblockProducers(); -} +package org.apache.qpid.util.concurrent; + +/** + * A SynchRef is an interface which is returned from the synchronous take and drain methods of {@link BatchSynchQueue}, + * allowing call-backs to be made against the synchronizing strucutre. It allows the consumer to communicate when it + * wants producers that have their data taken to be unblocked. + * + *

      + *
      CRC Card
      Responsibilities + *
      Report number of records returned by a taking operation. + *
      Provide call-back to release producers of taken records. + *
      + */ +public interface SynchRef +{ + /** + * Reports the number of records taken by the take or drain operation. + * + * @return The number of records taken by the take or drain operation. + */ + public int getNumRecords(); + + /** + * Any producers that have had their data elements taken from the queue but have not been unblocked are + * unblocked when this method is called. The exception to this is producers that have had their data put back + * onto the queue by a consumer. Producers that have had exceptions for their data items registered by consumers + * will be unblocked but will not return from their put call normally, but with an exception instead. + */ + public void unblockProducers(); +} diff --git a/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java b/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java index 66fb3252df..942901f1c0 100644 --- a/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java +++ b/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java @@ -1,554 +1,554 @@ -/* - * - * 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.util; - -import junit.framework.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Properties; - -/** - * Unit tests the {@link CommandLineParser} class. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Check that parsing a single flag works ok. - *
      Check that parsing multiple flags condensed together works ok. - *
      Check that parsing an option with a space between it and its argument works ok. - *
      Check that parsing an option with no space between it and its argument works ok. - *
      Check that parsing an option with specific argument format works ok. - *
      Check that parsing an option with specific argument format fails on bad argument. - *
      Check that parsing a flag condensed together with an option fails. - *
      Check that parsing a free argument works ok. - *
      Check that parsing a free argument with specific format works ok. - *
      Check that parsing a free argument with specific format fails on bad argument. - *
      Check that parsing a mandatory option works ok. - *
      Check that parsing a mandatory free argument works ok. - *
      Check that parsing a mandatory option fails when no option is set. - *
      Check that parsing a mandatory free argument fails when no argument is specified. - *
      Check that parsing an unknown option works when unknowns not errors. - *
      Check that parsing an unknown flag fails when unknowns are to be reported as errors. - *
      Check that parsing an unknown option fails when unknowns are to be reported as errors. - *
      Check that get errors returns a string on errors. - *
      Check that get errors returns an empty string on no errors. - *
      Check that get usage returns a string. - *
      Check that get options in force returns an empty string before parsing. - *
      Check that get options in force return a non-empty string after parsing. - *
      - */ -public class CommandLineParserTest extends TestCase -{ - private static final Logger log = LoggerFactory.getLogger(CommandLineParserTest.class); - - public CommandLineParserTest(String name) - { - super(name); - } - - /** - * Compile all the tests for the default test implementation of a traversable state into a test suite. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("CommandLineParser Tests"); - - // Add all the tests defined in this class (using the default constructor) - suite.addTestSuite(CommandLineParserTest.class); - - return suite; - } - - /** Check that get errors returns an empty string on no errors. */ - public void testGetErrorsReturnsEmptyStringOnNoErrors() throws Exception - { - // Create a command line parser for some flags and options. - CommandLineParser parser = - new CommandLineParser( - new String[][] - { - { "t1", "Test Flag 1." }, - { "t2", "Test Option 2.", "test" }, - { "t3", "Test Option 3.", "test", "true" }, - { "t4", "Test Option 4.", "test", null, "^test$" } - }); - - // Do some legal parsing. - parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" }); - - // Check that the get errors message returns an empty string. - assertTrue("The errors method did not return an empty string.", "".equals(parser.getErrors())); - } - - /** Check that get errors returns a string on errors. */ - public void testGetErrorsReturnsStringOnErrors() throws Exception - { - // Create a command line parser for some flags and options. - CommandLineParser parser = - new CommandLineParser( - new String[][] - { - { "t1", "Test Flag 1." }, - { "t2", "Test Option 2.", "test" }, - { "t3", "Test Option 3.", "test", "true" }, - { "t4", "Test Option 4.", "test", null, "^test$" } - }); - - try - { - // Do some illegal parsing. - parser.parseCommandLine(new String[] { "-t1", "-t1t2test", "-t4fail" }); - } - catch (IllegalArgumentException e) - { } - - // Check that the get errors message returns a string. - assertTrue("The errors method returned an empty string.", - !((parser.getErrors() == null) || "".equals(parser.getErrors()))); - - } - - /** Check that get options in force returns an empty string before parsing. */ - public void testGetOptionsInForceReturnsEmptyStringBeforeParsing() throws Exception - { - // Create a command line parser for some flags and options. - CommandLineParser parser = - new CommandLineParser( - new String[][] - { - { "t1", "Test Flag 1." }, - { "t2", "Test Option 2.", "test" }, - { "t3", "Test Option 3.", "test", "true" }, - { "t4", "Test Option 4.", "test", null, "^test$" } - }); - - // Check that the options in force method returns an empty string. - assertTrue("The options in force method did not return an empty string.", "".equals(parser.getOptionsInForce())); - } - - /** Check that get options in force return a non-empty string after parsing. */ - public void testGetOptionsInForceReturnsNonEmptyStringAfterParsing() throws Exception - { - // Create a command line parser for some flags and options. - CommandLineParser parser = - new CommandLineParser( - new String[][] - { - { "t1", "Test Flag 1." }, - { "t2", "Test Option 2.", "test" }, - { "t3", "Test Option 3.", "test", "true" }, - { "t4", "Test Option 4.", "test", null, "^test$" } - }); - - // Do some parsing. - parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" }); - - // Check that the options in force method returns a string. - assertTrue("The options in force method did not return a non empty string.", - !((parser.getOptionsInForce() == null) || "".equals(parser.getOptionsInForce()))); - } - - /** Check that get usage returns a string. */ - public void testGetUsageReturnsString() throws Exception - { - // Create a command line parser for some flags and options. - CommandLineParser parser = - new CommandLineParser( - new String[][] - { - { "t1", "Test Flag 1." }, - { "t2", "Test Option 2.", "test" }, - { "t3", "Test Option 3.", "test", "true" }, - { "t4", "Test Option 4.", "test", null, "^test$" } - }); - - // Check that the usage method returns a string. - assertTrue("The usage method did not return a non empty string.", - !((parser.getUsage() == null) || "".equals(parser.getUsage()))); - } - - /** Check that parsing multiple flags condensed together works ok. */ - public void testParseCondensedFlagsOk() throws Exception - { - // Create a command line parser for multiple flags. - CommandLineParser parser = - new CommandLineParser( - new String[][] - { - { "t1", "Test Flag 1." }, - { "t2", "Test Flag 2." }, - { "t3", "Test Flag 3." } - }); - - // Parse a command line with the flags set and condensed together. - Properties testProps = parser.parseCommandLine(new String[] { "-t1t2t3" }); - - // Check that the flags were set in the parsed properties. - assertTrue("The t1 flag was not \"true\", it was: " + testProps.get("t1"), "true".equals(testProps.get("t1"))); - assertTrue("The t2 flag was not \"true\", it was: " + testProps.get("t2"), "true".equals(testProps.get("t2"))); - assertTrue("The t3 flag was not \"true\", it was: " + testProps.get("t3"), "true".equals(testProps.get("t3"))); - } - - /** Check that parsing a flag condensed together with an option fails. */ - public void testParseFlagCondensedWithOptionFails() throws Exception - { - // Create a command line parser for a flag and an option. - CommandLineParser parser = - new CommandLineParser(new String[][] - { - { "t1", "Test Flag 1." }, - { "t2", "Test Option 2.", "test" } - }); - - // Check that the parser reports an error. - boolean testPassed = false; - - try - { - // Parse a command line with the flag and option condensed together. - Properties testProps = parser.parseCommandLine(new String[] { "-t1t2" }); - } - catch (IllegalArgumentException e) - { - testPassed = true; - } - - assertTrue("IllegalArgumentException not thrown when a flag and option are condensed together.", testPassed); - } - - /** Check that parsing a free argument with specific format fails on bad argument. */ - public void testParseFormattedFreeArgumentFailsBadArgument() throws Exception - { - // Create a command line parser for a formatted free argument. - CommandLineParser parser = - new CommandLineParser(new String[][] - { - { "1", "Test Free Argument.", "test", null, "^test$" } - }); - - // Check that the parser signals an error for a badly formatted argument. - boolean testPassed = false; - - try - { - // Parse a command line with this option set incorrectly. - Properties testProps = parser.parseCommandLine(new String[] { "fail" }); - } - catch (IllegalArgumentException e) - { - testPassed = true; - } - - assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed); - } - - /** Check that parsing a free argument with specific format works ok. */ - public void testParseFormattedFreeArgumentOk() throws Exception - { - // Create a command line parser for a formatted free argument. - CommandLineParser parser = - new CommandLineParser(new String[][] - { - { "1", "Test Free Argument.", "test", null, "^test$" } - }); - - // Parse a command line with this argument set correctly. - Properties testProps = parser.parseCommandLine(new String[] { "test" }); - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), - "test".equals(testProps.get("1"))); - } - - /** Check that parsing an option with specific argument format fails on bad argument. */ - public void testParseFormattedOptionArgumentFailsBadArgument() throws Exception - { - // Create a command line parser for a formatted option. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "t", "Test Option.", "test", null, "^test$" } - }); - - // Check that the parser signals an error for a badly formatted argument. - boolean testPassed = false; - - try - { - // Parse a command line with this option set incorrectly. - Properties testProps = parser.parseCommandLine(new String[] { "-t", "fail" }); - } - catch (IllegalArgumentException e) - { - testPassed = true; - } - - assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed); - } - - /** Check that parsing an option with specific argument format works ok. */ - public void testParseFormattedOptionArgumentOk() throws Exception - { - // Create a command line parser for a formatted option. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "t", "Test Option.", "test", null, "^test$" } - }); - - // Parse a command line with this option set correctly. - Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), - "test".equals(testProps.get("t"))); - } - - /** Check that parsing a free argument works ok. */ - public void testParseFreeArgumentOk() throws Exception - { - // Create a command line parser for a free argument. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "1", "Test Free Argument.", "test" } - }); - - // Parse a command line with this argument set. - Properties testProps = parser.parseCommandLine(new String[] { "test" }); - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), - "test".equals(testProps.get("1"))); - } - - /** Check that parsing a mandatory option works ok. */ - public void testParseMandatoryOptionOk() throws Exception - { - // Create a command line parser for a mandatory option. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "t", "Test Option.", "test", "true" } - }); - - // Parse a command line with this option set correctly. - Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), - "test".equals(testProps.get("t"))); - } - - /** Check that parsing a mandatory free argument works ok. */ - public void testParseMandatoryFreeArgumentOk() throws Exception - { - // Create a command line parser for a mandatory free argument. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "1", "Test Option.", "test", "true" } - }); - - // Parse a command line with this argument set. - Properties testProps = parser.parseCommandLine(new String[] { "test" }); - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), - "test".equals(testProps.get("1"))); - } - - /** Check that parsing a mandatory free argument fails when no argument is specified. */ - public void testParseManadatoryFreeArgumentFailsNoArgument() throws Exception - { - // Create a command line parser for a mandatory free argument. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "1", "Test Option.", "test", "true" } - }); - - // Check that parsing fails when this mandatory free argument is missing. - boolean testPassed = false; - - try - { - // Parse a command line with this free argument not set. - Properties testProps = parser.parseCommandLine(new String[] {}); - } - catch (IllegalArgumentException e) - { - testPassed = true; - } - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed); - } - - /** Check that parsing a mandatory option fails when no option is set. */ - public void testParseMandatoryFailsNoOption() throws Exception - { - // Create a command line parser for a mandatory option. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "t", "Test Option.", "test", "true" } - }); - - // Check that parsing fails when this mandatory option is missing. - boolean testPassed = false; - - try - { - // Parse a command line with this option not set. - Properties testProps = parser.parseCommandLine(new String[] {}); - } - catch (IllegalArgumentException e) - { - testPassed = true; - } - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed); - } - - /** Check that parsing an option with no space between it and its argument works ok. */ - public void testParseOptionWithNoSpaceOk() throws Exception - { - // Create a command line parser for an option. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "t", "Test Option.", "test" } - }); - - // Parse a command line with this option set with no space. - Properties testProps = parser.parseCommandLine(new String[] { "-ttest" }); - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), - "test".equals(testProps.get("t"))); - } - - /** Check that parsing an option with a space between it and its argument works ok. */ - public void testParseOptionWithSpaceOk() throws Exception - { - // Create a command line parser for an option. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "t", "Test Option.", "test" } - }); - - // Parse a command line with this option set with a space. - Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); - - // Check that the resultant properties contains the correctly parsed option. - assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), - "test".equals(testProps.get("t"))); - } - - /** Check that parsing a single flag works ok. */ - public void testParseSingleFlagOk() throws Exception - { - // Create a command line parser for a single flag. - CommandLineParser parser = new CommandLineParser(new String[][] - { - { "t", "Test Flag." } - }); - - // Parse a command line with the single flag set. - Properties testProps = parser.parseCommandLine(new String[] { "-t" }); - - // Check that the flag is set in the parsed properties. - assertTrue("The t flag was not \"true\", it was: " + testProps.get("t"), "true".equals(testProps.get("t"))); - - // Reset the parser. - parser.reset(); - - // Parse a command line with the single flag not set. - testProps = parser.parseCommandLine(new String[] {}); - - // Check that the flag is cleared in the parsed properties. - assertTrue("The t flag was not \"false\", it was: " + testProps.get("t"), "false".equals(testProps.get("t"))); - } - - /** Check that parsing an unknown option works when unknowns not errors. */ - public void testParseUnknownOptionOk() throws Exception - { - // Create a command line parser for no flags or options - CommandLineParser parser = new CommandLineParser(new String[][] {}); - - // Check that parsing does not fail on an unknown flag. - try - { - parser.parseCommandLine(new String[] { "-t" }); - } - catch (IllegalArgumentException e) - { - fail("The parser threw an IllegalArgumentException on an unknown flag when errors on unkowns is off."); - } - } - - /** Check that parsing an unknown flag fails when unknowns are to be reported as errors. */ - public void testParseUnknownFlagFailsWhenUnknownsAreErrors() throws Exception - { - // Create a command line parser for no flags or options - CommandLineParser parser = new CommandLineParser(new String[][] {}); - - // Turn on fail on unknowns mode. - parser.setErrorsOnUnknowns(true); - - // Check that parsing fails on an unknown flag. - boolean testPassed = false; - - try - { - parser.parseCommandLine(new String[] { "-t" }); - } - catch (IllegalArgumentException e) - { - testPassed = true; - } - - assertTrue("IllegalArgumentException not thrown for an unknown flag when errors on unknowns mode is on.", - testPassed); - } - - /** Check that parsing an unknown option fails when unknowns are to be reported as errors. */ - public void testParseUnknownOptionFailsWhenUnknownsAreErrors() throws Exception - { - // Create a command line parser for no flags or options - CommandLineParser parser = new CommandLineParser(new String[][] {}); - - // Turn on fail on unknowns mode. - parser.setErrorsOnUnknowns(true); - - // Check that parsing fails on an unknown flag. - boolean testPassed = false; - - try - { - parser.parseCommandLine(new String[] { "-t", "test" }); - } - catch (IllegalArgumentException e) - { - testPassed = true; - } - - assertTrue("IllegalArgumentException not thrown for an unknown option when errors on unknowns mode is on.", - testPassed); - } -} +/* + * + * 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.util; + +import junit.framework.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Properties; + +/** + * Unit tests the {@link CommandLineParser} class. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Check that parsing a single flag works ok. + *
      Check that parsing multiple flags condensed together works ok. + *
      Check that parsing an option with a space between it and its argument works ok. + *
      Check that parsing an option with no space between it and its argument works ok. + *
      Check that parsing an option with specific argument format works ok. + *
      Check that parsing an option with specific argument format fails on bad argument. + *
      Check that parsing a flag condensed together with an option fails. + *
      Check that parsing a free argument works ok. + *
      Check that parsing a free argument with specific format works ok. + *
      Check that parsing a free argument with specific format fails on bad argument. + *
      Check that parsing a mandatory option works ok. + *
      Check that parsing a mandatory free argument works ok. + *
      Check that parsing a mandatory option fails when no option is set. + *
      Check that parsing a mandatory free argument fails when no argument is specified. + *
      Check that parsing an unknown option works when unknowns not errors. + *
      Check that parsing an unknown flag fails when unknowns are to be reported as errors. + *
      Check that parsing an unknown option fails when unknowns are to be reported as errors. + *
      Check that get errors returns a string on errors. + *
      Check that get errors returns an empty string on no errors. + *
      Check that get usage returns a string. + *
      Check that get options in force returns an empty string before parsing. + *
      Check that get options in force return a non-empty string after parsing. + *
      + */ +public class CommandLineParserTest extends TestCase +{ + private static final Logger log = LoggerFactory.getLogger(CommandLineParserTest.class); + + public CommandLineParserTest(String name) + { + super(name); + } + + /** + * Compile all the tests for the default test implementation of a traversable state into a test suite. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("CommandLineParser Tests"); + + // Add all the tests defined in this class (using the default constructor) + suite.addTestSuite(CommandLineParserTest.class); + + return suite; + } + + /** Check that get errors returns an empty string on no errors. */ + public void testGetErrorsReturnsEmptyStringOnNoErrors() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Do some legal parsing. + parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" }); + + // Check that the get errors message returns an empty string. + assertTrue("The errors method did not return an empty string.", "".equals(parser.getErrors())); + } + + /** Check that get errors returns a string on errors. */ + public void testGetErrorsReturnsStringOnErrors() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + try + { + // Do some illegal parsing. + parser.parseCommandLine(new String[] { "-t1", "-t1t2test", "-t4fail" }); + } + catch (IllegalArgumentException e) + { } + + // Check that the get errors message returns a string. + assertTrue("The errors method returned an empty string.", + !((parser.getErrors() == null) || "".equals(parser.getErrors()))); + + } + + /** Check that get options in force returns an empty string before parsing. */ + public void testGetOptionsInForceReturnsEmptyStringBeforeParsing() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Check that the options in force method returns an empty string. + assertTrue("The options in force method did not return an empty string.", "".equals(parser.getOptionsInForce())); + } + + /** Check that get options in force return a non-empty string after parsing. */ + public void testGetOptionsInForceReturnsNonEmptyStringAfterParsing() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Do some parsing. + parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" }); + + // Check that the options in force method returns a string. + assertTrue("The options in force method did not return a non empty string.", + !((parser.getOptionsInForce() == null) || "".equals(parser.getOptionsInForce()))); + } + + /** Check that get usage returns a string. */ + public void testGetUsageReturnsString() throws Exception + { + // Create a command line parser for some flags and options. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" }, + { "t3", "Test Option 3.", "test", "true" }, + { "t4", "Test Option 4.", "test", null, "^test$" } + }); + + // Check that the usage method returns a string. + assertTrue("The usage method did not return a non empty string.", + !((parser.getUsage() == null) || "".equals(parser.getUsage()))); + } + + /** Check that parsing multiple flags condensed together works ok. */ + public void testParseCondensedFlagsOk() throws Exception + { + // Create a command line parser for multiple flags. + CommandLineParser parser = + new CommandLineParser( + new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Flag 2." }, + { "t3", "Test Flag 3." } + }); + + // Parse a command line with the flags set and condensed together. + Properties testProps = parser.parseCommandLine(new String[] { "-t1t2t3" }); + + // Check that the flags were set in the parsed properties. + assertTrue("The t1 flag was not \"true\", it was: " + testProps.get("t1"), "true".equals(testProps.get("t1"))); + assertTrue("The t2 flag was not \"true\", it was: " + testProps.get("t2"), "true".equals(testProps.get("t2"))); + assertTrue("The t3 flag was not \"true\", it was: " + testProps.get("t3"), "true".equals(testProps.get("t3"))); + } + + /** Check that parsing a flag condensed together with an option fails. */ + public void testParseFlagCondensedWithOptionFails() throws Exception + { + // Create a command line parser for a flag and an option. + CommandLineParser parser = + new CommandLineParser(new String[][] + { + { "t1", "Test Flag 1." }, + { "t2", "Test Option 2.", "test" } + }); + + // Check that the parser reports an error. + boolean testPassed = false; + + try + { + // Parse a command line with the flag and option condensed together. + Properties testProps = parser.parseCommandLine(new String[] { "-t1t2" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown when a flag and option are condensed together.", testPassed); + } + + /** Check that parsing a free argument with specific format fails on bad argument. */ + public void testParseFormattedFreeArgumentFailsBadArgument() throws Exception + { + // Create a command line parser for a formatted free argument. + CommandLineParser parser = + new CommandLineParser(new String[][] + { + { "1", "Test Free Argument.", "test", null, "^test$" } + }); + + // Check that the parser signals an error for a badly formatted argument. + boolean testPassed = false; + + try + { + // Parse a command line with this option set incorrectly. + Properties testProps = parser.parseCommandLine(new String[] { "fail" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed); + } + + /** Check that parsing a free argument with specific format works ok. */ + public void testParseFormattedFreeArgumentOk() throws Exception + { + // Create a command line parser for a formatted free argument. + CommandLineParser parser = + new CommandLineParser(new String[][] + { + { "1", "Test Free Argument.", "test", null, "^test$" } + }); + + // Parse a command line with this argument set correctly. + Properties testProps = parser.parseCommandLine(new String[] { "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), + "test".equals(testProps.get("1"))); + } + + /** Check that parsing an option with specific argument format fails on bad argument. */ + public void testParseFormattedOptionArgumentFailsBadArgument() throws Exception + { + // Create a command line parser for a formatted option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", null, "^test$" } + }); + + // Check that the parser signals an error for a badly formatted argument. + boolean testPassed = false; + + try + { + // Parse a command line with this option set incorrectly. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "fail" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed); + } + + /** Check that parsing an option with specific argument format works ok. */ + public void testParseFormattedOptionArgumentOk() throws Exception + { + // Create a command line parser for a formatted option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", null, "^test$" } + }); + + // Parse a command line with this option set correctly. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing a free argument works ok. */ + public void testParseFreeArgumentOk() throws Exception + { + // Create a command line parser for a free argument. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "1", "Test Free Argument.", "test" } + }); + + // Parse a command line with this argument set. + Properties testProps = parser.parseCommandLine(new String[] { "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), + "test".equals(testProps.get("1"))); + } + + /** Check that parsing a mandatory option works ok. */ + public void testParseMandatoryOptionOk() throws Exception + { + // Create a command line parser for a mandatory option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", "true" } + }); + + // Parse a command line with this option set correctly. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing a mandatory free argument works ok. */ + public void testParseMandatoryFreeArgumentOk() throws Exception + { + // Create a command line parser for a mandatory free argument. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "1", "Test Option.", "test", "true" } + }); + + // Parse a command line with this argument set. + Properties testProps = parser.parseCommandLine(new String[] { "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"), + "test".equals(testProps.get("1"))); + } + + /** Check that parsing a mandatory free argument fails when no argument is specified. */ + public void testParseManadatoryFreeArgumentFailsNoArgument() throws Exception + { + // Create a command line parser for a mandatory free argument. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "1", "Test Option.", "test", "true" } + }); + + // Check that parsing fails when this mandatory free argument is missing. + boolean testPassed = false; + + try + { + // Parse a command line with this free argument not set. + Properties testProps = parser.parseCommandLine(new String[] {}); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed); + } + + /** Check that parsing a mandatory option fails when no option is set. */ + public void testParseMandatoryFailsNoOption() throws Exception + { + // Create a command line parser for a mandatory option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test", "true" } + }); + + // Check that parsing fails when this mandatory option is missing. + boolean testPassed = false; + + try + { + // Parse a command line with this option not set. + Properties testProps = parser.parseCommandLine(new String[] {}); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed); + } + + /** Check that parsing an option with no space between it and its argument works ok. */ + public void testParseOptionWithNoSpaceOk() throws Exception + { + // Create a command line parser for an option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test" } + }); + + // Parse a command line with this option set with no space. + Properties testProps = parser.parseCommandLine(new String[] { "-ttest" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing an option with a space between it and its argument works ok. */ + public void testParseOptionWithSpaceOk() throws Exception + { + // Create a command line parser for an option. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Option.", "test" } + }); + + // Parse a command line with this option set with a space. + Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" }); + + // Check that the resultant properties contains the correctly parsed option. + assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"), + "test".equals(testProps.get("t"))); + } + + /** Check that parsing a single flag works ok. */ + public void testParseSingleFlagOk() throws Exception + { + // Create a command line parser for a single flag. + CommandLineParser parser = new CommandLineParser(new String[][] + { + { "t", "Test Flag." } + }); + + // Parse a command line with the single flag set. + Properties testProps = parser.parseCommandLine(new String[] { "-t" }); + + // Check that the flag is set in the parsed properties. + assertTrue("The t flag was not \"true\", it was: " + testProps.get("t"), "true".equals(testProps.get("t"))); + + // Reset the parser. + parser.reset(); + + // Parse a command line with the single flag not set. + testProps = parser.parseCommandLine(new String[] {}); + + // Check that the flag is cleared in the parsed properties. + assertTrue("The t flag was not \"false\", it was: " + testProps.get("t"), "false".equals(testProps.get("t"))); + } + + /** Check that parsing an unknown option works when unknowns not errors. */ + public void testParseUnknownOptionOk() throws Exception + { + // Create a command line parser for no flags or options + CommandLineParser parser = new CommandLineParser(new String[][] {}); + + // Check that parsing does not fail on an unknown flag. + try + { + parser.parseCommandLine(new String[] { "-t" }); + } + catch (IllegalArgumentException e) + { + fail("The parser threw an IllegalArgumentException on an unknown flag when errors on unkowns is off."); + } + } + + /** Check that parsing an unknown flag fails when unknowns are to be reported as errors. */ + public void testParseUnknownFlagFailsWhenUnknownsAreErrors() throws Exception + { + // Create a command line parser for no flags or options + CommandLineParser parser = new CommandLineParser(new String[][] {}); + + // Turn on fail on unknowns mode. + parser.setErrorsOnUnknowns(true); + + // Check that parsing fails on an unknown flag. + boolean testPassed = false; + + try + { + parser.parseCommandLine(new String[] { "-t" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown for an unknown flag when errors on unknowns mode is on.", + testPassed); + } + + /** Check that parsing an unknown option fails when unknowns are to be reported as errors. */ + public void testParseUnknownOptionFailsWhenUnknownsAreErrors() throws Exception + { + // Create a command line parser for no flags or options + CommandLineParser parser = new CommandLineParser(new String[][] {}); + + // Turn on fail on unknowns mode. + parser.setErrorsOnUnknowns(true); + + // Check that parsing fails on an unknown flag. + boolean testPassed = false; + + try + { + parser.parseCommandLine(new String[] { "-t", "test" }); + } + catch (IllegalArgumentException e) + { + testPassed = true; + } + + assertTrue("IllegalArgumentException not thrown for an unknown option when errors on unknowns mode is on.", + testPassed); + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java index 45bbfcd148..db17c7aacc 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java @@ -1,135 +1,135 @@ -/* - * - * 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.interop.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; - -/** - * Implements tet case 1, dummy run. This test case sends no test messages, it exists to confirm that the test harness - * is interacting with the coordinator correctly. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Supply the name of the test case that this implements. - *
      Accept/Reject invites based on test parameters. - *
      Adapt to assigned roles. - *
      Perform test case actions. - *
      Generate test reports. - *
      - */ -public class TestCase1DummyRun implements TestClientControlledTest -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); - - /** - * Should provide the name of the test case that this class implements. The exact names are defined in the - * interop testing spec. - * - * @return The name of the test case that this implements. - */ - public String getName() - { - log.debug("public String getName(): called"); - - return "TC1_DummyRun"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage): called"); - - // Test parameters don't matter, accept all invites. - return true; - } - - /** - * Assigns the role to be played by this test case. The test parameters are fully specified in the - * assignment message. When this method return the test case will be ready to execute. - * - * @param role The role to be played; sender or receivers. - * @param assignRoleMessage The role assingment message, contains the full test parameters. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); - - // Do nothing, both roles are the same. - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - */ - public void start(int numMessages) - { - log.debug("public void start(): called"); - - // Do nothing. - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession to create the report message in. - * - * @return The report message. - * - * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. - */ - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session controlSession): called"); - - // Generate a dummy report, the coordinator expects a report but doesn't care what it is. - return session.createTextMessage("Dummy Run, Ok."); - } - - /** - * Handles incoming test messages. Does nothing. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Ignore any messages. - } -} +/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * Implements tet case 1, dummy run. This test case sends no test messages, it exists to confirm that the test harness + * is interacting with the coordinator correctly. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Supply the name of the test case that this implements. + *
      Accept/Reject invites based on test parameters. + *
      Adapt to assigned roles. + *
      Perform test case actions. + *
      Generate test reports. + *
      + */ +public class TestCase1DummyRun implements TestClientControlledTest +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase1DummyRun.class); + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC1_DummyRun"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage): called"); + + // Test parameters don't matter, accept all invites. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role, Message assignRoleMessage): called"); + + // Do nothing, both roles are the same. + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + */ + public void start(int numMessages) + { + log.debug("public void start(): called"); + + // Do nothing. + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Generate a dummy report, the coordinator expects a report but doesn't care what it is. + return session.createTextMessage("Dummy Run, Ok."); + } + + /** + * Handles incoming test messages. Does nothing. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Ignore any messages. + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java index 1d30ff7ca6..36d3cce7f7 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java @@ -1,209 +1,209 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.interop.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 2, basic P2P. Sends/received a specified number of messages to a specified route on the - * default direct exchange. Produces reports on the actual number of messages sent/received. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Supply the name of the test case that this implements. - *
      Accept/Reject invites based on test parameters. - *
      Adapt to assigned roles. - *
      Send required number of test messages. - *
      Generate test reports. - *
      - */ -public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The controlSession to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * Should provide the name of the test case that this class implements. The exact names are defined in the - * interop testing spec. - * - * @return The name of the test case that this implements. - */ - public String getName() - { - log.debug("public String getName(): called"); - - return "TC2_BasicP2P"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // All invites are acceptable. - return true; - } - - /** - * Assigns the role to be played by this test case. The test parameters are fully specified in the - * assignment message. When this method return the test case will be ready to execute. - * - * @param role The role to be played; sender or receivers. - * - * @param assignRoleMessage The role assingment message, contains the full test parameters. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = TestUtils.createConnection(TestClient.testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session.createTextMessage("test"); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession to create the report message in. - * - * @return The report message. - * - * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. - */ - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 2, basic P2P. Sends/received a specified number of messages to a specified route on the + * default direct exchange. Produces reports on the actual number of messages sent/received. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Supply the name of the test case that this implements. + *
      Accept/Reject invites based on test parameters. + *
      Adapt to assigned roles. + *
      Send required number of test messages. + *
      Generate test reports. + *
      + */ +public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC2_BasicP2P"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session.createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java index 622ddc2f64..205472716b 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java @@ -1,239 +1,239 @@ -/* - * - * 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.interop.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Supply the name of the test case that this implements. - *
      Accept/Reject invites based on test parameters. - *
      Adapt to assigned roles. - *
      Send required number of test messages using pub/sub. - *
      Generate test reports. - *
      - */ -public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * Should provide the name of the test case that this class implements. The exact names are defined in the - * interop testing spec. - * - * @return The name of the test case that this implements. - */ - public String getName() - { - log.debug("public String getName(): called"); - - return "TC3_BasicPubSub"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // All invites are acceptable. - return true; - } - - /** - * Assigns the role to be played by this test case. The test parameters are fully specified in the - * assignment message. When this method return the test case will be ready to execute. - * - * @param role The role to be played; sender or receivers. - * - * @param assignRoleMessage The role assingment message, contains the full test parameters. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - Destination sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (Connection conn : connection) - { - conn.start(); - } - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = session[0].createTextMessage("test"); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession to create the report message in. - * - * @return The report message. - * - * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. - */ - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (Connection conn : connection) - { - conn.close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Supply the name of the test case that this implements. + *
      Accept/Reject invites based on test parameters. + *
      Adapt to assigned roles. + *
      Send required number of test messages using pub/sub. + *
      Generate test reports. + *
      + */ +public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC3_BasicPubSub"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = session[0].createTextMessage("test"); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java index 0388c56678..3730233264 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java @@ -1,214 +1,214 @@ -/* - * - * 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.interop.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified - * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages - * sent/received. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Supply the name of the test case that this implements. - *
      Accept/Reject invites based on test parameters. - *
      Adapt to assigned roles. - *
      Send required number of test messages. - *
      Generate test reports. - *
      - */ -public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The size of the test messages to send. */ - private int messageSize; - - /** The connection to send the test messages on. */ - private Connection connection; - - /** The controlSession to send the test messages on. */ - private Session session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * Should provide the name of the test case that this class implements. The exact names are defined in the - * interop testing spec. - * - * @return The name of the test case that this implements. - */ - public String getName() - { - log.debug("public String getName(): called"); - - return "TC4_P2PMessageSize"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // All invites are acceptable. - return true; - } - - /** - * Assigns the role to be played by this test case. The test parameters are fully specified in the - * assignment message. When this method return the test case will be ready to execute. - * - * @param role The role to be played; sender or receivers. - * - * @param assignRoleMessage The role assingment message, contains the full test parameters. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Create a new connection to pass the test messages on. - connection = TestUtils.createConnection(TestClient.testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); - messageSize = assignRoleMessage.getIntProperty("messageSize"); - Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); - - log.debug("numMessages = " + numMessages); - log.debug("sendDestination = " + sendDestination); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a message producer if so. - case SENDER: - producer = session.createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages. - case RECEIVER: - MessageConsumer consumer = session.createConsumer(sendDestination); - consumer.setMessageListener(this); - break; - } - - connection.start(); - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession to create the report message in. - * - * @return The report message. - * - * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. - */ - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connection. - connection.close(); - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified + * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages + * sent/received. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Supply the name of the test case that this implements. + *
      Accept/Reject invites based on test parameters. + *
      Adapt to assigned roles. + *
      Send required number of test messages. + *
      Generate test reports. + *
      + */ +public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connection to send the test messages on. */ + private Connection connection; + + /** The controlSession to send the test messages on. */ + private Session session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC4_P2PMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Create a new connection to pass the test messages on. + connection = TestUtils.createConnection(TestClient.testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME")); + + log.debug("numMessages = " + numMessages); + log.debug("sendDestination = " + sendDestination); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a message producer if so. + case SENDER: + producer = session.createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages. + case RECEIVER: + MessageConsumer consumer = session.createConsumer(sendDestination); + consumer.setMessageListener(this); + break; + } + + connection.start(); + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connection. + connection.close(); + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java index ab59e16ab3..f601712bc9 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java @@ -1,243 +1,243 @@ -/* - * - * 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.interop.clienttestcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; - -import javax.jms.*; - -/** - * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route - * on the default topic exchange, using the specified number of receivers connections, and the specified message size. - * Produces reports on the actual number of messages sent/received. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Supply the name of the test case that this implements. - *
      Accept/Reject invites based on test parameters. - *
      Adapt to assigned roles. - *
      Send required number of test messages using pub/sub. - *
      Generate test reports. - *
      - */ -public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class); - - /** Holds the count of test messages received. */ - private int messageCount; - - /** The role to be played by the test. */ - private Roles role; - - /** The number of test messages to send. */ - private int numMessages; - - /** The size of the test messages to send. */ - private int messageSize; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** - * Should provide the name of the test case that this class implements. The exact names are defined in the - * interop testing spec. - * - * @return The name of the test case that this implements. - */ - public String getName() - { - log.debug("public String getName(): called"); - - return "TC5_PubSubMessageSize"; - } - - /** - * Determines whether the test invite that matched this test case is acceptable. - * - * @param inviteMessage The invitation to accept or reject. - * - * @return true to accept the invitation, false to reject it. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public boolean acceptInvite(Message inviteMessage) throws JMSException - { - log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); - - // All invites are acceptable. - return true; - } - - /** - * Assigns the role to be played by this test case. The test parameters are fully specified in the - * assignment message. When this method return the test case will be ready to execute. - * - * @param role The role to be played; sender or receivers. - * - * @param assignRoleMessage The role assingment message, contains the full test parameters. - * - * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Reset the message count for a new test. - messageCount = 0; - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); - messageSize = assignRoleMessage.getIntProperty("messageSize"); - int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); - String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); - - log.debug("numMessages = " + numMessages); - log.debug("numReceivers = " + numReceivers); - log.debug("sendKey = " + sendKey); - log.debug("role = " + role); - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Extract and retain the test parameters. - Destination sendDestination = session[0].createTopic(sendKey); - - producer = session[0].createProducer(sendDestination); - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); - - sendDestination = session[i].createTopic(sendKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - consumer.setMessageListener(this); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (Connection conn : connection) - { - conn.start(); - } - } - - /** - * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. - * - * @param numMessages The number of test messages to send. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - if (role.equals(Roles.SENDER)) - { - Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize); - - for (int i = 0; i < this.numMessages; i++) - { - producer.send(testMessage); - - // Increment the message count. - messageCount++; - } - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession to create the report message in. - * - * @return The report message. - * - * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. - */ - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (Connection conn : connection) - { - conn.close(); - } - - // Generate a report message containing the count of the number of messages passed. - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - report.setIntProperty("MESSAGE_COUNT", messageCount); - - return report; - } - - /** - * Counts incoming test messages. - * - * @param message The incoming test message. - */ - public void onMessage(Message message) - { - log.debug("public void onMessage(Message message = " + message + "): called"); - - // Increment the message count. - messageCount++; - } -} +/* + * + * 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.interop.clienttestcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; + +import javax.jms.*; + +/** + * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route + * on the default topic exchange, using the specified number of receivers connections, and the specified message size. + * Produces reports on the actual number of messages sent/received. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Supply the name of the test case that this implements. + *
      Accept/Reject invites based on test parameters. + *
      Adapt to assigned roles. + *
      Send required number of test messages using pub/sub. + *
      Generate test reports. + *
      + */ +public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class); + + /** Holds the count of test messages received. */ + private int messageCount; + + /** The role to be played by the test. */ + private Roles role; + + /** The number of test messages to send. */ + private int numMessages; + + /** The size of the test messages to send. */ + private int messageSize; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the + * interop testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "TC5_PubSubMessageSize"; + } + + /** + * Determines whether the test invite that matched this test case is acceptable. + * + * @param inviteMessage The invitation to accept or reject. + * + * @return true to accept the invitation, false to reject it. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public boolean acceptInvite(Message inviteMessage) throws JMSException + { + log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called"); + + // All invites are acceptable. + return true; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the + * assignment message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Reset the message count for a new test. + messageCount = 0; + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES"); + messageSize = assignRoleMessage.getIntProperty("messageSize"); + int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS"); + String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY"); + + log.debug("numMessages = " + numMessages); + log.debug("numReceivers = " + numReceivers); + log.debug("sendKey = " + sendKey); + log.debug("role = " + role); + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Extract and retain the test parameters. + Destination sendDestination = session[0].createTopic(sendKey); + + producer = session[0].createProducer(sendDestination); + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + + sendDestination = session[i].createTopic(sendKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + consumer.setMessageListener(this); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (Connection conn : connection) + { + conn.start(); + } + } + + /** + * Performs the test case actions. Returning from here, indicates that the sending role has completed its test. + * + * @param numMessages The number of test messages to send. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + if (role.equals(Roles.SENDER)) + { + Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize); + + for (int i = 0; i < this.numMessages; i++) + { + producer.send(testMessage); + + // Increment the message count. + messageCount++; + } + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (Connection conn : connection) + { + conn.close(); + } + + // Generate a report message containing the count of the number of messages passed. + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + report.setIntProperty("MESSAGE_COUNT", messageCount); + + return report; + } + + /** + * Counts incoming test messages. + * + * @param message The incoming test message. + */ + public void onMessage(Message message) + { + log.debug("public void onMessage(Message message = " + message + "): called"); + + // Increment the message count. + messageCount++; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java index 60cd9f47f3..a2e4a00aa6 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java @@ -1,84 +1,84 @@ -/* - * - * 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.interop.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, - * and gets some dummy test reports from them, in order to check that the test framework itself is operational. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Exercises the interop testing framework without actually sending any test messages. - * {@link FrameworkBaseCase} - *
      - */ -public class InteropTestCase1DummyRun extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase1DummyRun(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testDummyRun() throws Exception - { - log.debug("public void testDummyRun(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC1_DummyRun"); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC1_DummyRun"; - } -} +/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles, + * and gets some dummy test reports from them, in order to check that the test framework itself is operational. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Exercises the interop testing framework without actually sending any test messages. + * {@link FrameworkBaseCase} + *
      + */ +public class InteropTestCase1DummyRun extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase1DummyRun(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testDummyRun() throws Exception + { + log.debug("public void testDummyRun(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC1_DummyRun"); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC1_DummyRun"; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java index 8983249daa..6d6515f1fd 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java @@ -1,90 +1,90 @@ -/* - * - * 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.interop.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 - * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted - * successfully. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} - *
      - */ -public class InteropTestCase2BasicP2P extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase2BasicP2P(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicP2P() throws Exception - { - log.debug("public void testBasicP2P(): called"); - - Properties testConfig = new Properties(); - testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); - testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC2_BasicP2P"; - } -} +/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50 + * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted + * successfully. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} + *
      + */ +public class InteropTestCase2BasicP2P extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase2BasicP2P(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicP2P() throws Exception + { + log.debug("public void testBasicP2P(): called"); + + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC2_BasicP2P"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC2_BasicP2P"; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java index 6e87c3e3ee..2faca91e73 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java @@ -1,88 +1,88 @@ -/* - * - * 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.interop.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} - *
      - */ -public class InteropTestCase3BasicPubSub extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase3BasicPubSub(String name) - { - super(name); - } - - /** - * Performs the basic P2P test case, "Test Case 2" in the specification. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testBasicPubSub(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC3_BasicPubSub"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - - /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); - Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, - messagesReceived);*/ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC3_BasicPubSub"; - } -} +/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} + *
      + */ +public class InteropTestCase3BasicPubSub extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase3BasicPubSub(String name) + { + super(name); + } + + /** + * Performs the basic P2P test case, "Test Case 2" in the specification. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testBasicPubSub(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC3_BasicPubSub"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + + /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent); + Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5, + messagesReceived);*/ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC3_BasicPubSub"; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java index c3f450ec42..2d64f2b805 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java @@ -1,193 +1,193 @@ -/* - * - * 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.interop.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50 - * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all - * the test messages were transmitted successfully. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} - *
      - */ -public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase4P2PMessageSize(String name) - { - super(name); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize0K() throws Exception - { - runTestForMessagesOfSize(0); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize63K() throws Exception - { - runTestForMessagesOfSize(63 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize64K() throws Exception - { - runTestForMessagesOfSize(64 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize65K() throws Exception - { - runTestForMessagesOfSize(65 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize127K() throws Exception - { - runTestForMessagesOfSize(127 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize128K() throws Exception - { - runTestForMessagesOfSize(128 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize129K() throws Exception - { - runTestForMessagesOfSize(129 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize255K() throws Exception - { - runTestForMessagesOfSize(255 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize256K() throws Exception - { - runTestForMessagesOfSize(256 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testP2PMessageSize257K() throws Exception - { - runTestForMessagesOfSize(257 * 1024); - } - - /** - * Sends 50 test messages of the specified size, and asserts that all were received. - * - * @param size The size of the messages to send in bytes. - */ - private void runTestForMessagesOfSize(int size) - { - Properties testConfig = new Properties(); - testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize"); - testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); - testConfig.put("P2P_NUM_MESSAGES", 50); - testConfig.put("messageSize", size); - - /*Message[] reports =*/ - getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /* - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC4_P2PMessageSize"; - } -} +/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50 + * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all + * the test messages were transmitted successfully. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Setup p2p test parameters and compare with test output. {@link FrameworkBaseCase} + *
      + */ +public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase4P2PMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testP2PMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize"); + testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue"); + testConfig.put("P2P_NUM_MESSAGES", 50); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC4_P2PMessageSize"; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java index 86a0a60ea4..23d33fc115 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java @@ -1,193 +1,193 @@ -/* - * - * 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.interop.testcases; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import java.util.Properties; - -/** - * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10 - * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both - * indicate that all the test messages were transmitted successfully. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} - *
      - */ -public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class); - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public InteropTestCase5PubSubMessageSize(String name) - { - super(name); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize0K() throws Exception - { - runTestForMessagesOfSize(0); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize63K() throws Exception - { - runTestForMessagesOfSize(63 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize64K() throws Exception - { - runTestForMessagesOfSize(64 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize65K() throws Exception - { - runTestForMessagesOfSize(65 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize127K() throws Exception - { - runTestForMessagesOfSize(127 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize128K() throws Exception - { - runTestForMessagesOfSize(128 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize129K() throws Exception - { - runTestForMessagesOfSize(129 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize255K() throws Exception - { - runTestForMessagesOfSize(255 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize256K() throws Exception - { - runTestForMessagesOfSize(256 * 1024); - } - - /** - * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testPubSubMessageSize257K() throws Exception - { - runTestForMessagesOfSize(257 * 1024); - } - - /** - * Sends 50 test messages of the specified size, and asserts that all were received. - * - * @param size The size of the messages to send in bytes. - */ - private void runTestForMessagesOfSize(int size) - { - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "TC5_PubSubMessageSize"); - testConfig.put("PUBSUB_KEY", "tc3route"); - testConfig.put("PUBSUB_NUM_MESSAGES", 10); - testConfig.put("PUBSUB_NUM_RECEIVERS", 5); - testConfig.put("messageSize", size); - - /*Message[] reports =*/ - getCircuitFactory().sequenceTest(null, null, testConfig); - - // Compare sender and receivers reports. - /* - int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); - int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); - - Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); - Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as defined in the - * interop testing specification. For example the method "testP2P" might map onto the interop test case name - * "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "TC5_PubSubMessageSize"; - } -} +/* + * + * 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.interop.testcases; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import java.util.Properties; + +/** + * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10 + * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both + * indicate that all the test messages were transmitted successfully. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Setup pub/sub test parameters and compare with test output. {@link FrameworkBaseCase} + *
      + */ +public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class); + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public InteropTestCase5PubSubMessageSize(String name) + { + super(name); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize0K() throws Exception + { + runTestForMessagesOfSize(0); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize63K() throws Exception + { + runTestForMessagesOfSize(63 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize64K() throws Exception + { + runTestForMessagesOfSize(64 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize65K() throws Exception + { + runTestForMessagesOfSize(65 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize127K() throws Exception + { + runTestForMessagesOfSize(127 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize128K() throws Exception + { + runTestForMessagesOfSize(128 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize129K() throws Exception + { + runTestForMessagesOfSize(129 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize255K() throws Exception + { + runTestForMessagesOfSize(255 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize256K() throws Exception + { + runTestForMessagesOfSize(256 * 1024); + } + + /** + * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testPubSubMessageSize257K() throws Exception + { + runTestForMessagesOfSize(257 * 1024); + } + + /** + * Sends 50 test messages of the specified size, and asserts that all were received. + * + * @param size The size of the messages to send in bytes. + */ + private void runTestForMessagesOfSize(int size) + { + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "TC5_PubSubMessageSize"); + testConfig.put("PUBSUB_KEY", "tc3route"); + testConfig.put("PUBSUB_NUM_MESSAGES", 10); + testConfig.put("PUBSUB_NUM_RECEIVERS", 5); + testConfig.put("messageSize", size); + + /*Message[] reports =*/ + getCircuitFactory().sequenceTest(null, null, testConfig); + + // Compare sender and receivers reports. + /* + int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT"); + int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT"); + + Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent); + Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as defined in the + * interop testing specification. For example the method "testP2P" might map onto the interop test case name + * "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "TC5_PubSubMessageSize"; + } +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java index 5ed502287d..63e2c75509 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java @@ -1,906 +1,906 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; -import org.apache.qpid.test.framework.distributedtesting.TestClient; -import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; -import org.apache.qpid.test.framework.TestUtils; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TextMessage; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -/** - * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the - * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of - * messages sent/received. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Supply the name of the test case that this implements. - *
      Accept/Reject invites based on test parameters. - *
      Adapt to assigned roles. - *
      Send required number of test messages using pub/sub.
      Generate test reports. - *
      - */ -public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); - - /** Used to log to the console. */ - private static final Logger console = Logger.getLogger("SustainedTest"); - - /** The role to be played by the test. */ - private Roles role; - - /** The number of receivers connection to use. */ - private int numReceivers; - - /** The routing key to send them to on the default direct exchange. */ - private Destination sendDestination; - - /** The routing key to send updates to on the default direct exchange. */ - private Destination sendUpdateDestination; - - /** The connections to send/receive the test messages on. */ - private Connection[] connection; - - /** The sessions to send/receive the test messages on. */ - private Session[] session; - - /** The producer to send the test messages with. */ - MessageProducer producer; - - /** Adapter that adjusts the send rate based on the updates from clients. */ - SustainedRateAdapter _rateAdapter; - - /** */ - int _batchSize; - - private static final long TEN_MILLI_SEC = 10000000; - private static final int DEBUG_LOG_UPATE_INTERVAL = 10; - private static final int LOG_UPATE_INTERVAL = 10; - private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); - - /** - * Should provide the name of the test case that this class implements. The exact names are defined in the interop - * testing spec. - * - * @return The name of the test case that this implements. - */ - public String getName() - { - log.debug("public String getName(): called"); - - return "Perf_SustainedPubSub"; - } - - /** - * Assigns the role to be played by this test case. The test parameters are fully specified in the assignment - * message. When this method return the test case will be ready to execute. - * - * @param role The role to be played; sender or receivers. - * @param assignRoleMessage The role assingment message, contains the full test parameters. - * - * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. - */ - public void assignRole(Roles role, Message assignRoleMessage) throws JMSException - { - log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage - + "): called"); - - // Take note of the role to be played. - this.role = role; - - // Extract and retain the test parameters. - numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); - _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); - String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); - String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); - int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); - String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); - - if (log.isDebugEnabled()) - { - log.debug("numReceivers = " + numReceivers); - log.debug("_batchSize = " + _batchSize); - log.debug("ackMode = " + ackMode); - log.debug("sendKey = " + sendKey); - log.debug("sendUpdateKey = " + sendUpdateKey); - log.debug("role = " + role); - } - - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - console.info("Creating Sender"); - // Create a new connection to pass the test messages on. - connection = new Connection[1]; - session = new Session[1]; - - connection[0] = TestUtils.createConnection(TestClient.testContextProperties); - session[0] = connection[0].createSession(false, ackMode); - - // Extract and retain the test parameters. - sendDestination = session[0].createTopic(sendKey); - - connection[0].setExceptionListener(this); - - producer = session[0].createProducer(sendDestination); - - sendUpdateDestination = session[0].createTopic(sendUpdateKey); - MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); - - _rateAdapter = new SustainedRateAdapter(this); - updateConsumer.setMessageListener(_rateAdapter); - - break; - - // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number - // of receivers connections. - case RECEIVER: - console.info("Creating Receiver"); - // Create the required number of receivers connections. - connection = new Connection[numReceivers]; - session = new Session[numReceivers]; - - for (int i = 0; i < numReceivers; i++) - { - connection[i] = TestUtils.createConnection(TestClient.testContextProperties); - session[i] = connection[i].createSession(false, ackMode); - - sendDestination = session[i].createTopic(sendKey); - - sendUpdateDestination = session[i].createTopic(sendUpdateKey); - - MessageConsumer consumer = session[i].createConsumer(sendDestination); - - consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], - sendUpdateDestination)); - } - - break; - } - - // Start all the connection dispatcher threads running. - for (int i = 0; i < connection.length; i++) - { - connection[i].start(); - } - } - - /** Performs the test case actions. - * @param numMessages*/ - public void start(int numMessages) throws JMSException - { - log.debug("public void start(): called"); - - // Check that the sender role is being performed. - switch (role) - { - // Check if the sender role is being assigned, and set up a single message producer if so. - case SENDER: - _rateAdapter.run(); - break; - case RECEIVER: - - } - - // return from here when you have finished the test.. this will signal the controller and - } - - public void terminate() throws JMSException, InterruptedException - { - if (_rateAdapter != null) - { - _rateAdapter.stop(); - } - } - - /** - * Gets a report on the actions performed by the test case in its assigned role. - * - * @param session The controlSession to create the report message in. - * - * @return The report message. - * - * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. - */ - public Message getReport(Session session) throws JMSException - { - log.debug("public Message getReport(Session controlSession): called"); - - // Close the test connections. - for (int i = 0; i < connection.length; i++) - { - connection[i].close(); - } - - Message report = session.createMessage(); - report.setStringProperty("CONTROL_TYPE", "REPORT"); - - return report; - } - - public void onException(JMSException jmsException) - { - Exception linked = jmsException.getLinkedException(); - - if (linked != null) - { - if (log.isDebugEnabled()) - { - log.debug("Linked Exception:" + linked); - } - - if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) - { - if (log.isDebugEnabled()) - { - if (linked instanceof AMQNoConsumersException) - { - log.warn("No clients currently available for message:" - + ((AMQNoConsumersException) linked).getUndeliveredMessage()); - } - else - { - log.warn("No route for message"); - } - } - - // Tell the rate adapter that there are no clients ready yet - _rateAdapter.NO_CLIENTS = true; - } - } - else - { - log.warn("Exception:" + linked); - } - } - - /** - * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and - * 'end' messages. - */ - class SustainedListener implements MessageListener - { - /** Number of messages received */ - private long _received = 0; - /** The number of messages in the batch */ - private int _batchSize = 0; - /** Record of the when the 'start' messagse was sen */ - private Long _startTime; - /** Message producer to use to send reports */ - MessageProducer _updater; - /** Session to create the report message on */ - Session _session; - /** Record of the client ID used for this SustainedListnener */ - String _client; - - /** - * Main Constructor - * - * @param clientname The _client id used to identify this connection. - * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to - * control the interval between sending reports. - * @param session The controlSession used for communication. - * @param sendDestination The destination that update reports should be sent to. - * - * @throws JMSException My occur if creatingthe Producer fails - */ - public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) - throws JMSException - { - _batchSize = batchSize; - _client = clientname; - _session = session; - _updater = session.createProducer(sendDestination); - } - - public void onMessage(Message message) - { - if (log.isDebugEnabled()) - { - log.debug("Message " + _received + "received in listener"); - } - - if (message instanceof TextMessage) - { - try - { - _received++; - if (((TextMessage) message).getText().equals("start")) - { - log.debug("Starting Batch"); - _startTime = System.nanoTime(); - } - else if (((TextMessage) message).getText().equals("end")) - { - if (_startTime != null) - { - long currentTime = System.nanoTime(); - sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); - log.debug("End Batch"); - } - } - } - catch (JMSException e) - { - // ignore error - } - } - - } - - /** - * sendStatus creates and sends the report back to the publisher - * - * @param time taken for the the last batch - * @param received Total number of messages received. - * @param batchNumber the batch number - * @throws JMSException if an error occurs during the send - */ - private void sendStatus(long time, long received, int batchNumber) throws JMSException - { - Message updateMessage = _session.createTextMessage("update"); - updateMessage.setStringProperty("CLIENT_ID", ":" + _client); - updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); - updateMessage.setLongProperty("RECEIVED", received); - updateMessage.setIntProperty("BATCH", batchNumber); - updateMessage.setLongProperty("DURATION", time); - - if (log.isInfoEnabled()) - { - log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - // Output on the main console.info the details of this batch - if ((batchNumber % 10) == 0) - { - console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received - + " BATCH:" + batchNumber + " DURATION:" + time); - } - - _updater.send(updateMessage); - } - } - - /** - * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second - * that are sent through the test system. - * - * By keeping a record of the messages recevied and the average time taken to process the batch size can be - * calculated and so the delay can be adjusted to maintain that rate. - * - * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no - * messages in the batch. Otherwise the delay is used at the end of the batch. - */ - class SustainedRateAdapter implements MessageListener, Runnable - { - private SustainedClientTestCase _client; - private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting - private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) - private volatile long _delay; // in nanos - private long _sent; - private Map _slowClients = new HashMap(); - private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms - private static final long NO_CLIENT_SLEEP = 1000; // 1s - private volatile boolean NO_CLIENTS = true; - private int _delayShifting; - private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); - private boolean _warmedup = false; - private static final long EXPECTED_TIME_PER_BATCH = 100000L; - private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); - - SustainedRateAdapter(SustainedClientTestCase client) - { - _client = client; - } - - public void onMessage(Message message) - { - if (log.isDebugEnabled()) - { - log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); - } - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - - // Check if the message is a test invite. - if ("UPDATE".equals(controlType)) - { - NO_CLIENTS = false; - long duration = message.getLongProperty("DURATION"); - long totalReceived = message.getLongProperty("RECEIVED"); - String client = message.getStringProperty("CLIENT_ID"); - int batchNumber = message.getIntProperty("BATCH"); - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" - + batchNumber + " DURATION:" + duration); - } - - recordSlow(client, totalReceived, batchNumber); - - adjustDelay(client, batchNumber, duration); - - // Warm up completes when: - // we haven't warmed up - // and the number of batches sent to each client is at least half of the required warmup batches - if (!_warmedup && (batchNumber >= _warmUpBatches)) - { - _warmedup = true; - _warmup.countDown(); - - } - } - } - catch (JMSException e) - { - // - } - } - - CountDownLatch _warmup = new CountDownLatch(1); - - int _numBatches = 10000; - - // long[] _timings = new long[_numBatches]; - private boolean _running = true; - - public void run() - { - console.info("Warming up"); - - doBatch(_warmUpBatches); - - try - { - // wait for warmup to complete. - _warmup.await(); - - // set delay to the average length of the batches - _delay = _totalDuration / _warmUpBatches / delays.size(); - - console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration - + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); - - _totalDuration = 0L; - _totalReceived = 0L; - _sent = 0L; - } - catch (InterruptedException e) - { - // - } - - doBatch(_numBatches); - - } - - private void doBatch(int batchSize) // long[] timings, - { - TextMessage testMessage = null; - try - { - testMessage = _client.session[0].createTextMessage("start"); - - for (int batch = 0; batch <= batchSize; batch++) - // while (_running) - { - long start = System.nanoTime(); - - testMessage.setText("start"); - testMessage.setIntProperty("BATCH", batch); - - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - testMessage.setText("test"); - // start at 2 so start and end count as part of batch - for (int m = 2; m < _batchSize; m++) - { - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - } - - testMessage.setText("end"); - _client.producer.send(testMessage); - _rateAdapter.sentMessage(); - - long end = System.nanoTime(); - - long sendtime = end - start; - - if (log.isDebugEnabled()) - { - log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); - } - - if ((batch % LOG_UPATE_INTERVAL) == 0) - { - console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); - } - - _rateAdapter.sleepBatch(); - - } - } - catch (JMSException e) - { - console.error("Runner ended"); - } - } - - private String status() - { - return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay - + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); - } - - private void sleepBatch() - { - if (checkForSlowClients()) - { // if there werwe slow clients we have already slept so don't sleep anymore again. - return; - } - - if (!SLEEP_PER_MESSAGE) - { - // per batch sleep.. if sleep is to small to spread over the batch. - if (_delay <= (TEN_MILLI_SEC * _batchSize)) - { - sleepLong(_delay); - } - else - { - log.info("Not sleeping _delay > ten*batch is:" + _delay); - } - } - } - - public void stop() - { - _running = false; - } - - Map delays = new HashMap(); - Long _totalReceived = 0L; - Long _totalDuration = 0L; - int _skipUpdate = 0; - - /** - * Adjust the delay for sending messages based on this update from the client - * - * @param client The client that send this update - * @param duration The time taken for the last batch of messagse - * @param batchNumber The reported batchnumber from the client - */ - private void adjustDelay(String client, int batchNumber, long duration) - { - // Retrieve the current total time taken for this client. - Long currentTime = delays.get(client); - - // Add the new duration time to this client - if (currentTime == null) - { - currentTime = duration; - } - else - { - currentTime += duration; - } - - delays.put(client, currentTime); - - long batchesSent = _sent / _batchSize; - - // ensure we don't divide by zero - if (batchesSent == 0) - { - batchesSent = 1L; - } - - _totalReceived += _batchSize; - _totalDuration += duration; - - // calculate average duration accross clients per batch - long averageDuration = _totalDuration / delays.size() / batchesSent; - - // calculate the difference between current send delay and average report delay - long diff = (duration) - averageDuration; - - if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " - + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " - + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " - + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); - } - - // if the averageDuration differs from the current by more than the specified variane then adjust delay. - if (Math.abs(diff) > _timeVariance) - { - - // if the the _delay is larger than the required duration to send report - // speed up - if (diff > TEN_MILLI_SEC) - { - _delay -= TEN_MILLI_SEC; - - if (_delay < 0) - { - _delay = 0; - log.info("Reset _delay to 0"); - delayStable(); - } - else - { - delayChanged(); - } - - } - else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance - { - // the report took longer - _delay += TEN_MILLI_SEC; - delayChanged(); - } - } - else - { - delayStable(); - } - - // If we have a consumer that is behind with the batches. - if ((batchesSent - batchNumber) > _batchVariance) - { - log.debug("Increasing _delay as sending more than receiving"); - - _delay += 2 * TEN_MILLI_SEC; - delayChanged(); - } - - } - - /** Reset the number of iterations before we say the delay has stabilised. */ - private void delayChanged() - { - _delayShifting = REPORTS_WITHOUT_CHANGE; - } - - /** - * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will - * output Delay stabilised - */ - private void delayStable() - { - _delayShifting--; - - if (_delayShifting < 0) - { - _delayShifting = 0; - console.debug("Delay stabilised:" + _delay); - } - } - - /** - * Checks that the client has received enough messages. If the client has fallen behind then they are put in the - * _slowClients lists which will increase the delay. - * - * @param client The client identifier to check - * @param received the number of messages received by that client - * @param batchNumber - */ - private void recordSlow(String client, long received, int batchNumber) - { - if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) - { - _slowClients.put(client, received); - } - else - { - _slowClients.remove(client); - } - } - - /** Incrment the number of sent messages and then sleep, if required. */ - public void sentMessage() - { - - _sent++; - - if (_delay > (TEN_MILLI_SEC * _batchSize)) - { - long batchDelay = _delay / _batchSize; - // less than 10ms sleep doesn't always work. - // _delay is in nano seconds - // if (batchDelay < (TEN_MILLI_SEC)) - // { - // sleep(0, (int) batchDelay); - // } - // else - { - // if (batchDelay < 30000000000L) - { - sleepLong(batchDelay); - } - } - } - else - { - if (SLEEP_PER_MESSAGE && (_delay > 0)) - { - sleepLong(_delay / _batchSize); - } - } - } - - /** - * Check at the end of each batch and pause sending messages to allow slow clients to catch up. - * - * @return true if there were slow clients that caught up. - */ - private boolean checkForSlowClients() - { - // This will allways be true as we are running this at the end of each batchSize - // if (_sent % _batchSize == 0) - { - // Cause test to pause when we have slow - if (!_slowClients.isEmpty() || NO_CLIENTS) - { - - while (!_slowClients.isEmpty()) - { - if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) - { - String clients = ""; - Iterator it = _slowClients.keySet().iterator(); - while (it.hasNext()) - { - clients += it.next(); - if (it.hasNext()) - { - clients += ", "; - } - } - - log.info("Pausing for slow clients:" + clients); - } - - if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) - { - console.debug(_slowClients.size() + " slow clients."); - } - - sleep(PAUSE_SLEEP); - } - - if (NO_CLIENTS) - { - sleep(NO_CLIENT_SLEEP); - } - - log.debug("Continuing"); - - return true; - } - else - { - if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) - { - console.info("Total Delay :" + _delay + " " - + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); - } - } - - } - - return false; - } - - /** - * Sleep normally takes micro-seconds this allows the use of a nano-second value. - * - * @param delay nanoseconds to sleep for. - */ - private void sleepLong(long delay) - { - sleep(delay / 1000000, (int) (delay % 1000000)); - } - - /** - * Sleep for the specified micro-seconds. - * @param sleep microseconds to sleep for. - */ - private void sleep(long sleep) - { - sleep(sleep, 0); - } - - /** - * Perform the sleep , swallowing any InteruptException. - * - * NOTE: If a sleep request is > 10s then reset only sleep for 5s - * - * @param milli to sleep for - * @param nano sub miliseconds to sleep for - */ - private void sleep(long milli, int nano) - { - try - { - log.debug("Sleep:" + milli + ":" + nano); - if (milli > 10000) - { - - if (_delay == milli) - { - _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) - + "s. Reset _totalDuration:" + _totalDuration); - } - else - { - log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); - } - - milli = 5000; - } - - Thread.sleep(milli, nano); - } - catch (InterruptedException e) - { - // - } - } - - public void setClient(SustainedClientTestCase client) - { - _client = client; - } - } - -} +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.distributedtesting.TestClient; +import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub; +import org.apache.qpid.test.framework.TestUtils; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +/** + * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the + * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of + * messages sent/received. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Supply the name of the test case that this implements. + *
      Accept/Reject invites based on test parameters. + *
      Adapt to assigned roles. + *
      Send required number of test messages using pub/sub.
      Generate test reports. + *
      + */ +public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(SustainedClientTestCase.class); + + /** Used to log to the console. */ + private static final Logger console = Logger.getLogger("SustainedTest"); + + /** The role to be played by the test. */ + private Roles role; + + /** The number of receivers connection to use. */ + private int numReceivers; + + /** The routing key to send them to on the default direct exchange. */ + private Destination sendDestination; + + /** The routing key to send updates to on the default direct exchange. */ + private Destination sendUpdateDestination; + + /** The connections to send/receive the test messages on. */ + private Connection[] connection; + + /** The sessions to send/receive the test messages on. */ + private Session[] session; + + /** The producer to send the test messages with. */ + MessageProducer producer; + + /** Adapter that adjusts the send rate based on the updates from clients. */ + SustainedRateAdapter _rateAdapter; + + /** */ + int _batchSize; + + private static final long TEN_MILLI_SEC = 10000000; + private static final int DEBUG_LOG_UPATE_INTERVAL = 10; + private static final int LOG_UPATE_INTERVAL = 10; + private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage"); + + /** + * Should provide the name of the test case that this class implements. The exact names are defined in the interop + * testing spec. + * + * @return The name of the test case that this implements. + */ + public String getName() + { + log.debug("public String getName(): called"); + + return "Perf_SustainedPubSub"; + } + + /** + * Assigns the role to be played by this test case. The test parameters are fully specified in the assignment + * message. When this method return the test case will be ready to execute. + * + * @param role The role to be played; sender or receivers. + * @param assignRoleMessage The role assingment message, contains the full test parameters. + * + * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through. + */ + public void assignRole(Roles role, Message assignRoleMessage) throws JMSException + { + log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage + + "): called"); + + // Take note of the role to be played. + this.role = role; + + // Extract and retain the test parameters. + numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS"); + _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL"); + String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY"); + String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY"); + int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE"); + String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME"); + + if (log.isDebugEnabled()) + { + log.debug("numReceivers = " + numReceivers); + log.debug("_batchSize = " + _batchSize); + log.debug("ackMode = " + ackMode); + log.debug("sendKey = " + sendKey); + log.debug("sendUpdateKey = " + sendUpdateKey); + log.debug("role = " + role); + } + + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + console.info("Creating Sender"); + // Create a new connection to pass the test messages on. + connection = new Connection[1]; + session = new Session[1]; + + connection[0] = TestUtils.createConnection(TestClient.testContextProperties); + session[0] = connection[0].createSession(false, ackMode); + + // Extract and retain the test parameters. + sendDestination = session[0].createTopic(sendKey); + + connection[0].setExceptionListener(this); + + producer = session[0].createProducer(sendDestination); + + sendUpdateDestination = session[0].createTopic(sendUpdateKey); + MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination); + + _rateAdapter = new SustainedRateAdapter(this); + updateConsumer.setMessageListener(_rateAdapter); + + break; + + // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number + // of receivers connections. + case RECEIVER: + console.info("Creating Receiver"); + // Create the required number of receivers connections. + connection = new Connection[numReceivers]; + session = new Session[numReceivers]; + + for (int i = 0; i < numReceivers; i++) + { + connection[i] = TestUtils.createConnection(TestClient.testContextProperties); + session[i] = connection[i].createSession(false, ackMode); + + sendDestination = session[i].createTopic(sendKey); + + sendUpdateDestination = session[i].createTopic(sendUpdateKey); + + MessageConsumer consumer = session[i].createConsumer(sendDestination); + + consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i], + sendUpdateDestination)); + } + + break; + } + + // Start all the connection dispatcher threads running. + for (int i = 0; i < connection.length; i++) + { + connection[i].start(); + } + } + + /** Performs the test case actions. + * @param numMessages*/ + public void start(int numMessages) throws JMSException + { + log.debug("public void start(): called"); + + // Check that the sender role is being performed. + switch (role) + { + // Check if the sender role is being assigned, and set up a single message producer if so. + case SENDER: + _rateAdapter.run(); + break; + case RECEIVER: + + } + + // return from here when you have finished the test.. this will signal the controller and + } + + public void terminate() throws JMSException, InterruptedException + { + if (_rateAdapter != null) + { + _rateAdapter.stop(); + } + } + + /** + * Gets a report on the actions performed by the test case in its assigned role. + * + * @param session The controlSession to create the report message in. + * + * @return The report message. + * + * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through. + */ + public Message getReport(Session session) throws JMSException + { + log.debug("public Message getReport(Session controlSession): called"); + + // Close the test connections. + for (int i = 0; i < connection.length; i++) + { + connection[i].close(); + } + + Message report = session.createMessage(); + report.setStringProperty("CONTROL_TYPE", "REPORT"); + + return report; + } + + public void onException(JMSException jmsException) + { + Exception linked = jmsException.getLinkedException(); + + if (linked != null) + { + if (log.isDebugEnabled()) + { + log.debug("Linked Exception:" + linked); + } + + if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException)) + { + if (log.isDebugEnabled()) + { + if (linked instanceof AMQNoConsumersException) + { + log.warn("No clients currently available for message:" + + ((AMQNoConsumersException) linked).getUndeliveredMessage()); + } + else + { + log.warn("No route for message"); + } + } + + // Tell the rate adapter that there are no clients ready yet + _rateAdapter.NO_CLIENTS = true; + } + } + else + { + log.warn("Exception:" + linked); + } + } + + /** + * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and + * 'end' messages. + */ + class SustainedListener implements MessageListener + { + /** Number of messages received */ + private long _received = 0; + /** The number of messages in the batch */ + private int _batchSize = 0; + /** Record of the when the 'start' messagse was sen */ + private Long _startTime; + /** Message producer to use to send reports */ + MessageProducer _updater; + /** Session to create the report message on */ + Session _session; + /** Record of the client ID used for this SustainedListnener */ + String _client; + + /** + * Main Constructor + * + * @param clientname The _client id used to identify this connection. + * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to + * control the interval between sending reports. + * @param session The controlSession used for communication. + * @param sendDestination The destination that update reports should be sent to. + * + * @throws JMSException My occur if creatingthe Producer fails + */ + public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination) + throws JMSException + { + _batchSize = batchSize; + _client = clientname; + _session = session; + _updater = session.createProducer(sendDestination); + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("Message " + _received + "received in listener"); + } + + if (message instanceof TextMessage) + { + try + { + _received++; + if (((TextMessage) message).getText().equals("start")) + { + log.debug("Starting Batch"); + _startTime = System.nanoTime(); + } + else if (((TextMessage) message).getText().equals("end")) + { + if (_startTime != null) + { + long currentTime = System.nanoTime(); + sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH")); + log.debug("End Batch"); + } + } + } + catch (JMSException e) + { + // ignore error + } + } + + } + + /** + * sendStatus creates and sends the report back to the publisher + * + * @param time taken for the the last batch + * @param received Total number of messages received. + * @param batchNumber the batch number + * @throws JMSException if an error occurs during the send + */ + private void sendStatus(long time, long received, int batchNumber) throws JMSException + { + Message updateMessage = _session.createTextMessage("update"); + updateMessage.setStringProperty("CLIENT_ID", ":" + _client); + updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE"); + updateMessage.setLongProperty("RECEIVED", received); + updateMessage.setIntProperty("BATCH", batchNumber); + updateMessage.setLongProperty("DURATION", time); + + if (log.isInfoEnabled()) + { + log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + // Output on the main console.info the details of this batch + if ((batchNumber % 10) == 0) + { + console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received + + " BATCH:" + batchNumber + " DURATION:" + time); + } + + _updater.send(updateMessage); + } + } + + /** + * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second + * that are sent through the test system. + * + * By keeping a record of the messages recevied and the average time taken to process the batch size can be + * calculated and so the delay can be adjusted to maintain that rate. + * + * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no + * messages in the batch. Otherwise the delay is used at the end of the batch. + */ + class SustainedRateAdapter implements MessageListener, Runnable + { + private SustainedClientTestCase _client; + private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting + private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms) + private volatile long _delay; // in nanos + private long _sent; + private Map _slowClients = new HashMap(); + private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms + private static final long NO_CLIENT_SLEEP = 1000; // 1s + private volatile boolean NO_CLIENTS = true; + private int _delayShifting; + private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5); + private boolean _warmedup = false; + private static final long EXPECTED_TIME_PER_BATCH = 100000L; + private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10); + + SustainedRateAdapter(SustainedClientTestCase client) + { + _client = client; + } + + public void onMessage(Message message) + { + if (log.isDebugEnabled()) + { + log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called"); + } + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + + // Check if the message is a test invite. + if ("UPDATE".equals(controlType)) + { + NO_CLIENTS = false; + long duration = message.getLongProperty("DURATION"); + long totalReceived = message.getLongProperty("RECEIVED"); + String client = message.getStringProperty("CLIENT_ID"); + int batchNumber = message.getIntProperty("BATCH"); + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:" + + batchNumber + " DURATION:" + duration); + } + + recordSlow(client, totalReceived, batchNumber); + + adjustDelay(client, batchNumber, duration); + + // Warm up completes when: + // we haven't warmed up + // and the number of batches sent to each client is at least half of the required warmup batches + if (!_warmedup && (batchNumber >= _warmUpBatches)) + { + _warmedup = true; + _warmup.countDown(); + + } + } + } + catch (JMSException e) + { + // + } + } + + CountDownLatch _warmup = new CountDownLatch(1); + + int _numBatches = 10000; + + // long[] _timings = new long[_numBatches]; + private boolean _running = true; + + public void run() + { + console.info("Warming up"); + + doBatch(_warmUpBatches); + + try + { + // wait for warmup to complete. + _warmup.await(); + + // set delay to the average length of the batches + _delay = _totalDuration / _warmUpBatches / delays.size(); + + console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration + + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size()); + + _totalDuration = 0L; + _totalReceived = 0L; + _sent = 0L; + } + catch (InterruptedException e) + { + // + } + + doBatch(_numBatches); + + } + + private void doBatch(int batchSize) // long[] timings, + { + TextMessage testMessage = null; + try + { + testMessage = _client.session[0].createTextMessage("start"); + + for (int batch = 0; batch <= batchSize; batch++) + // while (_running) + { + long start = System.nanoTime(); + + testMessage.setText("start"); + testMessage.setIntProperty("BATCH", batch); + + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + testMessage.setText("test"); + // start at 2 so start and end count as part of batch + for (int m = 2; m < _batchSize; m++) + { + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + } + + testMessage.setText("end"); + _client.producer.send(testMessage); + _rateAdapter.sentMessage(); + + long end = System.nanoTime(); + + long sendtime = end - start; + + if (log.isDebugEnabled()) + { + log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]); + } + + if ((batch % LOG_UPATE_INTERVAL) == 0) + { + console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status()); + } + + _rateAdapter.sleepBatch(); + + } + } + catch (JMSException e) + { + console.error("Runner ended"); + } + } + + private String status() + { + return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay + + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")); + } + + private void sleepBatch() + { + if (checkForSlowClients()) + { // if there werwe slow clients we have already slept so don't sleep anymore again. + return; + } + + if (!SLEEP_PER_MESSAGE) + { + // per batch sleep.. if sleep is to small to spread over the batch. + if (_delay <= (TEN_MILLI_SEC * _batchSize)) + { + sleepLong(_delay); + } + else + { + log.info("Not sleeping _delay > ten*batch is:" + _delay); + } + } + } + + public void stop() + { + _running = false; + } + + Map delays = new HashMap(); + Long _totalReceived = 0L; + Long _totalDuration = 0L; + int _skipUpdate = 0; + + /** + * Adjust the delay for sending messages based on this update from the client + * + * @param client The client that send this update + * @param duration The time taken for the last batch of messagse + * @param batchNumber The reported batchnumber from the client + */ + private void adjustDelay(String client, int batchNumber, long duration) + { + // Retrieve the current total time taken for this client. + Long currentTime = delays.get(client); + + // Add the new duration time to this client + if (currentTime == null) + { + currentTime = duration; + } + else + { + currentTime += duration; + } + + delays.put(client, currentTime); + + long batchesSent = _sent / _batchSize; + + // ensure we don't divide by zero + if (batchesSent == 0) + { + batchesSent = 1L; + } + + _totalReceived += _batchSize; + _totalDuration += duration; + + // calculate average duration accross clients per batch + long averageDuration = _totalDuration / delays.size() / batchesSent; + + // calculate the difference between current send delay and average report delay + long diff = (duration) - averageDuration; + + if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: " + + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: " + + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in " + + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"))); + } + + // if the averageDuration differs from the current by more than the specified variane then adjust delay. + if (Math.abs(diff) > _timeVariance) + { + + // if the the _delay is larger than the required duration to send report + // speed up + if (diff > TEN_MILLI_SEC) + { + _delay -= TEN_MILLI_SEC; + + if (_delay < 0) + { + _delay = 0; + log.info("Reset _delay to 0"); + delayStable(); + } + else + { + delayChanged(); + } + + } + else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance + { + // the report took longer + _delay += TEN_MILLI_SEC; + delayChanged(); + } + } + else + { + delayStable(); + } + + // If we have a consumer that is behind with the batches. + if ((batchesSent - batchNumber) > _batchVariance) + { + log.debug("Increasing _delay as sending more than receiving"); + + _delay += 2 * TEN_MILLI_SEC; + delayChanged(); + } + + } + + /** Reset the number of iterations before we say the delay has stabilised. */ + private void delayChanged() + { + _delayShifting = REPORTS_WITHOUT_CHANGE; + } + + /** + * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will + * output Delay stabilised + */ + private void delayStable() + { + _delayShifting--; + + if (_delayShifting < 0) + { + _delayShifting = 0; + console.debug("Delay stabilised:" + _delay); + } + } + + /** + * Checks that the client has received enough messages. If the client has fallen behind then they are put in the + * _slowClients lists which will increase the delay. + * + * @param client The client identifier to check + * @param received the number of messages received by that client + * @param batchNumber + */ + private void recordSlow(String client, long received, int batchNumber) + { + if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance) + { + _slowClients.put(client, received); + } + else + { + _slowClients.remove(client); + } + } + + /** Incrment the number of sent messages and then sleep, if required. */ + public void sentMessage() + { + + _sent++; + + if (_delay > (TEN_MILLI_SEC * _batchSize)) + { + long batchDelay = _delay / _batchSize; + // less than 10ms sleep doesn't always work. + // _delay is in nano seconds + // if (batchDelay < (TEN_MILLI_SEC)) + // { + // sleep(0, (int) batchDelay); + // } + // else + { + // if (batchDelay < 30000000000L) + { + sleepLong(batchDelay); + } + } + } + else + { + if (SLEEP_PER_MESSAGE && (_delay > 0)) + { + sleepLong(_delay / _batchSize); + } + } + } + + /** + * Check at the end of each batch and pause sending messages to allow slow clients to catch up. + * + * @return true if there were slow clients that caught up. + */ + private boolean checkForSlowClients() + { + // This will allways be true as we are running this at the end of each batchSize + // if (_sent % _batchSize == 0) + { + // Cause test to pause when we have slow + if (!_slowClients.isEmpty() || NO_CLIENTS) + { + + while (!_slowClients.isEmpty()) + { + if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0)) + { + String clients = ""; + Iterator it = _slowClients.keySet().iterator(); + while (it.hasNext()) + { + clients += it.next(); + if (it.hasNext()) + { + clients += ", "; + } + } + + log.info("Pausing for slow clients:" + clients); + } + + if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)) + { + console.debug(_slowClients.size() + " slow clients."); + } + + sleep(PAUSE_SLEEP); + } + + if (NO_CLIENTS) + { + sleep(NO_CLIENT_SLEEP); + } + + log.debug("Continuing"); + + return true; + } + else + { + if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0) + { + console.info("Total Delay :" + _delay + " " + + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")"))); + } + } + + } + + return false; + } + + /** + * Sleep normally takes micro-seconds this allows the use of a nano-second value. + * + * @param delay nanoseconds to sleep for. + */ + private void sleepLong(long delay) + { + sleep(delay / 1000000, (int) (delay % 1000000)); + } + + /** + * Sleep for the specified micro-seconds. + * @param sleep microseconds to sleep for. + */ + private void sleep(long sleep) + { + sleep(sleep, 0); + } + + /** + * Perform the sleep , swallowing any InteruptException. + * + * NOTE: If a sleep request is > 10s then reset only sleep for 5s + * + * @param milli to sleep for + * @param nano sub miliseconds to sleep for + */ + private void sleep(long milli, int nano) + { + try + { + log.debug("Sleep:" + milli + ":" + nano); + if (milli > 10000) + { + + if (_delay == milli) + { + _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH; + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + + "s. Reset _totalDuration:" + _totalDuration); + } + else + { + log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s"); + } + + milli = 5000; + } + + Thread.sleep(milli, nano); + } + catch (InterruptedException e) + { + // + } + } + + public void setClient(SustainedClientTestCase client) + { + _client = client; + } + } + +} diff --git a/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java index 5979a459ec..0077b4727a 100644 --- a/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java +++ b/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java @@ -1,126 +1,126 @@ -/* - * - * 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.sustained; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.test.framework.DropInTest; -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import javax.jms.JMSException; -import javax.jms.Message; - -import java.util.Properties; - -/** - * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one - * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast - * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - *
      - */ -public class SustainedTestCase extends FrameworkBaseCase implements DropInTest -{ - /** Used for debugging. */ - Logger log = Logger.getLogger(SustainedTestCase.class); - - /** Holds the root name of the topic on which to send the test messages. */ - private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; - - /** - * Creates a new coordinating test case with the specified name. - * - * @param name The test case name. - */ - public SustainedTestCase(String name) - { - super(name); - } - - /** - * Performs a single test run of the sustained test. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - public void testBasicPubSub() throws Exception - { - log.debug("public void testSinglePubSubCycle(): called"); - - Properties testConfig = new Properties(); - testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); - testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); - testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); - testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); - testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); - testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); - - log.info("Created Config: " + testConfig.entrySet().toArray()); - - getCircuitFactory().sequenceTest(null, null, testConfig); - } - - /** - * Accepts a late joining client into this test case. The client will be enlisted with a control message - * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: - * - *

      - *
      CLIENT_NAME A unique name for the new client. - *
      CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. - *
      - * - * @param message The late joiners join message. - * - * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. - */ - public void lateJoin(Message message) throws JMSException - { - throw new RuntimeException("Not implemented."); - /* - // Extract the joining clients details from its join request message. - TestClientDetails clientDetails = new TestClientDetails(); - clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); - clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); - - // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this - // method call, as it may have been called from an 'onMessage' method. - assignReceiverRole(clientDetails, new Properties(), false); - */ - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as known to the test - * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test - * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case - * name "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - return "Perf_SustainedPubSub"; - } -} +/* + * + * 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.sustained; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.DropInTest; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Properties; + +/** + * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one + * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast + * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + *
      + */ +public class SustainedTestCase extends FrameworkBaseCase implements DropInTest +{ + /** Used for debugging. */ + Logger log = Logger.getLogger(SustainedTestCase.class); + + /** Holds the root name of the topic on which to send the test messages. */ + private static final String SUSTAINED_KEY = "Perf_SustainedPubSub"; + + /** + * Creates a new coordinating test case with the specified name. + * + * @param name The test case name. + */ + public SustainedTestCase(String name) + { + super(name); + } + + /** + * Performs a single test run of the sustained test. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + public void testBasicPubSub() throws Exception + { + log.debug("public void testSinglePubSubCycle(): called"); + + Properties testConfig = new Properties(); + testConfig.put("TEST_NAME", "Perf_SustainedPubSub"); + testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY); + testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2)); + testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000)); + testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE"); + testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE)); + + log.info("Created Config: " + testConfig.entrySet().toArray()); + + getCircuitFactory().sequenceTest(null, null, testConfig); + } + + /** + * Accepts a late joining client into this test case. The client will be enlisted with a control message + * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields: + * + *

      + *
      CLIENT_NAME A unique name for the new client. + *
      CLIENT_PRIVATE_CONTROL_KEY The key for the route on which the client receives its control messages. + *
      + * + * @param message The late joiners join message. + * + * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed. + */ + public void lateJoin(Message message) throws JMSException + { + throw new RuntimeException("Not implemented."); + /* + // Extract the joining clients details from its join request message. + TestClientDetails clientDetails = new TestClientDetails(); + clientDetails.clientName = message.getStringProperty("CLIENT_NAME"); + clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY"); + + // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this + // method call, as it may have been called from an 'onMessage' method. + assignReceiverRole(clientDetails, new Properties(), false); + */ + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + return "Perf_SustainedPubSub"; + } +} diff --git a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java index dc74590f60..fdae005f00 100644 --- a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java +++ b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java @@ -1,113 +1,113 @@ -/* - * - * 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.junit.maven; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.HashSet; -import java.util.Set; - -/** - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - * - * @author Rupert Smith - * - * @noinspection CustomClassloader - */ -public class IsolatedClassLoader extends URLClassLoader -{ - - private static final URL[] EMPTY_URL_ARRAY = new URL[0]; - private ClassLoader parent = ClassLoader.getSystemClassLoader(); - - private Set urls = new HashSet(); - - private boolean childDelegation = true; - - public IsolatedClassLoader() - { - super(EMPTY_URL_ARRAY, null); - } - - public IsolatedClassLoader(ClassLoader parent, boolean childDelegation) - { - super(EMPTY_URL_ARRAY, parent); - - this.childDelegation = childDelegation; - } - - public IsolatedClassLoader(ClassLoader parent) - { - super(EMPTY_URL_ARRAY, parent); - } - - public void addURL(URL url) - { - // avoid duplicates - if (!urls.contains(url)) - { - super.addURL(url); - urls.add(url); - } - } - - public synchronized Class loadClass(String name) throws ClassNotFoundException - { - Class c; - - if (childDelegation) - { - c = findLoadedClass(name); - - ClassNotFoundException ex = null; - - if (c == null) - { - try - { - c = findClass(name); - } - catch (ClassNotFoundException e) - { - ex = e; - - if (parent != null) - { - c = parent.loadClass(name); - } - } - } - - if (c == null) - { - throw ex; - } - } - else - { - c = super.loadClass(name); - } - - return c; - } -} +/* + * + * 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.junit.maven; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashSet; +import java.util.Set; + +/** + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + * + * @author Rupert Smith + * + * @noinspection CustomClassloader + */ +public class IsolatedClassLoader extends URLClassLoader +{ + + private static final URL[] EMPTY_URL_ARRAY = new URL[0]; + private ClassLoader parent = ClassLoader.getSystemClassLoader(); + + private Set urls = new HashSet(); + + private boolean childDelegation = true; + + public IsolatedClassLoader() + { + super(EMPTY_URL_ARRAY, null); + } + + public IsolatedClassLoader(ClassLoader parent, boolean childDelegation) + { + super(EMPTY_URL_ARRAY, parent); + + this.childDelegation = childDelegation; + } + + public IsolatedClassLoader(ClassLoader parent) + { + super(EMPTY_URL_ARRAY, parent); + } + + public void addURL(URL url) + { + // avoid duplicates + if (!urls.contains(url)) + { + super.addURL(url); + urls.add(url); + } + } + + public synchronized Class loadClass(String name) throws ClassNotFoundException + { + Class c; + + if (childDelegation) + { + c = findLoadedClass(name); + + ClassNotFoundException ex = null; + + if (c == null) + { + try + { + c = findClass(name); + } + catch (ClassNotFoundException e) + { + ex = e; + + if (parent != null) + { + c = parent.loadClass(name); + } + } + } + + if (c == null) + { + throw ex; + } + } + else + { + c = super.loadClass(name); + } + + return c; + } +} diff --git a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java index 442529b609..36e594d9dc 100644 --- a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java +++ b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java @@ -1,274 +1,274 @@ -/* - * - * 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.junit.maven; - -import org.apache.maven.plugin.AbstractMojo; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.*; - -/** - * TKTestRunnerMojo is a JUnit test runner plugin for Maven 2. It is intended to be compatible with the surefire - * plugin (though not all features of that are yet implemented), with some extended capabilities. - * - *

      This plugin adds the ability to use different JUnit test runners, and to pass arbitrary options to them. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - * - * @author Rupert Smith - * - * @goal tktest - * @phase test - * @requiresDependencyResolution test - */ -public class TKTestRunnerMojo extends AbstractMojo -{ - private static final BitSet UNRESERVED = new BitSet(256); - - /** - * Set this to 'true' to bypass unit tests entirely. Its use is NOT RECOMMENDED, but quite convenient on occasion. - * - * @parameter expression="${maven.test.skip}" - */ - private boolean skip; - - /** - * The TKTest runner command lines. There are passed directly to the TKTestRunner main method. - * - * @parameter - */ - private Map commands = new LinkedHashMap(); - - /** - * The base directory of the project being tested. This can be obtained in your unit test by - * System.getProperty("basedir"). - * - * @parameter expression="${basedir}" - * @required - */ - private File basedir; - - /** - * The directory containing generated classes of the project being tested. - * - * @parameter expression="${project.build.outputDirectory}" - * @required - */ - private File classesDirectory; - - /** - * The directory containing generated test classes of the project being tested. - * - * @parameter expression="${project.build.testOutputDirectory}" - * @required - */ - private File testClassesDirectory; - - /** - * The classpath elements of the project being tested. - * - * @parameter expression="${project.testClasspathElements}" - * @required - * @readonly - */ - private List classpathElements; - - /** - * List of System properties to pass to the tests. - * - * @parameter - */ - private Properties systemProperties; - - /** - * Map of of plugin artifacts. - * - * @parameter expression="${plugin.artifactMap}" - * @required - * @readonly - */ - private Map pluginArtifactMap; - - /** - * Map of of project artifacts. - * - * @parameter expression="${project.artifactMap}" - * @required - * @readonly - */ - private Map projectArtifactMap; - - /** - * Option to specify the forking mode. Can be "never" (default), "once" or "always". - * "none" and "pertest" are also accepted for backwards compatibility. - * - * @parameter expression="${forkMode}" default-value="once" - */ - private String forkMode; - - /** - * Option to specify the jvm (or path to the java executable) to use with - * the forking options. For the default we will assume that java is in the path. - * - * @parameter expression="${jvm}" - * default-value="java" - */ - private String jvm; - - /** - * The test runner to use. - * - * @parameter - */ - private String testrunner; - - /** - * The additional properties to append to the test runner invocation command line. - * - * @parameter - */ - private Properties testrunnerproperties; - - /** - * The options to pass to all test runner invocation command lines. - * - * @parameter - */ - private String[] testrunneroptions; - - /** - * Implementation of the tktest goal. - */ - public void execute() - { - // Skip these tests if test skipping is turned on. - if (skip) - { - getLog().info("Skipping Tests."); - - return; - } - - // Log out the classpath if debugging is on. - if (getLog().isDebugEnabled()) - { - getLog().info("Test Classpath :"); - - for (Object classpathElement1 : classpathElements) - { - String classpathElement = (String) classpathElement1; - getLog().info(" " + classpathElement); - } - } - - try - { - // Create a class loader to load the test runner with. This also gets set as the context loader for this - // thread, so that all subsequent class loading activity by the test runner or the test code, has the - // test classes available to it. The system loader is set up for the maven build, which is why a seperate - // loader needs to be created; in order to inject the test dependencies into it. - ClassLoader runnerClassLoader = createClassLoader(classpathElements, ClassLoader.getSystemClassLoader(), true); - Thread.currentThread().setContextClassLoader(runnerClassLoader); - - // Load the test runner implementation that will be used to run the tests. - if ((testrunner == null) || "".equals(testrunner)) - { - testrunner = "org.apache.qpid.junit.extensions.TKTestRunner"; - } - - Class testRunnerClass = Class.forName(testrunner, false, runnerClassLoader); - Method run = testRunnerClass.getMethod("main", String[].class); - - // Concatenate all of the options to pass on the command line to the test runner. - String preOptions = ""; - - for (String option : testrunneroptions) - { - preOptions += option + " "; - } - - // Concatenate all of the additional properties as name=value pairs on the command line. - String nvPairs = ""; - - if (testrunnerproperties != null) - { - for (Object objKey : testrunnerproperties.keySet()) - { - String key = (String) objKey; - String value = testrunnerproperties.getProperty(key); - - nvPairs = key + "=" + value + " "; - } - } - - // Pass each of the test runner command lines in turn to the toolkit test runner. - // The command line is made up of the options, the command specific command line, then the trailing - // name=value pairs. - for (String testName : commands.keySet()) - { - String commandLine = preOptions + " " + commands.get(testName) + " " + nvPairs; - getLog().info("commandLine = " + commandLine); - - // Tokenize the command line on white space, into an array of string components. - String[] tokenizedCommandLine = commandLine.split("\\s+"); - - // Run the tests. - run.invoke(testRunnerClass, new Object[] { tokenizedCommandLine }); - } - } - catch (Exception e) - { - getLog().error("There was an exception: " + e.getMessage(), e); - } - } - - private static ClassLoader createClassLoader(List classPathUrls, ClassLoader parent, boolean childDelegation) - throws MalformedURLException - { - List urls = new ArrayList(); - - for (Iterator i = classPathUrls.iterator(); i.hasNext();) - { - String url = (String) i.next(); - - if (url != null) - { - File f = new File(url); - urls.add(f.toURL()); - } - } - - IsolatedClassLoader classLoader = new IsolatedClassLoader(parent, childDelegation); - - for (Iterator iter = urls.iterator(); iter.hasNext();) - { - URL url = (URL) iter.next(); - classLoader.addURL(url); - } - - return classLoader; - } -} +/* + * + * 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.junit.maven; + +import org.apache.maven.plugin.AbstractMojo; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; + +/** + * TKTestRunnerMojo is a JUnit test runner plugin for Maven 2. It is intended to be compatible with the surefire + * plugin (though not all features of that are yet implemented), with some extended capabilities. + * + *

      This plugin adds the ability to use different JUnit test runners, and to pass arbitrary options to them. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + * + * @author Rupert Smith + * + * @goal tktest + * @phase test + * @requiresDependencyResolution test + */ +public class TKTestRunnerMojo extends AbstractMojo +{ + private static final BitSet UNRESERVED = new BitSet(256); + + /** + * Set this to 'true' to bypass unit tests entirely. Its use is NOT RECOMMENDED, but quite convenient on occasion. + * + * @parameter expression="${maven.test.skip}" + */ + private boolean skip; + + /** + * The TKTest runner command lines. There are passed directly to the TKTestRunner main method. + * + * @parameter + */ + private Map commands = new LinkedHashMap(); + + /** + * The base directory of the project being tested. This can be obtained in your unit test by + * System.getProperty("basedir"). + * + * @parameter expression="${basedir}" + * @required + */ + private File basedir; + + /** + * The directory containing generated classes of the project being tested. + * + * @parameter expression="${project.build.outputDirectory}" + * @required + */ + private File classesDirectory; + + /** + * The directory containing generated test classes of the project being tested. + * + * @parameter expression="${project.build.testOutputDirectory}" + * @required + */ + private File testClassesDirectory; + + /** + * The classpath elements of the project being tested. + * + * @parameter expression="${project.testClasspathElements}" + * @required + * @readonly + */ + private List classpathElements; + + /** + * List of System properties to pass to the tests. + * + * @parameter + */ + private Properties systemProperties; + + /** + * Map of of plugin artifacts. + * + * @parameter expression="${plugin.artifactMap}" + * @required + * @readonly + */ + private Map pluginArtifactMap; + + /** + * Map of of project artifacts. + * + * @parameter expression="${project.artifactMap}" + * @required + * @readonly + */ + private Map projectArtifactMap; + + /** + * Option to specify the forking mode. Can be "never" (default), "once" or "always". + * "none" and "pertest" are also accepted for backwards compatibility. + * + * @parameter expression="${forkMode}" default-value="once" + */ + private String forkMode; + + /** + * Option to specify the jvm (or path to the java executable) to use with + * the forking options. For the default we will assume that java is in the path. + * + * @parameter expression="${jvm}" + * default-value="java" + */ + private String jvm; + + /** + * The test runner to use. + * + * @parameter + */ + private String testrunner; + + /** + * The additional properties to append to the test runner invocation command line. + * + * @parameter + */ + private Properties testrunnerproperties; + + /** + * The options to pass to all test runner invocation command lines. + * + * @parameter + */ + private String[] testrunneroptions; + + /** + * Implementation of the tktest goal. + */ + public void execute() + { + // Skip these tests if test skipping is turned on. + if (skip) + { + getLog().info("Skipping Tests."); + + return; + } + + // Log out the classpath if debugging is on. + if (getLog().isDebugEnabled()) + { + getLog().info("Test Classpath :"); + + for (Object classpathElement1 : classpathElements) + { + String classpathElement = (String) classpathElement1; + getLog().info(" " + classpathElement); + } + } + + try + { + // Create a class loader to load the test runner with. This also gets set as the context loader for this + // thread, so that all subsequent class loading activity by the test runner or the test code, has the + // test classes available to it. The system loader is set up for the maven build, which is why a seperate + // loader needs to be created; in order to inject the test dependencies into it. + ClassLoader runnerClassLoader = createClassLoader(classpathElements, ClassLoader.getSystemClassLoader(), true); + Thread.currentThread().setContextClassLoader(runnerClassLoader); + + // Load the test runner implementation that will be used to run the tests. + if ((testrunner == null) || "".equals(testrunner)) + { + testrunner = "org.apache.qpid.junit.extensions.TKTestRunner"; + } + + Class testRunnerClass = Class.forName(testrunner, false, runnerClassLoader); + Method run = testRunnerClass.getMethod("main", String[].class); + + // Concatenate all of the options to pass on the command line to the test runner. + String preOptions = ""; + + for (String option : testrunneroptions) + { + preOptions += option + " "; + } + + // Concatenate all of the additional properties as name=value pairs on the command line. + String nvPairs = ""; + + if (testrunnerproperties != null) + { + for (Object objKey : testrunnerproperties.keySet()) + { + String key = (String) objKey; + String value = testrunnerproperties.getProperty(key); + + nvPairs = key + "=" + value + " "; + } + } + + // Pass each of the test runner command lines in turn to the toolkit test runner. + // The command line is made up of the options, the command specific command line, then the trailing + // name=value pairs. + for (String testName : commands.keySet()) + { + String commandLine = preOptions + " " + commands.get(testName) + " " + nvPairs; + getLog().info("commandLine = " + commandLine); + + // Tokenize the command line on white space, into an array of string components. + String[] tokenizedCommandLine = commandLine.split("\\s+"); + + // Run the tests. + run.invoke(testRunnerClass, new Object[] { tokenizedCommandLine }); + } + } + catch (Exception e) + { + getLog().error("There was an exception: " + e.getMessage(), e); + } + } + + private static ClassLoader createClassLoader(List classPathUrls, ClassLoader parent, boolean childDelegation) + throws MalformedURLException + { + List urls = new ArrayList(); + + for (Iterator i = classPathUrls.iterator(); i.hasNext();) + { + String url = (String) i.next(); + + if (url != null) + { + File f = new File(url); + urls.add(f.toURL()); + } + } + + IsolatedClassLoader classLoader = new IsolatedClassLoader(parent, childDelegation); + + for (Iterator iter = urls.iterator(); iter.hasNext();) + { + URL url = (URL) iter.next(); + classLoader.addURL(url); + } + + return classLoader; + } +} diff --git a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java index 5c7669e069..adbe527e5e 100644 --- a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java +++ b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java @@ -1,148 +1,148 @@ -/* - * - * 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.junit.maven; - -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Properties; - -/** - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - * - * @author Rupert Smith - * @goal tkscriptgen - * @phase test - * @execute phase="test" - * @requiresDependencyResolution test - */ -public class TKTestScriptGenMojo extends AbstractMojo -{ - private static final String _scriptLanguage = "#!/bin/bash\n\n"; - - private static final String _javaOptArgParser = - "# Parse arguements taking all - prefixed args as JAVA_OPTS\n" + "for arg in \"$@\"; do\n" - + " if [[ $arg == -java:* ]]; then\n" + " JAVA_OPTS=\"${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` \"\n" - + " else\n" + " ARGS=\"${ARGS}$arg \"\n" + " fi\n" + "done\n\n"; - - /** - * Where to write out the scripts. - * - * @parameter - */ - private String scriptOutDirectory; - - /** - * The all-in-one test jar location. - * - * @parameter - */ - private String testJar; - - /** - * The system properties to pass to java runtime. - * - * @parameter - */ - private Properties systemproperties; - - /** - * The TKTest runner command lines. There are passed directly to the TKTestRunner main method. - * - * @parameter - */ - private Map commands = new LinkedHashMap(); - - /** - * Implementation of the tkscriptgen goal. - * - * @throws MojoExecutionException - */ - public void execute() throws MojoExecutionException - { - // Turn each of the test runner command lines into a script. - for (String testName : commands.keySet()) - { - String testOptions = commands.get(testName); - String commandLine = "java "; - - String logdir = null; - - for (Object key : systemproperties.keySet()) - { - String keyString = (String) key; - String value = systemproperties.getProperty(keyString); - - if (keyString.equals("logdir")) - { - logdir = value; - } - else - { - if (keyString.startsWith("-X")) - { - commandLine += keyString + value + " "; - } - else - { - commandLine += "-D" + keyString + "=" + value + " "; - } - } - } - - commandLine += - "${JAVA_OPTS} -cp " + testJar + " org.apache.qpid.junit.extensions.TKTestRunner " + testOptions + " ${ARGS}"; - - getLog().info("Generating Script for test: " + testName); - getLog().debug(commandLine); - - String fileName = scriptOutDirectory + "/" + testName + ".sh"; - - try - { - File scriptFile = new File(fileName); - Writer scriptWriter = new FileWriter(scriptFile); - scriptWriter.write(_scriptLanguage); - scriptWriter.write(_javaOptArgParser); - if (logdir != null) - { - scriptWriter.write("mkdir -p " + logdir + "\n"); - } - - scriptWriter.write(commandLine); - scriptWriter.flush(); - scriptWriter.close(); - } - catch (IOException e) - { - getLog().error("Failed to write: " + fileName); - } - } - } -} +/* + * + * 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.junit.maven; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +/** + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + * + * @author Rupert Smith + * @goal tkscriptgen + * @phase test + * @execute phase="test" + * @requiresDependencyResolution test + */ +public class TKTestScriptGenMojo extends AbstractMojo +{ + private static final String _scriptLanguage = "#!/bin/bash\n\n"; + + private static final String _javaOptArgParser = + "# Parse arguements taking all - prefixed args as JAVA_OPTS\n" + "for arg in \"$@\"; do\n" + + " if [[ $arg == -java:* ]]; then\n" + " JAVA_OPTS=\"${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` \"\n" + + " else\n" + " ARGS=\"${ARGS}$arg \"\n" + " fi\n" + "done\n\n"; + + /** + * Where to write out the scripts. + * + * @parameter + */ + private String scriptOutDirectory; + + /** + * The all-in-one test jar location. + * + * @parameter + */ + private String testJar; + + /** + * The system properties to pass to java runtime. + * + * @parameter + */ + private Properties systemproperties; + + /** + * The TKTest runner command lines. There are passed directly to the TKTestRunner main method. + * + * @parameter + */ + private Map commands = new LinkedHashMap(); + + /** + * Implementation of the tkscriptgen goal. + * + * @throws MojoExecutionException + */ + public void execute() throws MojoExecutionException + { + // Turn each of the test runner command lines into a script. + for (String testName : commands.keySet()) + { + String testOptions = commands.get(testName); + String commandLine = "java "; + + String logdir = null; + + for (Object key : systemproperties.keySet()) + { + String keyString = (String) key; + String value = systemproperties.getProperty(keyString); + + if (keyString.equals("logdir")) + { + logdir = value; + } + else + { + if (keyString.startsWith("-X")) + { + commandLine += keyString + value + " "; + } + else + { + commandLine += "-D" + keyString + "=" + value + " "; + } + } + } + + commandLine += + "${JAVA_OPTS} -cp " + testJar + " org.apache.qpid.junit.extensions.TKTestRunner " + testOptions + " ${ARGS}"; + + getLog().info("Generating Script for test: " + testName); + getLog().debug(commandLine); + + String fileName = scriptOutDirectory + "/" + testName + ".sh"; + + try + { + File scriptFile = new File(fileName); + Writer scriptWriter = new FileWriter(scriptFile); + scriptWriter.write(_scriptLanguage); + scriptWriter.write(_javaOptArgParser); + if (logdir != null) + { + scriptWriter.write("mkdir -p " + logdir + "\n"); + } + + scriptWriter.write(commandLine); + scriptWriter.flush(); + scriptWriter.close(); + } + catch (IOException e) + { + getLog().error("Failed to write: " + fileName); + } + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java index 6c88d019c4..8fb0a6a90e 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java @@ -1,48 +1,48 @@ -/* - * - * 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.junit.concurrency; - -import java.util.concurrent.ThreadFactory; - -/** - * Implements a default thread factory. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Create default threads with no specialization. - *
      - * - * @author Rupert Smith - */ -public class DefaultThreadFactory implements ThreadFactory -{ - /** - * Constructs a new Thread. - * - * @param r A runnable to be executed by new thread instance. - * - * @return The constructed thread. - */ - public Thread newThread(Runnable r) - { - return new Thread(r); - } -} +/* + * + * 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.junit.concurrency; + +import java.util.concurrent.ThreadFactory; + +/** + * Implements a default thread factory. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Create default threads with no specialization. + *
      + * + * @author Rupert Smith + */ +public class DefaultThreadFactory implements ThreadFactory +{ + /** + * Constructs a new Thread. + * + * @param r A runnable to be executed by new thread instance. + * + * @return The constructed thread. + */ + public Thread newThread(Runnable r) + { + return new Thread(r); + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java index 0bb07a4557..3bbfc2d502 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java @@ -1,46 +1,46 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.junit.concurrency; - -/** - * PossibleDeadlockException is used to signal that two test threads being executed by a {@link ThreadTestCoordinator} - * may be in a state of deadlock because they are mutually blocking each other or one is waiting on the other and the - * other has been blocked elsewhere for longer than a specified timeout. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Signal a possible state of deadlock between coordinated test threads. - *
      - * - * @author Rupert Smith - */ -public class PossibleDeadlockException extends RuntimeException -{ - /** - * Create a new possible deadlock execption. - * - * @param message The exception message. - */ - public PossibleDeadlockException(String message) - { - super(message); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.junit.concurrency; + +/** + * PossibleDeadlockException is used to signal that two test threads being executed by a {@link ThreadTestCoordinator} + * may be in a state of deadlock because they are mutually blocking each other or one is waiting on the other and the + * other has been blocked elsewhere for longer than a specified timeout. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Signal a possible state of deadlock between coordinated test threads. + *
      + * + * @author Rupert Smith + */ +public class PossibleDeadlockException extends RuntimeException +{ + /** + * Create a new possible deadlock execption. + * + * @param message The exception message. + */ + public PossibleDeadlockException(String message) + { + super(message); + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java index 5bf0c430cd..02e776a4ea 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java @@ -1,239 +1,239 @@ -/* - * - * 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.junit.concurrency; - -/** - * TestRunnable is an extension of java.util.Runnable that adds some features to make it easier to coordinate the - * activities of threads in such a way as to expose bugs in multi threaded code. - * - *

      Sometimes several threads will run in a particular order so that a bug is not revealed. Other times the ordering - * of the threads will expose a bug. Such bugs can be hard to replicate as the exact execution ordering of threads is not - * usually controlled. This class adds some methods that allow threads to synchronize other threads, either allowing them - * to run, or waiting for them to allow this thread to run. It also provides convenience methods to gather error messages - * and exceptions from threads, which will often be reported in unit testing code. - * - *

      Coordination between threads is handled by the {@link ThreadTestCoordinator}. It is called through the convenience - * methods {@link #allow} and {@link #waitFor}. Threads to be coordinated must be set up with the coordinator and assigned - * integer ids. It is then possible to call the coordinator with an array of thread ids requesting that those threads - * be allowed to continue, or to wait until one of them allows this thread to continue. The otherwise non-deterministic - * execution order of threads can be controlled into a carefully determined sequence using these methods in order - * to reproduce race conditions, dead locks, live locks, dirty reads, phantom reads, non repeatable reads and so on. - * - *

      When waiting for another thread to give a signal to continue it is sometimes the case that the other thread has - * become blocked by the code under test. For example in testing for a dirty read (for example in database code), - * thread 1 lets thread 2 perform a write but not commit it, then thread 2 lets thread 1 run and attempt to perform a - * dirty read on its uncommitted write. Transaction synchronization code being tested against the possibility of a dirty - * write may make use of snapshots in which case both threads should be able to read and write without blocking. It may - * make use of explicit keys in which case thread 2 may become blocked on its write attempt because thread 1 holds a - * read lock and it must wait until thread 1 completes its transaction before it can acquire this lock. The - * {@link #waitFor} method accepts a boolean parameter to indicate that threads being blocked (other than on the - * coordinator) can be interpreted the same as if the thread explicitly allows the thread calling waitFor to continue. - * Using this technique a dirty read test could be written that works against either the snapshot or the locking - * implementation, allowing both approaches to pass the test yet arranging for multiple threads to run against the - * implementation in such a way that a potential dirty read bug is exposed. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Wait for another thread to allow this one to continue. - *
      Allow another thread to continue. - *
      Accumulate error messages. - *
      Record exceptions from thread run. - *
      Maintain link to thread coordinator. - *
      Explicitly mark a thread with an integer id. - *
      Maintian a flag to indicate whether or not this thread is waiting on the coordinator. - *
      - * - * @todo The allow then waitFor operations are very often used as a pair. So create a method allowAndWait that combines - * them into a single method call. - * - * @author Rupert Smith - */ -public abstract class TestRunnable implements Runnable -{ - /** Holds a reference to the thread coordinator. */ - private ThreadTestCoordinator coordinator; - - /** Holds the explicit integer id of this thread. */ - private int id; - - /** Used to indicate that this thread is waiting on the coordinator and not elsewhere. */ - private boolean waitingOnCoordinator = false; - - /** Used to accumulate error messsages. */ - private String errorMessage = ""; - - /** Holds the Java thread object that this is running under. */ - private Thread thisThread; - - /** Used to hold any exceptions resulting from the run method. */ - private Exception runException = null; - - /** - * Implementations override this to perform coordinated thread sequencing. - * - * @throws Exception Any exception raised by the implementation will be caught by the default {@link #run()} - * implementation for later querying by the {@link #getException()} method. - */ - public abstract void runWithExceptions() throws Exception; - - /** - * Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record - * of those exceptions. Defers to the {@link #runWithExceptions()} method to provide the thread body implementation - * and catches any exceptions thrown by it. - */ - public void run() - { - try - { - runWithExceptions(); - } - catch (Exception e) - { - this.runException = e; - } - } - - /** - * Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs. - * - * @param threads The set of threads that can allow this one to continue. - * @param otherWaitIsAllow If set to true if the threads being waited on are blocked other than on - * the coordinator itself then this is to be interpreted as allowing this thread to - * continue. - * - * @return If the otherWaitIsAllow flag is set, then true is returned when the thread being waited on is found - * to be blocked outside of the thread test coordinator. false under all other conditions. - */ - protected boolean waitFor(int[] threads, boolean otherWaitIsAllow) - { - return coordinator.consumeAllowEvent(threads, otherWaitIsAllow, id, this); - } - - /** - * Produces allow events on each of the specified threads. - * - * @param threads The set of threads that are to be allowed to continue. - */ - protected void allow(int[] threads) - { - coordinator.produceAllowEvents(threads, id, this); - } - - /** - * Keeps the error message for later reporting by the coordinator. - * - * @param message The error message to keep. - */ - protected void addErrorMessage(String message) - { - errorMessage += message; - } - - /** - * Sets the coordinator for this thread. - * - * @param coordinator The coordinator for this thread. - */ - void setCoordinator(ThreadTestCoordinator coordinator) - { - this.coordinator = coordinator; - } - - /** - * Reports whether or not this thread is waiting on the coordinator. - * - * @return If this thread is waiting on the coordinator. - */ - boolean isWaitingOnCoordinator() - { - return waitingOnCoordinator; - } - - /** - * Sets the value of the waiting on coordinator flag. - * - * @param waiting The value of the waiting on coordinator flag. - */ - void setWaitingOnCoordinator(boolean waiting) - { - waitingOnCoordinator = waiting; - } - - /** - * Sets up the explicit int id for this thread. - * - * @param id The integer id. - */ - void setId(int id) - { - this.id = id; - } - - /** - * Reports any accumulated error messages. - * - * @return Any accumulated error messages. - */ - String getErrorMessage() - { - return errorMessage; - } - - /** - * Reports any exception thrown by the {@link #runWithExceptions} method. - * - * @return Any exception thrown by the {@link #runWithExceptions} method. - */ - Exception getException() - { - return runException; - } - - /** - * Sets the Java thread under which this runs. - * - * @param thread The Java thread under which this runs. - */ - void setThread(Thread thread) - { - thisThread = thread; - } - - /** - * Gets the Java thread under which this runs. - * - * @return The Java thread under which this runs. - */ - Thread getThread() - { - return thisThread; - } - - /** - * Provides a string summary of this test threads status. - * - * @return Summarizes this threads status. - */ - public String toString() - { - return "id = " + id + ", waitingOnCoordinator = " + waitingOnCoordinator; - } -} +/* + * + * 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.junit.concurrency; + +/** + * TestRunnable is an extension of java.util.Runnable that adds some features to make it easier to coordinate the + * activities of threads in such a way as to expose bugs in multi threaded code. + * + *

      Sometimes several threads will run in a particular order so that a bug is not revealed. Other times the ordering + * of the threads will expose a bug. Such bugs can be hard to replicate as the exact execution ordering of threads is not + * usually controlled. This class adds some methods that allow threads to synchronize other threads, either allowing them + * to run, or waiting for them to allow this thread to run. It also provides convenience methods to gather error messages + * and exceptions from threads, which will often be reported in unit testing code. + * + *

      Coordination between threads is handled by the {@link ThreadTestCoordinator}. It is called through the convenience + * methods {@link #allow} and {@link #waitFor}. Threads to be coordinated must be set up with the coordinator and assigned + * integer ids. It is then possible to call the coordinator with an array of thread ids requesting that those threads + * be allowed to continue, or to wait until one of them allows this thread to continue. The otherwise non-deterministic + * execution order of threads can be controlled into a carefully determined sequence using these methods in order + * to reproduce race conditions, dead locks, live locks, dirty reads, phantom reads, non repeatable reads and so on. + * + *

      When waiting for another thread to give a signal to continue it is sometimes the case that the other thread has + * become blocked by the code under test. For example in testing for a dirty read (for example in database code), + * thread 1 lets thread 2 perform a write but not commit it, then thread 2 lets thread 1 run and attempt to perform a + * dirty read on its uncommitted write. Transaction synchronization code being tested against the possibility of a dirty + * write may make use of snapshots in which case both threads should be able to read and write without blocking. It may + * make use of explicit keys in which case thread 2 may become blocked on its write attempt because thread 1 holds a + * read lock and it must wait until thread 1 completes its transaction before it can acquire this lock. The + * {@link #waitFor} method accepts a boolean parameter to indicate that threads being blocked (other than on the + * coordinator) can be interpreted the same as if the thread explicitly allows the thread calling waitFor to continue. + * Using this technique a dirty read test could be written that works against either the snapshot or the locking + * implementation, allowing both approaches to pass the test yet arranging for multiple threads to run against the + * implementation in such a way that a potential dirty read bug is exposed. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Wait for another thread to allow this one to continue. + *
      Allow another thread to continue. + *
      Accumulate error messages. + *
      Record exceptions from thread run. + *
      Maintain link to thread coordinator. + *
      Explicitly mark a thread with an integer id. + *
      Maintian a flag to indicate whether or not this thread is waiting on the coordinator. + *
      + * + * @todo The allow then waitFor operations are very often used as a pair. So create a method allowAndWait that combines + * them into a single method call. + * + * @author Rupert Smith + */ +public abstract class TestRunnable implements Runnable +{ + /** Holds a reference to the thread coordinator. */ + private ThreadTestCoordinator coordinator; + + /** Holds the explicit integer id of this thread. */ + private int id; + + /** Used to indicate that this thread is waiting on the coordinator and not elsewhere. */ + private boolean waitingOnCoordinator = false; + + /** Used to accumulate error messsages. */ + private String errorMessage = ""; + + /** Holds the Java thread object that this is running under. */ + private Thread thisThread; + + /** Used to hold any exceptions resulting from the run method. */ + private Exception runException = null; + + /** + * Implementations override this to perform coordinated thread sequencing. + * + * @throws Exception Any exception raised by the implementation will be caught by the default {@link #run()} + * implementation for later querying by the {@link #getException()} method. + */ + public abstract void runWithExceptions() throws Exception; + + /** + * Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record + * of those exceptions. Defers to the {@link #runWithExceptions()} method to provide the thread body implementation + * and catches any exceptions thrown by it. + */ + public void run() + { + try + { + runWithExceptions(); + } + catch (Exception e) + { + this.runException = e; + } + } + + /** + * Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs. + * + * @param threads The set of threads that can allow this one to continue. + * @param otherWaitIsAllow If set to true if the threads being waited on are blocked other than on + * the coordinator itself then this is to be interpreted as allowing this thread to + * continue. + * + * @return If the otherWaitIsAllow flag is set, then true is returned when the thread being waited on is found + * to be blocked outside of the thread test coordinator. false under all other conditions. + */ + protected boolean waitFor(int[] threads, boolean otherWaitIsAllow) + { + return coordinator.consumeAllowEvent(threads, otherWaitIsAllow, id, this); + } + + /** + * Produces allow events on each of the specified threads. + * + * @param threads The set of threads that are to be allowed to continue. + */ + protected void allow(int[] threads) + { + coordinator.produceAllowEvents(threads, id, this); + } + + /** + * Keeps the error message for later reporting by the coordinator. + * + * @param message The error message to keep. + */ + protected void addErrorMessage(String message) + { + errorMessage += message; + } + + /** + * Sets the coordinator for this thread. + * + * @param coordinator The coordinator for this thread. + */ + void setCoordinator(ThreadTestCoordinator coordinator) + { + this.coordinator = coordinator; + } + + /** + * Reports whether or not this thread is waiting on the coordinator. + * + * @return If this thread is waiting on the coordinator. + */ + boolean isWaitingOnCoordinator() + { + return waitingOnCoordinator; + } + + /** + * Sets the value of the waiting on coordinator flag. + * + * @param waiting The value of the waiting on coordinator flag. + */ + void setWaitingOnCoordinator(boolean waiting) + { + waitingOnCoordinator = waiting; + } + + /** + * Sets up the explicit int id for this thread. + * + * @param id The integer id. + */ + void setId(int id) + { + this.id = id; + } + + /** + * Reports any accumulated error messages. + * + * @return Any accumulated error messages. + */ + String getErrorMessage() + { + return errorMessage; + } + + /** + * Reports any exception thrown by the {@link #runWithExceptions} method. + * + * @return Any exception thrown by the {@link #runWithExceptions} method. + */ + Exception getException() + { + return runException; + } + + /** + * Sets the Java thread under which this runs. + * + * @param thread The Java thread under which this runs. + */ + void setThread(Thread thread) + { + thisThread = thread; + } + + /** + * Gets the Java thread under which this runs. + * + * @return The Java thread under which this runs. + */ + Thread getThread() + { + return thisThread; + } + + /** + * Provides a string summary of this test threads status. + * + * @return Summarizes this threads status. + */ + public String toString() + { + return "id = " + id + ", waitingOnCoordinator = " + waitingOnCoordinator; + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java index 0be0fe37dc..605c35feed 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java @@ -1,485 +1,485 @@ -/* - * - * 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.junit.concurrency; - -import org.apache.log4j.Logger; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.ThreadFactory; - -/** - * ThreadTestCoordinator provides an array of binary latches that allows threads to wait for other threads or to send - * them a signal that allows them to continue running or to wait for another thread to signal them. The binary latch - * array is always a square array, allowing one latch from and to every thread. Upon accepting an allow signal from one - * sender the latches for all senders for a are cleared. This class is always used in conjunction with - * {@link TestRunnable} for writing concurrent test code that coordinates multi-threaded activity in order to reproduce - * concurrency bugs. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Accept test threads to coordinate. - *
      Allow test threads to send 'allow to continue' signals. - *
      Allow test threads to wait on this coordinator for 'allow to continue' signals. - *
      Report error messages from test threads. - *
      Report exceptions from test threads. - *
      Provide method to wait until all test threads have completed. - *
      - * - * @todo This code was hacked together as a bit of an experiment, because I wasn't sure if this idea would work. It has - * proved extremely usefull. Some documentation for this needs to be written to explain it better. - * - * @todo Consider how deadlock detection will be handled. If all threads are blocking on the coordinator, waiting for - * each other, they are deadlocked and there is something wrong with the test code that put them in that - * situation. If they are all blocked elsewhere, they may be deadlocked, or could just be waiting on some - * external event. A timeout should be used. Timeout is already implemented, just need to sanity check how - * this is working and document it. - * - * @todo Consider how livelock detection could be implemented? LockFree data structures might cause live locks. I - * guess a longish timeout is the only thing that can be done for that. - * - * @todo Only course grained synchronous at the method class level can be obtained. This is because test code can - * only insert synchronization points between method calls it makes. So this code will not be usefull for - * checking sequences of events within methods, unless the code under test is explicitly instrumented for it. - * It might be possible to instrument code by using labels, and then use the debugger/profiler interface to - * put breakpoints on the labels and use them as synchronization points. Not perfect, but at the unused labels - * can be left in the code, without altering its behaviour. - * - * @author Rupert Smith - */ -public class ThreadTestCoordinator -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(ThreadTestCoordinator.class); - - /** Keeps track of the test threads by their ids. */ - private TestRunnable[] testThreads; // = new TestRunnable[2]; - - /** An explicit thread monitor for the coordinator. Threads wait on the coordinator whilst waiting for events. */ - private final Object coordinatorLock = new Object(); - - /** A set of monitors for each test thread. */ - private Object[] locks; - - /** The binary latch array, this is always a square array allowing one event from and to every thread. */ - private boolean[][] allowEvents; - - /** Keeps track of the number of threads being coordinated. */ - private int threadCount = 0; - - /** Accumulates any exceptions resulting from the threads run methods. */ - private Collection exceptions = new ArrayList(); - - /** - * Holds the deadlock timeout after which threads are given a runtime exception to signal that a potential - * deadlock may be happening. - */ - private long deadlockTimeout = 1000 * 1000000; - - /** Holds the factory to create test thread with. */ - private ThreadFactory threadFactory; - - /** - * Creates a new test thread coordinator. The number of threads to run must be specified here. - * - * @param numThreads The number of threads to run. - */ - public ThreadTestCoordinator(int numThreads) - { - this.threadCount = numThreads; - - // Create an array big enough to hold all the test threads. - testThreads = new TestRunnable[threadCount]; - - // Use the default thread factory, as none specified. - threadFactory = new DefaultThreadFactory(); - } - - /** - * Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be - * specified here. - * - * @param numThreads The number of threads to run. - * @param threadFactory The factory to use to create the test threads. - */ - public ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory) - { - this.threadCount = numThreads; - - // Create an array big enough to hold all the test threads. - testThreads = new TestRunnable[threadCount]; - - // Use the specified thread factory. - this.threadFactory = threadFactory; - } - - /** - * Adds a thread to this coordinator and assigns an id to it. The ids must be numbered sequentially from 0 and - * it is up to the caller to do this. - * - * @param runnable The test thread. - * @param id The explicit id to assign to the test thread. - */ - public void addTestThread(TestRunnable runnable, int id) - { - testThreads[id] = runnable; - runnable.setCoordinator(this); - runnable.setId(id); - } - - /** - * Starts all the coordinated threads running. - */ - public void run() - { - // Create the monitors for each thread. - locks = new Object[threadCount]; - - // Create an appropriately sized event queue to allow one event from and to each thread. - allowEvents = new boolean[threadCount][threadCount]; - - // Initialize the monitors and clear the event queues. - for (int i = 0; i < locks.length; i++) - { - locks[i] = new Object(); - - for (int j = 0; j < locks.length; j++) - { - allowEvents[i][j] = false; - } - } - - // Start all the threads running. - for (TestRunnable nextRunnable : testThreads) - { - // Create a Java thread for the test thread. - Thread newThread = threadFactory.newThread(nextRunnable); - nextRunnable.setThread(newThread); - - // Start it running. - newThread.start(); - } - } - - /** - * Waits until all the test threads have completed and returns any accumulated error messages from them. Any - * exceptions thrown by their run methods are also kept at this point. - * - * @return The accumulated error messages from all the threads concatenated together. - */ - public String joinAndRetrieveMessages() - { - // Create an empty error message. - String errorMessage = ""; - - // Join all the test threads. - for (TestRunnable r : testThreads) - { - Thread t = r.getThread(); - - try - { - t.join(); - } - catch (InterruptedException e) - { } - - // Add any accumulated error messages to the return value. - errorMessage += r.getErrorMessage(); - - // Keep any exceptions resulting from the threads run method. - Exception e = r.getException(); - - if (e != null) - { - exceptions.add(e); - } - } - - return errorMessage; - } - - /** - * Reports any accumulated exceptions from the test threads run methods. This method must be called after - * {@link #joinAndRetrieveMessages}. - * - * @return Any accumulated exceptions from the test threads run methods. This method must be called after - */ - public Collection getExceptions() - { - return exceptions; - } - - /** - * Sets a timeout to break out of potential deadlocks. If all threads are waiting for other threads to send - * them continue events for longer than this timeout then the threads are all terminated. - * - * @param millis The minimum time to allow to pass before breaking out of any potential deadlocks. - * - * @todo This has not been implemented yet. If a potential deadlock happens then the joinAndRetrieveMessages - * method should throw a PotentialDeadlockException. - */ - public void setDeadlockTimeout(long millis) - { - deadlockTimeout = millis * 1000000; - } - - /** - * Creates a set of 'allow to continue' events on the event queues of the specified threads. - * - * @param threads The set of threads to allow to continue. - * @param callerId The explicit id of the calling test thread. - * @param caller The calling test thread. - */ - void produceAllowEvents(int[] threads, int callerId, TestRunnable caller) - { - // Generate some debugging messages. Very usefull to know how thread synchronization is progressing. - String message = "Thread " + callerId + " is allowing threads [ "; - - for (int j = 0; j < threads.length; j++) - { - message += threads[j] + ((j < (threads.length - 1)) ? ", " : ""); - } - - message += " ] to continue."; - log.debug(message); - - // For each allow event, synchronize on the threads lock then set the event flag to true. - for (int id : threads) - { - // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for - // being blocked at this time. - caller.setWaitingOnCoordinator(true); - - synchronized (locks[id]) - { - // Release the wating on coordinator flag now that this thread is running again. - caller.setWaitingOnCoordinator(false); - - // Send the allow to continue event to the receiving thread. - allowEvents[id][callerId] = true; - } - } - - // Wake up any threads waiting on the coordinator lock to recheck their event queues. - // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for - // being blocked at this time. - caller.setWaitingOnCoordinator(true); - - synchronized (coordinatorLock) - { - // Release the wating on coordinator flag now that this thread is running again. - caller.setWaitingOnCoordinator(false); - coordinatorLock.notifyAll(); - } - } - - /** - * Consumes an 'allow to continue' from one of the specified threads or waits until one is available or in some - * cases if one of the specified threads is blocked elsewhere to accept that as an 'allow to continue' event. - * - * @param threads The set of threads to accept an allow to continue event from. - * @param otherWaitIsAllow Whether or not to accept threads being blocked elsewhere as permission to continue. - * @param callerId The explicit id of the calling test thread. - * @param caller The calling test thread. - * - * @return If the otherWaitIsAllow flag is set, then true is returned when the thread being waited on is found - * to be blocked outside of the thread test coordinator. false under all other conditions. - */ - boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller) - { - // Generate some debugging messages. Very usefull to know how thread synchronization is progressing. - String message = "Thread " + callerId + " is requesting threads [ "; - - // Record the time at which this method was called. Will be used for breaking out of potential deadlocks. - long startTime = System.nanoTime(); - - for (int j = 0; j < threads.length; j++) - { - message += threads[j] + ((j < (threads.length - 1)) ? ", " : ""); - } - - message += " ] to allow it to continue."; - log.debug(message); - - // Loop until an allow to continue event is received. - while (true) - { - // Look at all the allowing thread to see if one has created an event for consumption. - for (int allowerId : threads) - { - // Get the threads lock for the event to consume. - // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for - // being blocked at this time. - caller.setWaitingOnCoordinator(true); - - synchronized (locks[callerId]) - { - // Release the wating on coordinator flag now that this thread is running again. - caller.setWaitingOnCoordinator(false); - - // Check if there is an event on the queue from the allowing thread to this one. - if (allowEvents[callerId][allowerId]) - { - log.debug("Found an allow event, thread " + allowerId + ", is allowing thread " + callerId - + ", to continue."); - - // Consume all the allow events for this thread. - /*for (int i = 0; i < allowEvents[callerId].length; i++) - { - allowEvents[callerId][i] = false; - }*/ - - // Consume just the event from the allower to the consumer, leaving other pending allow events alone. - allowEvents[callerId][allowerId] = false; - - return false; - } - } - } - - // If waiting elsewhere is to be interpreted as an 'allow to continue' event, then look at the thread status - // for the threads being waited on to see if any are blocked on other resources. - if (otherWaitIsAllow) - { - log.debug("Other wait is to be interpreted as an allow event."); - - // Look at all the potential allower threads. - for (int allowerId : threads) - { - // Get the Java thread state for the allowing thread. - Thread threadToTest = testThreads[allowerId].getThread(); - Thread.State state = threadToTest.getState(); - - // Check if the thread is blocked and so a potential candidate for releasing this one. - if ((state == Thread.State.BLOCKED) || (state == Thread.State.WAITING) - || (state == Thread.State.TIMED_WAITING)) - { - log.debug("Found an allower thread, id = " + allowerId + ", that is blocked or wating."); - - // Check that the allower thread is not waiting on the coordinator lock or any of the - // individual thread locks. It must be waiting or blocked on another monitor. - TestRunnable allowingRunnable = testThreads[allowerId]; - boolean isWaitingOnCoordinator = allowingRunnable.isWaitingOnCoordinator(); - - if (!isWaitingOnCoordinator) - { - log.debug("The allower thread, id = " + allowerId - + ", is blocked or waiting other than on the coordinator."); - - // Get the threads lock for the event to consume. - caller.setWaitingOnCoordinator(true); - - synchronized (locks[callerId]) - { - caller.setWaitingOnCoordinator(false); - - // Consume all the allow events for this thread. - for (int i = 0; i < allowEvents[callerId].length; i++) - { - allowEvents[callerId][i] = false; - } - - return true; - } - } - else - { - log.debug("The waiting allower thread, " + allowerId - + ", is waiting on the coordinator so does not allow thread " + callerId + " to continue."); - } - } - } - } - - // Keep waiting until an 'allow to continue' event can be consumed. - try - { - // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for - // being blocked at this time. - caller.setWaitingOnCoordinator(true); - - synchronized (coordinatorLock) - { - // Release the wating on coordinator flag now that this thread is running again. - caller.setWaitingOnCoordinator(false); - - log.debug("Thread " + callerId + " is waiting on coordinator lock for more allow events."); - - // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for - // being blocked at this time. - caller.setWaitingOnCoordinator(true); - coordinatorLock.wait(10); - } - } - catch (InterruptedException e) - { } - - // Release the waiting on coordinator flag now that this thread is running again. - caller.setWaitingOnCoordinator(false); - - // Check if this thread has been waiting for longer than the deadlock timeout and raise a possible - // deadlock exception if so. - long waitTime = System.nanoTime() - startTime; - log.debug("Thread " + callerId + " has been waiting for " + (waitTime / 1000000) + " milliseconds."); - - if (waitTime > deadlockTimeout) - { - // Throw a possible deadlock exception. - throw new PossibleDeadlockException("Possible deadlock due to timeout with state:\n" + this); - } - - log.debug("Thread " + callerId + " has woken up, was waiting for more allow events to become available."); - } - } - - /** - * Pretty prints the state of the thread test coordinator, for debugging purposes. - * - * @return Pretty printed state of the thread test coordinator. - */ - public String toString() - { - String result = "["; - - for (int i = 0; i < allowEvents.length; i++) - { - for (int j = 0; j < allowEvents[i].length; j++) - { - result += allowEvents[i][j]; - - result += (j < (allowEvents[i].length - 1)) ? ", " : ""; - } - - result += (i < (allowEvents.length - 1)) ? ",\n " : ""; - } - - result += "]"; - - for (int i = 0; i < testThreads.length; i++) - { - result += "thread[" + i + "] = " + testThreads[i].toString(); - } - - return result; - } - -} +/* + * + * 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.junit.concurrency; + +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ThreadFactory; + +/** + * ThreadTestCoordinator provides an array of binary latches that allows threads to wait for other threads or to send + * them a signal that allows them to continue running or to wait for another thread to signal them. The binary latch + * array is always a square array, allowing one latch from and to every thread. Upon accepting an allow signal from one + * sender the latches for all senders for a are cleared. This class is always used in conjunction with + * {@link TestRunnable} for writing concurrent test code that coordinates multi-threaded activity in order to reproduce + * concurrency bugs. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Accept test threads to coordinate. + *
      Allow test threads to send 'allow to continue' signals. + *
      Allow test threads to wait on this coordinator for 'allow to continue' signals. + *
      Report error messages from test threads. + *
      Report exceptions from test threads. + *
      Provide method to wait until all test threads have completed. + *
      + * + * @todo This code was hacked together as a bit of an experiment, because I wasn't sure if this idea would work. It has + * proved extremely usefull. Some documentation for this needs to be written to explain it better. + * + * @todo Consider how deadlock detection will be handled. If all threads are blocking on the coordinator, waiting for + * each other, they are deadlocked and there is something wrong with the test code that put them in that + * situation. If they are all blocked elsewhere, they may be deadlocked, or could just be waiting on some + * external event. A timeout should be used. Timeout is already implemented, just need to sanity check how + * this is working and document it. + * + * @todo Consider how livelock detection could be implemented? LockFree data structures might cause live locks. I + * guess a longish timeout is the only thing that can be done for that. + * + * @todo Only course grained synchronous at the method class level can be obtained. This is because test code can + * only insert synchronization points between method calls it makes. So this code will not be usefull for + * checking sequences of events within methods, unless the code under test is explicitly instrumented for it. + * It might be possible to instrument code by using labels, and then use the debugger/profiler interface to + * put breakpoints on the labels and use them as synchronization points. Not perfect, but at the unused labels + * can be left in the code, without altering its behaviour. + * + * @author Rupert Smith + */ +public class ThreadTestCoordinator +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(ThreadTestCoordinator.class); + + /** Keeps track of the test threads by their ids. */ + private TestRunnable[] testThreads; // = new TestRunnable[2]; + + /** An explicit thread monitor for the coordinator. Threads wait on the coordinator whilst waiting for events. */ + private final Object coordinatorLock = new Object(); + + /** A set of monitors for each test thread. */ + private Object[] locks; + + /** The binary latch array, this is always a square array allowing one event from and to every thread. */ + private boolean[][] allowEvents; + + /** Keeps track of the number of threads being coordinated. */ + private int threadCount = 0; + + /** Accumulates any exceptions resulting from the threads run methods. */ + private Collection exceptions = new ArrayList(); + + /** + * Holds the deadlock timeout after which threads are given a runtime exception to signal that a potential + * deadlock may be happening. + */ + private long deadlockTimeout = 1000 * 1000000; + + /** Holds the factory to create test thread with. */ + private ThreadFactory threadFactory; + + /** + * Creates a new test thread coordinator. The number of threads to run must be specified here. + * + * @param numThreads The number of threads to run. + */ + public ThreadTestCoordinator(int numThreads) + { + this.threadCount = numThreads; + + // Create an array big enough to hold all the test threads. + testThreads = new TestRunnable[threadCount]; + + // Use the default thread factory, as none specified. + threadFactory = new DefaultThreadFactory(); + } + + /** + * Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be + * specified here. + * + * @param numThreads The number of threads to run. + * @param threadFactory The factory to use to create the test threads. + */ + public ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory) + { + this.threadCount = numThreads; + + // Create an array big enough to hold all the test threads. + testThreads = new TestRunnable[threadCount]; + + // Use the specified thread factory. + this.threadFactory = threadFactory; + } + + /** + * Adds a thread to this coordinator and assigns an id to it. The ids must be numbered sequentially from 0 and + * it is up to the caller to do this. + * + * @param runnable The test thread. + * @param id The explicit id to assign to the test thread. + */ + public void addTestThread(TestRunnable runnable, int id) + { + testThreads[id] = runnable; + runnable.setCoordinator(this); + runnable.setId(id); + } + + /** + * Starts all the coordinated threads running. + */ + public void run() + { + // Create the monitors for each thread. + locks = new Object[threadCount]; + + // Create an appropriately sized event queue to allow one event from and to each thread. + allowEvents = new boolean[threadCount][threadCount]; + + // Initialize the monitors and clear the event queues. + for (int i = 0; i < locks.length; i++) + { + locks[i] = new Object(); + + for (int j = 0; j < locks.length; j++) + { + allowEvents[i][j] = false; + } + } + + // Start all the threads running. + for (TestRunnable nextRunnable : testThreads) + { + // Create a Java thread for the test thread. + Thread newThread = threadFactory.newThread(nextRunnable); + nextRunnable.setThread(newThread); + + // Start it running. + newThread.start(); + } + } + + /** + * Waits until all the test threads have completed and returns any accumulated error messages from them. Any + * exceptions thrown by their run methods are also kept at this point. + * + * @return The accumulated error messages from all the threads concatenated together. + */ + public String joinAndRetrieveMessages() + { + // Create an empty error message. + String errorMessage = ""; + + // Join all the test threads. + for (TestRunnable r : testThreads) + { + Thread t = r.getThread(); + + try + { + t.join(); + } + catch (InterruptedException e) + { } + + // Add any accumulated error messages to the return value. + errorMessage += r.getErrorMessage(); + + // Keep any exceptions resulting from the threads run method. + Exception e = r.getException(); + + if (e != null) + { + exceptions.add(e); + } + } + + return errorMessage; + } + + /** + * Reports any accumulated exceptions from the test threads run methods. This method must be called after + * {@link #joinAndRetrieveMessages}. + * + * @return Any accumulated exceptions from the test threads run methods. This method must be called after + */ + public Collection getExceptions() + { + return exceptions; + } + + /** + * Sets a timeout to break out of potential deadlocks. If all threads are waiting for other threads to send + * them continue events for longer than this timeout then the threads are all terminated. + * + * @param millis The minimum time to allow to pass before breaking out of any potential deadlocks. + * + * @todo This has not been implemented yet. If a potential deadlock happens then the joinAndRetrieveMessages + * method should throw a PotentialDeadlockException. + */ + public void setDeadlockTimeout(long millis) + { + deadlockTimeout = millis * 1000000; + } + + /** + * Creates a set of 'allow to continue' events on the event queues of the specified threads. + * + * @param threads The set of threads to allow to continue. + * @param callerId The explicit id of the calling test thread. + * @param caller The calling test thread. + */ + void produceAllowEvents(int[] threads, int callerId, TestRunnable caller) + { + // Generate some debugging messages. Very usefull to know how thread synchronization is progressing. + String message = "Thread " + callerId + " is allowing threads [ "; + + for (int j = 0; j < threads.length; j++) + { + message += threads[j] + ((j < (threads.length - 1)) ? ", " : ""); + } + + message += " ] to continue."; + log.debug(message); + + // For each allow event, synchronize on the threads lock then set the event flag to true. + for (int id : threads) + { + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (locks[id]) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + // Send the allow to continue event to the receiving thread. + allowEvents[id][callerId] = true; + } + } + + // Wake up any threads waiting on the coordinator lock to recheck their event queues. + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (coordinatorLock) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + coordinatorLock.notifyAll(); + } + } + + /** + * Consumes an 'allow to continue' from one of the specified threads or waits until one is available or in some + * cases if one of the specified threads is blocked elsewhere to accept that as an 'allow to continue' event. + * + * @param threads The set of threads to accept an allow to continue event from. + * @param otherWaitIsAllow Whether or not to accept threads being blocked elsewhere as permission to continue. + * @param callerId The explicit id of the calling test thread. + * @param caller The calling test thread. + * + * @return If the otherWaitIsAllow flag is set, then true is returned when the thread being waited on is found + * to be blocked outside of the thread test coordinator. false under all other conditions. + */ + boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller) + { + // Generate some debugging messages. Very usefull to know how thread synchronization is progressing. + String message = "Thread " + callerId + " is requesting threads [ "; + + // Record the time at which this method was called. Will be used for breaking out of potential deadlocks. + long startTime = System.nanoTime(); + + for (int j = 0; j < threads.length; j++) + { + message += threads[j] + ((j < (threads.length - 1)) ? ", " : ""); + } + + message += " ] to allow it to continue."; + log.debug(message); + + // Loop until an allow to continue event is received. + while (true) + { + // Look at all the allowing thread to see if one has created an event for consumption. + for (int allowerId : threads) + { + // Get the threads lock for the event to consume. + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (locks[callerId]) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + // Check if there is an event on the queue from the allowing thread to this one. + if (allowEvents[callerId][allowerId]) + { + log.debug("Found an allow event, thread " + allowerId + ", is allowing thread " + callerId + + ", to continue."); + + // Consume all the allow events for this thread. + /*for (int i = 0; i < allowEvents[callerId].length; i++) + { + allowEvents[callerId][i] = false; + }*/ + + // Consume just the event from the allower to the consumer, leaving other pending allow events alone. + allowEvents[callerId][allowerId] = false; + + return false; + } + } + } + + // If waiting elsewhere is to be interpreted as an 'allow to continue' event, then look at the thread status + // for the threads being waited on to see if any are blocked on other resources. + if (otherWaitIsAllow) + { + log.debug("Other wait is to be interpreted as an allow event."); + + // Look at all the potential allower threads. + for (int allowerId : threads) + { + // Get the Java thread state for the allowing thread. + Thread threadToTest = testThreads[allowerId].getThread(); + Thread.State state = threadToTest.getState(); + + // Check if the thread is blocked and so a potential candidate for releasing this one. + if ((state == Thread.State.BLOCKED) || (state == Thread.State.WAITING) + || (state == Thread.State.TIMED_WAITING)) + { + log.debug("Found an allower thread, id = " + allowerId + ", that is blocked or wating."); + + // Check that the allower thread is not waiting on the coordinator lock or any of the + // individual thread locks. It must be waiting or blocked on another monitor. + TestRunnable allowingRunnable = testThreads[allowerId]; + boolean isWaitingOnCoordinator = allowingRunnable.isWaitingOnCoordinator(); + + if (!isWaitingOnCoordinator) + { + log.debug("The allower thread, id = " + allowerId + + ", is blocked or waiting other than on the coordinator."); + + // Get the threads lock for the event to consume. + caller.setWaitingOnCoordinator(true); + + synchronized (locks[callerId]) + { + caller.setWaitingOnCoordinator(false); + + // Consume all the allow events for this thread. + for (int i = 0; i < allowEvents[callerId].length; i++) + { + allowEvents[callerId][i] = false; + } + + return true; + } + } + else + { + log.debug("The waiting allower thread, " + allowerId + + ", is waiting on the coordinator so does not allow thread " + callerId + " to continue."); + } + } + } + } + + // Keep waiting until an 'allow to continue' event can be consumed. + try + { + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + + synchronized (coordinatorLock) + { + // Release the wating on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + log.debug("Thread " + callerId + " is waiting on coordinator lock for more allow events."); + + // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for + // being blocked at this time. + caller.setWaitingOnCoordinator(true); + coordinatorLock.wait(10); + } + } + catch (InterruptedException e) + { } + + // Release the waiting on coordinator flag now that this thread is running again. + caller.setWaitingOnCoordinator(false); + + // Check if this thread has been waiting for longer than the deadlock timeout and raise a possible + // deadlock exception if so. + long waitTime = System.nanoTime() - startTime; + log.debug("Thread " + callerId + " has been waiting for " + (waitTime / 1000000) + " milliseconds."); + + if (waitTime > deadlockTimeout) + { + // Throw a possible deadlock exception. + throw new PossibleDeadlockException("Possible deadlock due to timeout with state:\n" + this); + } + + log.debug("Thread " + callerId + " has woken up, was waiting for more allow events to become available."); + } + } + + /** + * Pretty prints the state of the thread test coordinator, for debugging purposes. + * + * @return Pretty printed state of the thread test coordinator. + */ + public String toString() + { + String result = "["; + + for (int i = 0; i < allowEvents.length; i++) + { + for (int j = 0; j < allowEvents[i].length; j++) + { + result += allowEvents[i][j]; + + result += (j < (allowEvents[i].length - 1)) ? ", " : ""; + } + + result += (i < (allowEvents.length - 1)) ? ",\n " : ""; + } + + result += "]"; + + for (int i = 0; i < testThreads.length; i++) + { + result += "thread[" + i + "] = " + testThreads[i].toString(); + } + + return result; + } + +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java index ef177a4255..b9865f2e22 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java @@ -1,145 +1,145 @@ -/* - * - * 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.junit.concurrency; - -import org.apache.log4j.Logger; - -/** - * An example to illustrate the use of the {@link ThreadTestCoordinator} and {@link TestRunnable}s. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Demo multi-threaded testing. - *
      - * - * @author Rupert Smith - */ -public class ThreadTestExample -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(ThreadTestExample.class); - - /** Test thread 1. */ - TestRunnable testThread1 = - new TestRunnable() - { - public void runWithExceptions() throws Exception - { - log.debug("public void run(): called"); - log.info("in testThread0, block 1"); - - // Wait for t2 to allow t1 to continue. - allow(new int[] { 1 }); - waitFor(new int[] { 1 }, false); - - log.info("in testThread0, block 2"); - - // Wait for t2 to allow t1 to continue. T2 is allowed to be blocked elsewhere than giving explicit - // permission to allow t1 to continue. - allow(new int[] { 1 }); - waitFor(new int[] { 1 }, true); - - log.info("in testThread0, block 3"); - - // Release thread 2 from waiting on the shared lock. - synchronized (sharedLock) - { - sharedLock.notifyAll(); - } - - allow(new int[] { 1 }); - } - }; - - /** A shared lock between the test threads. */ - final Object sharedLock = new Object(); - - /** Test thread 2. */ - TestRunnable testThread2 = - new TestRunnable() - { - public void runWithExceptions() throws Exception - { - log.debug("public void run(): called"); - log.info("in testThread1, block 1"); - - // Wait for t1 to allow t2 to continue. - allow(new int[] { 0 }); - waitFor(new int[] { 0 }, false); - - log.info("in testThread1, block 2"); - - // Wait on another resource. T1 should accept this as permission to continue. - try - { - synchronized (sharedLock) - { - log.debug("in testThread1, waiting on shared lock."); - sharedLock.wait(); - } - } - catch (InterruptedException e) - { - // Bail-out with a runtime if this happens. - throw new RuntimeException("Interrupted whilst waiting for shared lock.", e); - } - - log.info("in testThread1, finished waiting on shared lock."); - - // allow(new int[] { 0 }); - - // Wait for t1 to allow t2 to continue. - waitFor(new int[] { 0 }, false); - - log.info("in testThread1, block 3"); - - allow(new int[] { 0 }); - } - }; - - /** - * Executes the test threads with coordination. - * - * @param args Ignored. - */ - public void main(String[] args) - { - ThreadTestCoordinator tt = new ThreadTestCoordinator(2); - - tt.addTestThread(testThread1, 0); - tt.addTestThread(testThread2, 1); - tt.setDeadlockTimeout(500); - tt.run(); - - String errorMessage = tt.joinAndRetrieveMessages(); - - // Print any error messages or exceptions. - log.info(errorMessage); - - if (!tt.getExceptions().isEmpty()) - { - for (Exception e : tt.getExceptions()) - { - log.warn("Exception thrown during test thread: ", e); - } - } - } -} +/* + * + * 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.junit.concurrency; + +import org.apache.log4j.Logger; + +/** + * An example to illustrate the use of the {@link ThreadTestCoordinator} and {@link TestRunnable}s. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Demo multi-threaded testing. + *
      + * + * @author Rupert Smith + */ +public class ThreadTestExample +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(ThreadTestExample.class); + + /** Test thread 1. */ + TestRunnable testThread1 = + new TestRunnable() + { + public void runWithExceptions() throws Exception + { + log.debug("public void run(): called"); + log.info("in testThread0, block 1"); + + // Wait for t2 to allow t1 to continue. + allow(new int[] { 1 }); + waitFor(new int[] { 1 }, false); + + log.info("in testThread0, block 2"); + + // Wait for t2 to allow t1 to continue. T2 is allowed to be blocked elsewhere than giving explicit + // permission to allow t1 to continue. + allow(new int[] { 1 }); + waitFor(new int[] { 1 }, true); + + log.info("in testThread0, block 3"); + + // Release thread 2 from waiting on the shared lock. + synchronized (sharedLock) + { + sharedLock.notifyAll(); + } + + allow(new int[] { 1 }); + } + }; + + /** A shared lock between the test threads. */ + final Object sharedLock = new Object(); + + /** Test thread 2. */ + TestRunnable testThread2 = + new TestRunnable() + { + public void runWithExceptions() throws Exception + { + log.debug("public void run(): called"); + log.info("in testThread1, block 1"); + + // Wait for t1 to allow t2 to continue. + allow(new int[] { 0 }); + waitFor(new int[] { 0 }, false); + + log.info("in testThread1, block 2"); + + // Wait on another resource. T1 should accept this as permission to continue. + try + { + synchronized (sharedLock) + { + log.debug("in testThread1, waiting on shared lock."); + sharedLock.wait(); + } + } + catch (InterruptedException e) + { + // Bail-out with a runtime if this happens. + throw new RuntimeException("Interrupted whilst waiting for shared lock.", e); + } + + log.info("in testThread1, finished waiting on shared lock."); + + // allow(new int[] { 0 }); + + // Wait for t1 to allow t2 to continue. + waitFor(new int[] { 0 }, false); + + log.info("in testThread1, block 3"); + + allow(new int[] { 0 }); + } + }; + + /** + * Executes the test threads with coordination. + * + * @param args Ignored. + */ + public void main(String[] args) + { + ThreadTestCoordinator tt = new ThreadTestCoordinator(2); + + tt.addTestThread(testThread1, 0); + tt.addTestThread(testThread2, 1); + tt.setDeadlockTimeout(500); + tt.run(); + + String errorMessage = tt.joinAndRetrieveMessages(); + + // Print any error messages or exceptions. + log.info(errorMessage); + + if (!tt.getExceptions().isEmpty()) + { + for (Exception e : tt.getExceptions()) + { + log.warn("Exception thrown during test thread: ", e); + } + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java index 03e465695e..58a7f60f3c 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java @@ -1,303 +1,303 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.TestCase; - -import org.apache.log4j.Logger; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -/** - * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour. - * - *

      ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal - * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is - * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure - * for different numbers of data elements held in the data structure the int parameter should be interpreted as the - * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour - * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class - * will also be run. - * - *

      TestCases derived from this class may also define tear down methods to clean up their memory usage. This is - * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is - * to write a test that allocates memory in the main test method in such a way that it leaves that memory still - * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method - * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the - * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of - * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Store the current int parameter value. {@link TKTestResult} and see {@link AsymptoticTestDecorator} too. - *
      Invoke parameterized test methods. - *
      - * - * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or - * {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this - * class to do time and space performance analysis, these methods could be added to any JUnit TestCase class - * instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to - * work with this extension, and also tests that extend other junit extension classes could have parameterized - * and tear down methods too. - * - * @author Rupert Smith - */ -public class AsymptoticTestCase extends TestCase implements InstrumentedTest -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(AsymptoticTestCase.class); - - /** The name of the test case. */ - private String testCaseName; - - /** Thread local for holding measurements on a per thread basis. */ - ThreadLocal threadLocalMeasurement = - new ThreadLocal() - { - /** - * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1). - * - * @return A set of default test measurements. - */ - protected synchronized TestMeasurements initialValue() - { - return new TestMeasurements(); - } - }; - - /** - * Constructs a test case with the given name. - * - * @param name The name of the test. - */ - public AsymptoticTestCase(String name) - { - super(name); - - log.debug("public AsymptoticTestCase(String " + name + "): called"); - testCaseName = name; - } - - /** - * Gets the current value of the integer parameter to be passed to the parameterized test. - * - * @return The current value of the integer parameter. - */ - public int getN() - { - log.debug("public int getN(): called"); - int n = threadLocalMeasurement.get().n; - - log.debug("return: " + n); - - return n; - } - - /** - * Sets the current value of the integer parameter to be passed to the parameterized test. - * - * @param n The new current value of the integer parameter. - */ - public void setN(int n) - { - log.debug("public void setN(int " + n + "): called"); - threadLocalMeasurement.get().n = n; - } - - /** - * Reports how long the test took to run. - * - * @return The time in milliseconds that the test took to run. - */ - public long getTestTime() - { - log.debug("public long getTestTime(): called"); - long startTime = threadLocalMeasurement.get().startTime; - long endTime = threadLocalMeasurement.get().endTime; - long testTime = endTime - startTime; - - log.debug("return: " + testTime); - - return testTime; - } - - /** - * Reports the memory usage at the start of the test. - * - * @return The memory usage at the start of the test. - */ - public long getTestStartMemory() - { - // log.debug("public long getTestStartMemory(): called"); - long startMem = threadLocalMeasurement.get().startMem; - - // log.debug("return: " + startMem); - - return startMem; - } - - /** - * Reports the memory usage at the end of the test. - * - * @return The memory usage at the end of the test. - */ - public long getTestEndMemory() - { - // log.debug("public long getTestEndMemory(): called"); - long endMem = threadLocalMeasurement.get().endMem; - - // log.debug("return: " + endMem); - return endMem; - } - - /** - * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory - * can be reclaimed. - */ - public void reset() - { - log.debug("public void reset(): called"); - threadLocalMeasurement.remove(); - } - - /** - * Runs the test method for this test case. - * - * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through. - */ - protected void runTest() throws Throwable - { - log.debug("protected void runTest(): called"); - - // Check that a test name has been set. This is used to define which method to run. - assertNotNull(testCaseName); - log.debug("testCaseName = " + testCaseName); - - // Try to get the method with matching name. - Method runMethod = null; - boolean isParameterized = false; - - // Check if a parameterized test method is available. - try - { - // Use getMethod to get all public inherited methods. getDeclaredMethods returns all - // methods of this class but excludes the inherited ones. - runMethod = getClass().getMethod(testCaseName, int.class); - isParameterized = true; - } - catch (NoSuchMethodException e) - { - // log.debug("Parameterized method \"" + testCaseName + "\" not found."); - // Set run method to null (it already will be but...) to indicate that no parameterized method - // version could be found. - runMethod = null; - } - - // If no parameterized method is available, try and get the unparameterized method. - if (runMethod == null) - { - try - { - runMethod = getClass().getMethod(testCaseName); - isParameterized = false; - - } - catch (NoSuchMethodException e) - { - fail("Method \"" + testCaseName + "\" not found."); - } - } - - // Check that the method is publicly accessable. - if (!Modifier.isPublic(runMethod.getModifiers())) - { - fail("Method \"" + testCaseName + "\" should be public."); - } - - // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or - // resulting exceptions from the method to fall through. - try - { - Integer paramN = getN(); - log.debug("paramN = " + paramN); - - // Calculate parameters for parameterized tests so new does not get called during memory measurement. - Object[] params = new Object[] { paramN }; - - // Take the test start memory and start time. - threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory(); - - threadLocalMeasurement.get().startTime = System.nanoTime(); - - if (isParameterized) - { - runMethod.invoke(this, params); - } - else - { - runMethod.invoke(this); - } - } - catch (InvocationTargetException e) - { - e.fillInStackTrace(); - throw e.getTargetException(); - } - catch (IllegalAccessException e) - { - e.fillInStackTrace(); - throw e; - } - finally - { - // Take the test end memory and end time and calculate how long it took to run. - long endTime = System.nanoTime(); - threadLocalMeasurement.get().endTime = endTime; - log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = " - + getTestTime()); - - threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory(); - } - } - - /** - * The test parameters, encapsulated as a unit for attaching on a per thread basis. - */ - private static class TestMeasurements - { - /** Holds the current value of the integer parameter to run tests on. */ - public int n = 1; - - /** Holds the test start memory. */ - public long startTime = 0; - - /** Holds the test end memory. */ - public long endTime = 0; - - /** Holds the test start memory. */ - public long startMem = 0; - - /** Holds the test end memory. */ - public long endMem = 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.junit.extensions; + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour. + * + *

      ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal + * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is + * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure + * for different numbers of data elements held in the data structure the int parameter should be interpreted as the + * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour + * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class + * will also be run. + * + *

      TestCases derived from this class may also define tear down methods to clean up their memory usage. This is + * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is + * to write a test that allocates memory in the main test method in such a way that it leaves that memory still + * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method + * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the + * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of + * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Store the current int parameter value. {@link TKTestResult} and see {@link AsymptoticTestDecorator} too. + *
      Invoke parameterized test methods. + *
      + * + * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or + * {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this + * class to do time and space performance analysis, these methods could be added to any JUnit TestCase class + * instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to + * work with this extension, and also tests that extend other junit extension classes could have parameterized + * and tear down methods too. + * + * @author Rupert Smith + */ +public class AsymptoticTestCase extends TestCase implements InstrumentedTest +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(AsymptoticTestCase.class); + + /** The name of the test case. */ + private String testCaseName; + + /** Thread local for holding measurements on a per thread basis. */ + ThreadLocal threadLocalMeasurement = + new ThreadLocal() + { + /** + * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1). + * + * @return A set of default test measurements. + */ + protected synchronized TestMeasurements initialValue() + { + return new TestMeasurements(); + } + }; + + /** + * Constructs a test case with the given name. + * + * @param name The name of the test. + */ + public AsymptoticTestCase(String name) + { + super(name); + + log.debug("public AsymptoticTestCase(String " + name + "): called"); + testCaseName = name; + } + + /** + * Gets the current value of the integer parameter to be passed to the parameterized test. + * + * @return The current value of the integer parameter. + */ + public int getN() + { + log.debug("public int getN(): called"); + int n = threadLocalMeasurement.get().n; + + log.debug("return: " + n); + + return n; + } + + /** + * Sets the current value of the integer parameter to be passed to the parameterized test. + * + * @param n The new current value of the integer parameter. + */ + public void setN(int n) + { + log.debug("public void setN(int " + n + "): called"); + threadLocalMeasurement.get().n = n; + } + + /** + * Reports how long the test took to run. + * + * @return The time in milliseconds that the test took to run. + */ + public long getTestTime() + { + log.debug("public long getTestTime(): called"); + long startTime = threadLocalMeasurement.get().startTime; + long endTime = threadLocalMeasurement.get().endTime; + long testTime = endTime - startTime; + + log.debug("return: " + testTime); + + return testTime; + } + + /** + * Reports the memory usage at the start of the test. + * + * @return The memory usage at the start of the test. + */ + public long getTestStartMemory() + { + // log.debug("public long getTestStartMemory(): called"); + long startMem = threadLocalMeasurement.get().startMem; + + // log.debug("return: " + startMem); + + return startMem; + } + + /** + * Reports the memory usage at the end of the test. + * + * @return The memory usage at the end of the test. + */ + public long getTestEndMemory() + { + // log.debug("public long getTestEndMemory(): called"); + long endMem = threadLocalMeasurement.get().endMem; + + // log.debug("return: " + endMem); + return endMem; + } + + /** + * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory + * can be reclaimed. + */ + public void reset() + { + log.debug("public void reset(): called"); + threadLocalMeasurement.remove(); + } + + /** + * Runs the test method for this test case. + * + * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through. + */ + protected void runTest() throws Throwable + { + log.debug("protected void runTest(): called"); + + // Check that a test name has been set. This is used to define which method to run. + assertNotNull(testCaseName); + log.debug("testCaseName = " + testCaseName); + + // Try to get the method with matching name. + Method runMethod = null; + boolean isParameterized = false; + + // Check if a parameterized test method is available. + try + { + // Use getMethod to get all public inherited methods. getDeclaredMethods returns all + // methods of this class but excludes the inherited ones. + runMethod = getClass().getMethod(testCaseName, int.class); + isParameterized = true; + } + catch (NoSuchMethodException e) + { + // log.debug("Parameterized method \"" + testCaseName + "\" not found."); + // Set run method to null (it already will be but...) to indicate that no parameterized method + // version could be found. + runMethod = null; + } + + // If no parameterized method is available, try and get the unparameterized method. + if (runMethod == null) + { + try + { + runMethod = getClass().getMethod(testCaseName); + isParameterized = false; + + } + catch (NoSuchMethodException e) + { + fail("Method \"" + testCaseName + "\" not found."); + } + } + + // Check that the method is publicly accessable. + if (!Modifier.isPublic(runMethod.getModifiers())) + { + fail("Method \"" + testCaseName + "\" should be public."); + } + + // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or + // resulting exceptions from the method to fall through. + try + { + Integer paramN = getN(); + log.debug("paramN = " + paramN); + + // Calculate parameters for parameterized tests so new does not get called during memory measurement. + Object[] params = new Object[] { paramN }; + + // Take the test start memory and start time. + threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory(); + + threadLocalMeasurement.get().startTime = System.nanoTime(); + + if (isParameterized) + { + runMethod.invoke(this, params); + } + else + { + runMethod.invoke(this); + } + } + catch (InvocationTargetException e) + { + e.fillInStackTrace(); + throw e.getTargetException(); + } + catch (IllegalAccessException e) + { + e.fillInStackTrace(); + throw e; + } + finally + { + // Take the test end memory and end time and calculate how long it took to run. + long endTime = System.nanoTime(); + threadLocalMeasurement.get().endTime = endTime; + log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = " + + getTestTime()); + + threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory(); + } + } + + /** + * The test parameters, encapsulated as a unit for attaching on a per thread basis. + */ + private static class TestMeasurements + { + /** Holds the current value of the integer parameter to run tests on. */ + public int n = 1; + + /** Holds the test start memory. */ + public long startTime = 0; + + /** Holds the test end memory. */ + public long endTime = 0; + + /** Holds the test start memory. */ + public long startMem = 0; + + /** Holds the test end memory. */ + public long endMem = 0; + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java index e99f904331..4faa58688f 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java @@ -1,170 +1,170 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.TestResult; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.util.MathUtils; - -/** - * A Decorator that runs a test repeatedly on an increasing int parameter, or for a fixed number of repeats. If both - * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each - * integer parameter. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Repeat a test for each of a set of integer parameters. {@link TKTestResult} - *
      Repeat a test multiple times. - *
      - *
      - * - * @author Rupert Smith - */ -public class AsymptoticTestDecorator extends WrappedSuiteTestDecorator -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(AsymptoticTestDecorator.class); - - /** The int size parameters to run the test with. */ - private int[] params; - - /** The number of times the whole test should be repeated. */ - private int repeat; - - /** - * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters - * to call the test with. - * - * @param test The test to wrap. - * @param params The integer 'size' parameters. - * @param repeat The number of times to repeat the test. - */ - public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat) - { - super(test); - - log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] " - + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called"); - - this.params = params; - this.repeat = repeat; - } - - /** - * Creates a new AsymptoticTestDecorator object. - * - * @param test The test to decorate. - * @param start The starting asymptotic integer parameter value. - * @param end The ending asymptotic integer parameter value. - * @param step The increment size to move from the start to end values by. - * @param repeat The number of times to repeat the test at each step of the cycle. - */ - public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat) - { - super(test); - - if (start < 0) - { - throw new IllegalArgumentException("Start must be >= 0"); - } - - if (end < start) - { - throw new IllegalArgumentException("End must be >= start"); - } - - if (step < 1) - { - throw new IllegalArgumentException("Step must be >= 1"); - } - - if (repeat < 1) - { - throw new IllegalArgumentException("Repeat must be >= 1"); - } - - // Generate the sequence. - params = new int[((end - start) / step) + 1]; - int i = 0; - for (int n = start; n <= end; n += step) - { - params[i++] = n; - } - - this.repeat = repeat; - } - - /** - * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test - * repeats. - * - * @param result The test result object that the tests will indicate their results to. This is also used - * to pass the int parameter from this class to the decorated test class. - */ - public void run(TestResult result) - { - log.debug("public void run(TestResult result): called"); - - if (!(result instanceof TKTestResult)) - { - throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult"); - } - - // Cast the test result into a TKTestResult to place the current parameter into. - TKTestResult tkResult = (TKTestResult) result; - - log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); - log.debug("repeat = " + repeat); - - for (int n : params) - { - for (int j = 0; j < repeat; j++) - { - log.debug("n = " + n); - - // Set the integer parameter in the TKTestResult to be passed to the tests. - tkResult.setN(n); - - if (tkResult.shouldStop()) - { - log.debug("tkResult.shouldStop = " + true); - - break; - } - - log.debug("Calling super#run"); - super.run(tkResult); - } - } - } - - /** - * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes. - * - * @return The name of this test with the string "(parameterized)" appended onto it. - */ - public String toString() - { - return super.toString() + "(parameterized)"; - } -} +/* + * + * 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.junit.extensions; + +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.util.MathUtils; + +/** + * A Decorator that runs a test repeatedly on an increasing int parameter, or for a fixed number of repeats. If both + * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each + * integer parameter. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Repeat a test for each of a set of integer parameters. {@link TKTestResult} + *
      Repeat a test multiple times. + *
      + *
      + * + * @author Rupert Smith + */ +public class AsymptoticTestDecorator extends WrappedSuiteTestDecorator +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(AsymptoticTestDecorator.class); + + /** The int size parameters to run the test with. */ + private int[] params; + + /** The number of times the whole test should be repeated. */ + private int repeat; + + /** + * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters + * to call the test with. + * + * @param test The test to wrap. + * @param params The integer 'size' parameters. + * @param repeat The number of times to repeat the test. + */ + public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat) + { + super(test); + + log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] " + + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called"); + + this.params = params; + this.repeat = repeat; + } + + /** + * Creates a new AsymptoticTestDecorator object. + * + * @param test The test to decorate. + * @param start The starting asymptotic integer parameter value. + * @param end The ending asymptotic integer parameter value. + * @param step The increment size to move from the start to end values by. + * @param repeat The number of times to repeat the test at each step of the cycle. + */ + public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat) + { + super(test); + + if (start < 0) + { + throw new IllegalArgumentException("Start must be >= 0"); + } + + if (end < start) + { + throw new IllegalArgumentException("End must be >= start"); + } + + if (step < 1) + { + throw new IllegalArgumentException("Step must be >= 1"); + } + + if (repeat < 1) + { + throw new IllegalArgumentException("Repeat must be >= 1"); + } + + // Generate the sequence. + params = new int[((end - start) / step) + 1]; + int i = 0; + for (int n = start; n <= end; n += step) + { + params[i++] = n; + } + + this.repeat = repeat; + } + + /** + * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test + * repeats. + * + * @param result The test result object that the tests will indicate their results to. This is also used + * to pass the int parameter from this class to the decorated test class. + */ + public void run(TestResult result) + { + log.debug("public void run(TestResult result): called"); + + if (!(result instanceof TKTestResult)) + { + throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult"); + } + + // Cast the test result into a TKTestResult to place the current parameter into. + TKTestResult tkResult = (TKTestResult) result; + + log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); + log.debug("repeat = " + repeat); + + for (int n : params) + { + for (int j = 0; j < repeat; j++) + { + log.debug("n = " + n); + + // Set the integer parameter in the TKTestResult to be passed to the tests. + tkResult.setN(n); + + if (tkResult.shouldStop()) + { + log.debug("tkResult.shouldStop = " + true); + + break; + } + + log.debug("Calling super#run"); + super.run(tkResult); + } + } + } + + /** + * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes. + * + * @return The name of this test with the string "(parameterized)" appended onto it. + */ + public String toString() + { + return super.toString() + "(parameterized)"; + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java index e8e203f0a3..61d5746421 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java @@ -1,98 +1,98 @@ -/* - * - * 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.junit.extensions; - -/** - * Provides a base implementation of the non-waiting throttle checking method, using the system nano timer. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Check against a throttle speed without waiting. - *
      - * - * @author Rupert Smith - */ -public abstract class BaseThrottle implements Throttle -{ - /** Holds the length of a single cycle in nano seconds. */ - protected long cycleTimeNanos; - - /** Holds the time of the last succesfull call to the check method. */ - private long lastCheckTimeNanos; - - /** Flag used to detect the first call to the {@link #checkThrottle()} method. */ - boolean firstCheckCall = true; - - /** - * Flag used to detect the first call to the {@link #throttle()} method. Zero or negative start time cannot be - * relied on to detect this as System.nanoTime can return zero or negative values. - */ - boolean firstCall = true; - - /** - * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse - * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer. - * The value must also be larger than zero. - * - * @param hertz The throttling rate in cycles per second. - */ - public void setRate(float hertz) - { - // Check that the argument is above zero. - if (hertz <= 0.0f) - { - throw new IllegalArgumentException("The throttle rate must be above zero."); - } - - // Calculate the cycle time. - cycleTimeNanos = (long) (1000000000f / hertz); - - // Reset the first pass flag. - firstCall = false; - firstCheckCall = false; - } - - /** - * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater - * than that equal to the inverse of the throttling rate has passed since it was last called and returned true - * - * @return true if a length of time greater than that equal to the inverse of the throttling rate has - * passed since this method was last called and returned true, false otherwise. The very - * first time this method is called on a throttle, it returns true as the base case to the above - * self-referential definition. - */ - public boolean checkThrottle() - { - long now = System.nanoTime(); - - if ((now > (cycleTimeNanos + lastCheckTimeNanos)) || firstCheckCall) - { - firstCheckCall = false; - lastCheckTimeNanos = now; - - return true; - } - else - { - return false; - } - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.junit.extensions; + +/** + * Provides a base implementation of the non-waiting throttle checking method, using the system nano timer. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Check against a throttle speed without waiting. + *
      + * + * @author Rupert Smith + */ +public abstract class BaseThrottle implements Throttle +{ + /** Holds the length of a single cycle in nano seconds. */ + protected long cycleTimeNanos; + + /** Holds the time of the last succesfull call to the check method. */ + private long lastCheckTimeNanos; + + /** Flag used to detect the first call to the {@link #checkThrottle()} method. */ + boolean firstCheckCall = true; + + /** + * Flag used to detect the first call to the {@link #throttle()} method. Zero or negative start time cannot be + * relied on to detect this as System.nanoTime can return zero or negative values. + */ + boolean firstCall = true; + + /** + * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse + * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer. + * The value must also be larger than zero. + * + * @param hertz The throttling rate in cycles per second. + */ + public void setRate(float hertz) + { + // Check that the argument is above zero. + if (hertz <= 0.0f) + { + throw new IllegalArgumentException("The throttle rate must be above zero."); + } + + // Calculate the cycle time. + cycleTimeNanos = (long) (1000000000f / hertz); + + // Reset the first pass flag. + firstCall = false; + firstCheckCall = false; + } + + /** + * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater + * than that equal to the inverse of the throttling rate has passed since it was last called and returned true + * + * @return true if a length of time greater than that equal to the inverse of the throttling rate has + * passed since this method was last called and returned true, false otherwise. The very + * first time this method is called on a throttle, it returns true as the base case to the above + * self-referential definition. + */ + public boolean checkThrottle() + { + long now = System.nanoTime(); + + if ((now > (cycleTimeNanos + lastCheckTimeNanos)) || firstCheckCall) + { + firstCheckCall = false; + lastCheckTimeNanos = now; + + return true; + } + else + { + return false; + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java index 1d00fcf3b6..241e7aa2b7 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java @@ -1,94 +1,94 @@ -/* - * - * 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.junit.extensions; - -/** - * BatchedThrottle is a {@link SleepThrottle} that uses batching to achieve much higher throttling rates than a sleep - * throttle can. Sleep throttle has difficulties once the rate gets above a few hundred hertz, because the JVM cannot - * generate timed pauses that are that short. BatchedThrottle gets around this by only inserting pauses once every so - * many calls to the {@link #throttle()} method, and using a sleep throttle run at a lower rate. The rate for the sleep - * throttle is chosen so that it remains under 100hz. The final throttling rate of this throttle is equal to the batch - * size times the rate of the underlying sleep throttle. - * - *

      The batching calculation involves taking the log to the base 100 of the desired rate and rounding this to - * an integer. The batch size is always an exact power of 100 because of the rounding. The rate for an underlying - * sleep throttle is then chosen appropriately. - * - *

      In practice, the accuracy of a BacthedThrottle skews off but can sometimes even be reasonable up to ten thousand - * hertz compared with 100 Hz for a {@link SleepThrottle}. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Accept throttling rate in operations per second. - *
      Inject short pauses, occasionaly, to fill out processing cycles to a specified rate. - *
      Check against a throttle speed without waiting. - *
      - * - * @todo Should always round the log base 100 down to the nearest integer? - * - * @author Rupert Smith - */ -public class BatchedThrottle extends BaseThrottle -{ - /** Holds the batch size. */ - int batchSize; - - /** The call count within the current batch. */ - long callCount; - - /** Holds a sleep throttle configured to run at the batched rate. */ - private Throttle batchRateThrottle = new SleepThrottle(); - - /** - * Specifies the throttling rate in operations per second. - * - * @param hertz The throttling rate in cycles per second. - */ - public void setRate(float hertz) - { - // Pass the rate unaltered down to the base implementation, for the check method. - super.setRate(hertz); - - // Log base 10 over 2 is used here to get a feel for what power of 100 the total rate is. - // As the total rate goes up the powers of 100 the batch size goes up by powers of 100 to keep the - // throttle rate in the range 1 to 100. - int x = (int) (Math.log10(hertz) / 2); - batchSize = (int) Math.pow(100, x); - float throttleRate = hertz / batchSize; - - // Reset the call count. - callCount = 0; - - // Set the sleep throttle wrapped implementation at a rate within its abilities. - batchRateThrottle.setRate(throttleRate); - } - - /** - * Throttle calls to this method to the rate specified by the {@link #setRate(float)} method. - */ - public void throttle() - { - if ((callCount++ % batchSize) == 0) - { - batchRateThrottle.throttle(); - } - } -} +/* + * + * 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.junit.extensions; + +/** + * BatchedThrottle is a {@link SleepThrottle} that uses batching to achieve much higher throttling rates than a sleep + * throttle can. Sleep throttle has difficulties once the rate gets above a few hundred hertz, because the JVM cannot + * generate timed pauses that are that short. BatchedThrottle gets around this by only inserting pauses once every so + * many calls to the {@link #throttle()} method, and using a sleep throttle run at a lower rate. The rate for the sleep + * throttle is chosen so that it remains under 100hz. The final throttling rate of this throttle is equal to the batch + * size times the rate of the underlying sleep throttle. + * + *

      The batching calculation involves taking the log to the base 100 of the desired rate and rounding this to + * an integer. The batch size is always an exact power of 100 because of the rounding. The rate for an underlying + * sleep throttle is then chosen appropriately. + * + *

      In practice, the accuracy of a BacthedThrottle skews off but can sometimes even be reasonable up to ten thousand + * hertz compared with 100 Hz for a {@link SleepThrottle}. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Accept throttling rate in operations per second. + *
      Inject short pauses, occasionaly, to fill out processing cycles to a specified rate. + *
      Check against a throttle speed without waiting. + *
      + * + * @todo Should always round the log base 100 down to the nearest integer? + * + * @author Rupert Smith + */ +public class BatchedThrottle extends BaseThrottle +{ + /** Holds the batch size. */ + int batchSize; + + /** The call count within the current batch. */ + long callCount; + + /** Holds a sleep throttle configured to run at the batched rate. */ + private Throttle batchRateThrottle = new SleepThrottle(); + + /** + * Specifies the throttling rate in operations per second. + * + * @param hertz The throttling rate in cycles per second. + */ + public void setRate(float hertz) + { + // Pass the rate unaltered down to the base implementation, for the check method. + super.setRate(hertz); + + // Log base 10 over 2 is used here to get a feel for what power of 100 the total rate is. + // As the total rate goes up the powers of 100 the batch size goes up by powers of 100 to keep the + // throttle rate in the range 1 to 100. + int x = (int) (Math.log10(hertz) / 2); + batchSize = (int) Math.pow(100, x); + float throttleRate = hertz / batchSize; + + // Reset the call count. + callCount = 0; + + // Set the sleep throttle wrapped implementation at a rate within its abilities. + batchRateThrottle.setRate(throttleRate); + } + + /** + * Throttle calls to this method to the rate specified by the {@link #setRate(float)} method. + */ + public void throttle() + { + if ((callCount++ % batchSize) == 0) + { + batchRateThrottle.throttle(); + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java index fe1e044e67..1c1c146361 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java @@ -1,199 +1,199 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.Test; -import junit.framework.TestResult; - -import org.apache.log4j.Logger; - -import java.util.Timer; -import java.util.TimerTask; - -/** - * A test decorator that runs a test repeatedly until a specified length of time has passed. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Repeatedly run a test for a fixed length of time. - *
      - * - * @todo The count of the number of tests run is an important number to keep. Also num passed/error/failed is also - * important to record. What to do with these numbers? They are already logged to the test listeners. - * - * @todo The duration test runner wraps on top of size, repeat or thread wrappers, need a way for it to tell - * TKTestResult when the duration is up, so that it can terminate any repeats in progress. It should end - * as soon as possible once the test method exits. - * - * @author Rupert Smith - */ -public class DurationTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(DurationTestDecorator.class); - - /** The test to run. */ - private Test test; - - /** The length of time to run the test for. */ - private long duration; - - /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */ - private boolean shutdown = false; - - /** - * Creates an active test with default multiplier (1). - * - * @param test The target test. - */ - public DurationTestDecorator(WrappedSuiteTestDecorator test) - { - super(test); - this.test = test; - } - - /** - * Creates active test with default multiplier (1). - * - * @param test The target test. - * @param duration The duration in milliseconds. - */ - public DurationTestDecorator(WrappedSuiteTestDecorator test, long duration) - { - super(test); - - // log.debug("public DurationTestDecorator(Test \"" + test + "\", long " + duration + "): called"); - - this.test = test; - this.duration = duration; - } - - /** - * Runs the test repeatedly for the fixed duration. - * - * @param testResult The the results object to monitor the test results with. - */ - public void run(TestResult testResult) - { - log.debug("public void run(TestResult testResult): called"); - - // Cast the test result to expose it as a TKTestResult if the test is running under the TKTestRunner. - TKTestResult tkTestResult = null; - - if (testResult instanceof TKTestResult) - { - tkTestResult = (TKTestResult) testResult; - } - - // Work out when the test should end. - long now = System.nanoTime(); - long end = (duration * 1000000) + now; - - // If running under the TKTestRunner, set up a timer to notify the test framework when the test reaches its - // completion time. - Timer durationTimer = null; - - if (tkTestResult != null) - { - log.debug("Creating duration timer."); - - durationTimer = new Timer(); - durationTimer.schedule(new DurationTimerTask((TKTestResult) testResult), duration); - } - - // Run the test until the duration times out or the shutdown flag is set. The test method may not exit until - // interrupted in some cases, in which case the timer will do the interrupting. - while ((now < end) && !shutdown) - { - test.run(testResult); - - now = System.nanoTime(); - } - - // Clean up any timer that was used. - if (durationTimer != null) - { - log.debug("Cancelling duration timer."); - - durationTimer.cancel(); - } - } - - /** - * Supplies the shutdown hook. This shutdown hook does not call {@link TKTestResult#shutdownNow()} because the - * {@link ScaledTestDecorator} already takes care of that. - * - * @return The shut down hook. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - // log.debug("DurationTestDecorator::ShutdownHook: called"); - - // Set the shutdown flag so that no new tests are started. - shutdown = true; - } - }); - } - - /** - * DurationTimerTask is a timer task that is configured, upon expiry of its timer, to invoke - * {@link TKTestResult#shutdownNow()}, for the test result object on which it is set. It also sets - * the {@link DurationTestDecorator#shutdown} flag to indicate that no new tests should be run. - * - *

      The test loop implemented by DurationTestDecorator checks that the duration has not expired, on each - * test case that it runs. However, it is possible to write test cases that never return until explicitly - * interrupted by the test framework. This timer task exists to notify the test framework - */ - private class DurationTimerTask extends TimerTask - { - /** Used for debugging purposes. */ - private final Logger log = Logger.getLogger(DurationTimerTask.class); - - /** Holds the test result for the test to which a duration limit is being applied. */ - TKTestResult testResult; - - /** - * Creates a duration limit timer which will notify the specified test result when the duration has - * expired. - * - * @param testResult The test result to notify upon expiry of the test duration. - */ - public DurationTimerTask(TKTestResult testResult) - { - this.testResult = testResult; - } - - /** - * The action to be performed by this timer task. - */ - public void run() - { - log.debug("public void run(): called"); - - shutdown = true; - testResult.shutdownNow(); - } - } -} +/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import java.util.Timer; +import java.util.TimerTask; + +/** + * A test decorator that runs a test repeatedly until a specified length of time has passed. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Repeatedly run a test for a fixed length of time. + *
      + * + * @todo The count of the number of tests run is an important number to keep. Also num passed/error/failed is also + * important to record. What to do with these numbers? They are already logged to the test listeners. + * + * @todo The duration test runner wraps on top of size, repeat or thread wrappers, need a way for it to tell + * TKTestResult when the duration is up, so that it can terminate any repeats in progress. It should end + * as soon as possible once the test method exits. + * + * @author Rupert Smith + */ +public class DurationTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(DurationTestDecorator.class); + + /** The test to run. */ + private Test test; + + /** The length of time to run the test for. */ + private long duration; + + /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */ + private boolean shutdown = false; + + /** + * Creates an active test with default multiplier (1). + * + * @param test The target test. + */ + public DurationTestDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Creates active test with default multiplier (1). + * + * @param test The target test. + * @param duration The duration in milliseconds. + */ + public DurationTestDecorator(WrappedSuiteTestDecorator test, long duration) + { + super(test); + + // log.debug("public DurationTestDecorator(Test \"" + test + "\", long " + duration + "): called"); + + this.test = test; + this.duration = duration; + } + + /** + * Runs the test repeatedly for the fixed duration. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + log.debug("public void run(TestResult testResult): called"); + + // Cast the test result to expose it as a TKTestResult if the test is running under the TKTestRunner. + TKTestResult tkTestResult = null; + + if (testResult instanceof TKTestResult) + { + tkTestResult = (TKTestResult) testResult; + } + + // Work out when the test should end. + long now = System.nanoTime(); + long end = (duration * 1000000) + now; + + // If running under the TKTestRunner, set up a timer to notify the test framework when the test reaches its + // completion time. + Timer durationTimer = null; + + if (tkTestResult != null) + { + log.debug("Creating duration timer."); + + durationTimer = new Timer(); + durationTimer.schedule(new DurationTimerTask((TKTestResult) testResult), duration); + } + + // Run the test until the duration times out or the shutdown flag is set. The test method may not exit until + // interrupted in some cases, in which case the timer will do the interrupting. + while ((now < end) && !shutdown) + { + test.run(testResult); + + now = System.nanoTime(); + } + + // Clean up any timer that was used. + if (durationTimer != null) + { + log.debug("Cancelling duration timer."); + + durationTimer.cancel(); + } + } + + /** + * Supplies the shutdown hook. This shutdown hook does not call {@link TKTestResult#shutdownNow()} because the + * {@link ScaledTestDecorator} already takes care of that. + * + * @return The shut down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + // log.debug("DurationTestDecorator::ShutdownHook: called"); + + // Set the shutdown flag so that no new tests are started. + shutdown = true; + } + }); + } + + /** + * DurationTimerTask is a timer task that is configured, upon expiry of its timer, to invoke + * {@link TKTestResult#shutdownNow()}, for the test result object on which it is set. It also sets + * the {@link DurationTestDecorator#shutdown} flag to indicate that no new tests should be run. + * + *

      The test loop implemented by DurationTestDecorator checks that the duration has not expired, on each + * test case that it runs. However, it is possible to write test cases that never return until explicitly + * interrupted by the test framework. This timer task exists to notify the test framework + */ + private class DurationTimerTask extends TimerTask + { + /** Used for debugging purposes. */ + private final Logger log = Logger.getLogger(DurationTimerTask.class); + + /** Holds the test result for the test to which a duration limit is being applied. */ + TKTestResult testResult; + + /** + * Creates a duration limit timer which will notify the specified test result when the duration has + * expired. + * + * @param testResult The test result to notify upon expiry of the test duration. + */ + public DurationTimerTask(TKTestResult testResult) + { + this.testResult = testResult; + } + + /** + * The action to be performed by this timer task. + */ + public void run() + { + log.debug("public void run(): called"); + + shutdown = true; + testResult.shutdownNow(); + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java index ed792fcd5a..0804757dce 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java @@ -1,66 +1,66 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.Test; - -/** - * An InstrumentedTest is one which can supply some additional instrumentation on top of the pass/fail/error behaviour - * of normal junit tests. Tests implementing this interface must additionally supply information about how long they - * took to run and how much memory they used. - * - *

      - *
      CRC Card
      Responsibilities - *
      Report test run time. - *
      Report test memory usage. - *
      - * - * @author Rupert Smith - */ -public interface InstrumentedTest extends Test -{ - /** - * Reports how long the test took to run. - * - * @return The time in milliseconds that the test took to run. - */ - public long getTestTime(); - - /** - * Reports the memory usage at the start of the test. - * - * @return The memory usage at the start of the test. - */ - public long getTestStartMemory(); - - /** - * Reports the memory usage at the end of the test. - * - * @return The memory usage at the end of the test. - */ - public long getTestEndMemory(); - - /** - * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory - * can be reclaimed. - */ - public void reset(); -} +/* + * + * 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.junit.extensions; + +import junit.framework.Test; + +/** + * An InstrumentedTest is one which can supply some additional instrumentation on top of the pass/fail/error behaviour + * of normal junit tests. Tests implementing this interface must additionally supply information about how long they + * took to run and how much memory they used. + * + *

      + *
      CRC Card
      Responsibilities + *
      Report test run time. + *
      Report test memory usage. + *
      + * + * @author Rupert Smith + */ +public interface InstrumentedTest extends Test +{ + /** + * Reports how long the test took to run. + * + * @return The time in milliseconds that the test took to run. + */ + public long getTestTime(); + + /** + * Reports the memory usage at the start of the test. + * + * @return The memory usage at the start of the test. + */ + public long getTestStartMemory(); + + /** + * Reports the memory usage at the end of the test. + * + * @return The memory usage at the end of the test. + */ + public long getTestEndMemory(); + + /** + * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory + * can be reclaimed. + */ + public void reset(); +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java index 2ffbcb5bb8..6727f6f152 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java @@ -1,92 +1,92 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.AssertionFailedError; -import junit.framework.Test; - -import junit.textui.ResultPrinter; - -import java.io.PrintStream; - -/** - * A ResultPrinter that prints nothing. This exists, in order to provide a replacement to JUnit's ResultPrinter, which - * is refered to directly by JUnit code, rather that as an abstracted TestListener. JUnit's text ui TestRunner must - * have a ResultPrinter. This provides an implementation of it that prints nothing, so that a better mechanism can - * be used for providing feedback to the console instead. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - *
      - * - * @todo See todo in TKTestRunner about completely replacing the test ui runner. Doing things like this in order to - * extend JUnit is not nice, and there needs to be a better way to do it. Delete this class and use a listener - * instead. - * - * @author Rupert Smith - */ -public class NullResultPrinter extends ResultPrinter -{ - /** - * Builds a fake ResultPrinter that prints nothing. - * - * @param writer The writer to send output to. - */ - public NullResultPrinter(PrintStream writer) - { - super(writer); - } - - /** - * Does nothing. - * - * @param test Ignored. - * @param t Ignored. - */ - public void addError(Test test, Throwable t) - { } - - /** - * Does nothing. - * - * @param test Ignored. - * @param t Ignored. - */ - public void addFailure(Test test, AssertionFailedError t) - { } - - /** - * Does nothing. - * - * @param test Ignored. - */ - public void endTest(Test test) - { } - - /** - * Does nothing. - * - * @param test Ignored. - */ - public void startTest(Test test) - { } -} +/* + * + * 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.junit.extensions; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; + +import junit.textui.ResultPrinter; + +import java.io.PrintStream; + +/** + * A ResultPrinter that prints nothing. This exists, in order to provide a replacement to JUnit's ResultPrinter, which + * is refered to directly by JUnit code, rather that as an abstracted TestListener. JUnit's text ui TestRunner must + * have a ResultPrinter. This provides an implementation of it that prints nothing, so that a better mechanism can + * be used for providing feedback to the console instead. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + *
      + * + * @todo See todo in TKTestRunner about completely replacing the test ui runner. Doing things like this in order to + * extend JUnit is not nice, and there needs to be a better way to do it. Delete this class and use a listener + * instead. + * + * @author Rupert Smith + */ +public class NullResultPrinter extends ResultPrinter +{ + /** + * Builds a fake ResultPrinter that prints nothing. + * + * @param writer The writer to send output to. + */ + public NullResultPrinter(PrintStream writer) + { + super(writer); + } + + /** + * Does nothing. + * + * @param test Ignored. + * @param t Ignored. + */ + public void addError(Test test, Throwable t) + { } + + /** + * Does nothing. + * + * @param test Ignored. + * @param t Ignored. + */ + public void addFailure(Test test, AssertionFailedError t) + { } + + /** + * Does nothing. + * + * @param test Ignored. + */ + public void endTest(Test test) + { } + + /** + * Does nothing. + * + * @param test Ignored. + */ + public void startTest(Test test) + { } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java index 60ec156354..2c207635c7 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java @@ -1,172 +1,172 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.junit.extensions; - -import junit.framework.TestResult; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.util.MathUtils; - -/** - * ParameterVariationTestDecorator is a test decorator that runs a test repeatedly under all permutations of its - * test parameters. - * - * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each - * integer parameter. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Repeat a test for each of a set of integer parameters. {@link org.apache.qpid.junit.extensions.TKTestResult} - *
      Repeat a test multiple times. - *
      - *
      - * - * @author Rupert Smith - */ -public class ParameterVariationTestDecorator extends WrappedSuiteTestDecorator -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(ParameterVariationTestDecorator.class); - - /** The int size parameters to run the test with. */ - private int[] params; - - /** The number of times the whole test should be repeated. */ - private int repeat; - - /** - * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters - * to call the test with. - * - * @param test The test to wrap. - * @param params The integer 'size' parameters. - * @param repeat The number of times to repeat the test. - */ - public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat) - { - super(test); - - log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] " - + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called"); - - this.params = params; - this.repeat = repeat; - } - - /** - * Creates a new AsymptoticTestDecorator object. - * - * @param test The test to decorate. - * @param start The starting asymptotic integer parameter value. - * @param end The ending asymptotic integer parameter value. - * @param step The increment size to move from the start to end values by. - * @param repeat The number of times to repeat the test at each step of the cycle. - */ - public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat) - { - super(test); - - if (start < 0) - { - throw new IllegalArgumentException("Start must be >= 0"); - } - - if (end < start) - { - throw new IllegalArgumentException("End must be >= start"); - } - - if (step < 1) - { - throw new IllegalArgumentException("Step must be >= 1"); - } - - if (repeat < 1) - { - throw new IllegalArgumentException("Repeat must be >= 1"); - } - - // Generate the sequence. - params = new int[((end - start) / step) + 1]; - int i = 0; - for (int n = start; n <= end; n += step) - { - params[i++] = n; - } - - this.repeat = repeat; - } - - /** - * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test - * repeats. - * - * @param result The test result object that the tests will indicate their results to. This is also used - * to pass the int parameter from this class to the decorated test class. - */ - public void run(TestResult result) - { - log.debug("public void run(TestResult result): called"); - - if (!(result instanceof TKTestResult)) - { - throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult"); - } - - // Cast the test result into a TKTestResult to place the current parameter into. - TKTestResult tkResult = (TKTestResult) result; - - log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); - log.debug("repeat = " + repeat); - - for (int n : params) - { - for (int j = 0; j < repeat; j++) - { - log.debug("n = " + n); - - // Set the integer parameter in the TKTestResult to be passed to the tests. - tkResult.setN(n); - - if (tkResult.shouldStop()) - { - log.debug("tkResult.shouldStop = " + true); - - break; - } - - log.debug("Calling super#run"); - super.run(tkResult); - } - } - } - - /** - * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes. - * - * @return The name of this test with the string "(parameterized)" appended onto it. - */ - public String toString() - { - return super.toString() + "(parameterized)"; - } -} +/* + * + * 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.junit.extensions; + +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.util.MathUtils; + +/** + * ParameterVariationTestDecorator is a test decorator that runs a test repeatedly under all permutations of its + * test parameters. + * + * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each + * integer parameter. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Repeat a test for each of a set of integer parameters. {@link org.apache.qpid.junit.extensions.TKTestResult} + *
      Repeat a test multiple times. + *
      + *
      + * + * @author Rupert Smith + */ +public class ParameterVariationTestDecorator extends WrappedSuiteTestDecorator +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(ParameterVariationTestDecorator.class); + + /** The int size parameters to run the test with. */ + private int[] params; + + /** The number of times the whole test should be repeated. */ + private int repeat; + + /** + * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters + * to call the test with. + * + * @param test The test to wrap. + * @param params The integer 'size' parameters. + * @param repeat The number of times to repeat the test. + */ + public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat) + { + super(test); + + log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] " + + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called"); + + this.params = params; + this.repeat = repeat; + } + + /** + * Creates a new AsymptoticTestDecorator object. + * + * @param test The test to decorate. + * @param start The starting asymptotic integer parameter value. + * @param end The ending asymptotic integer parameter value. + * @param step The increment size to move from the start to end values by. + * @param repeat The number of times to repeat the test at each step of the cycle. + */ + public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat) + { + super(test); + + if (start < 0) + { + throw new IllegalArgumentException("Start must be >= 0"); + } + + if (end < start) + { + throw new IllegalArgumentException("End must be >= start"); + } + + if (step < 1) + { + throw new IllegalArgumentException("Step must be >= 1"); + } + + if (repeat < 1) + { + throw new IllegalArgumentException("Repeat must be >= 1"); + } + + // Generate the sequence. + params = new int[((end - start) / step) + 1]; + int i = 0; + for (int n = start; n <= end; n += step) + { + params[i++] = n; + } + + this.repeat = repeat; + } + + /** + * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test + * repeats. + * + * @param result The test result object that the tests will indicate their results to. This is also used + * to pass the int parameter from this class to the decorated test class. + */ + public void run(TestResult result) + { + log.debug("public void run(TestResult result): called"); + + if (!(result instanceof TKTestResult)) + { + throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult"); + } + + // Cast the test result into a TKTestResult to place the current parameter into. + TKTestResult tkResult = (TKTestResult) result; + + log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); + log.debug("repeat = " + repeat); + + for (int n : params) + { + for (int j = 0; j < repeat; j++) + { + log.debug("n = " + n); + + // Set the integer parameter in the TKTestResult to be passed to the tests. + tkResult.setN(n); + + if (tkResult.shouldStop()) + { + log.debug("tkResult.shouldStop = " + true); + + break; + } + + log.debug("Calling super#run"); + super.run(tkResult); + } + } + } + + /** + * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes. + * + * @return The name of this test with the string "(parameterized)" appended onto it. + */ + public String toString() + { + return super.toString() + "(parameterized)"; + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java index f5e3e1a758..e0af22cfb7 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java @@ -1,375 +1,375 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.Test; -import junit.framework.TestResult; - -import java.util.concurrent.BrokenBarrierException; -import java.util.concurrent.CyclicBarrier; - -/** - * A test decorator that runs a test many times simultaneously in many threads. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Clone a test run into many threads and run them simultaneously. - *
      Inform the test results of the start and end of each concurrent test batch. {@link TKTestResult} - *
      Inform the test results of the concurrency level. {@link TKTestResult} - *
      - * - * @author Rupert Smith - */ -public class ScaledTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable // TestDecorator -{ - /** Used for logging. */ - // private static final Logger log = Logger.getLogger(ScaledTestDecorator.class); - - /** Determines how long to wait for tests to cleanly exit on shutdown. */ - private static final long SHUTDOWN_PAUSE = 3000; - - /** - * The stress levels or numbers of simultaneous threads to run the test in. The test is repeated at each of - * the concurrency levels specified here. Defaults to 1 thread. - */ - private int[] threads = new int[] { 1 }; - - /** Used to hold the number of tests currently being run in parallel. */ - private int concurrencyLevel; - - /** The test to run. */ - private WrappedSuiteTestDecorator test; - - /** - * Used to hold the current {@link TKTestResult} for the tests currently being run. This is made available so that - * the shutdown hook can ask it to cleanly end the current tests in the event of a shutdown. - */ - private TKTestResult currentTestResult; - - /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */ - private boolean shutdown = false; - - /** - * Creates an active test with default multiplier (1). - * - * @param test The target test. - */ - public ScaledTestDecorator(WrappedSuiteTestDecorator test) - { - super(test); - this.test = test; - } - - /** - * Creates a concurrently scaled test with the specified number of threads. - * - * @param test The target test. - * @param numThreads The stress level. - */ - public ScaledTestDecorator(WrappedSuiteTestDecorator test, int numThreads) - { - this(test, new int[] { numThreads }); - } - - /** - * Creates a concurrently scaled test with the specified thread levels, the test is repeated at each level. - * - * @param test The target test. - * @param threads The concurrency levels. - */ - public ScaledTestDecorator(WrappedSuiteTestDecorator test, int[] threads) - { - super(test); - - /*log.debug("public ScaledTestDecorator(WrappedSuiteTestDecorator test = \"" + test + "\", int[] threads = " - + MathUtils.printArray(threads) + "): called");*/ - - this.test = test; - this.threads = threads; - } - - /** - * Runs the test simultaneously in at the specified concurrency levels. - * - * @param testResult The results object to monitor the test results with. - */ - public void run(TestResult testResult) - { - // log.debug("public void run(TestResult testResult = " + testResult + "): called"); - - // Loop through all of the specified concurrent levels for the test, provided shutdown has not been called. - for (int i = 0; (i < threads.length) && !shutdown; i++) - { - // Get the number of threads for this run. - int numThreads = threads[i]; - - // Create test thread handlers for all the threads. - TestThreadHandler[] threadHandlers = new TestThreadHandler[numThreads]; - - // Create a cyclic barrier for the test threads to synch their setups and teardowns on. - CyclicBarrier barrier = new CyclicBarrier(numThreads); - - // Set up the test thread handlers to output results to the same test results object. - for (int j = 0; j < numThreads; j++) - { - threadHandlers[j] = new TestThreadHandler(testResult, test, barrier); - } - - // Ensure the concurrency level statistic is set up correctly. - concurrencyLevel = numThreads; - - // Begin batch. - if (testResult instanceof TKTestResult) - { - TKTestResult tkResult = (TKTestResult) testResult; - // tkResult.notifyStartBatch(); - tkResult.setConcurrencyLevel(numThreads); - - // Set the test result for the currently running tests, so that the shutdown hook can call it if necessary. - currentTestResult = tkResult; - } - - // Run all the tests and wait for them all to finish. - executeAndWaitForRunnables(threadHandlers); - - // Clear the test result for the currently running tests. - currentTestResult = null; - - // End batch. - if (testResult instanceof TKTestResult) - { - TKTestResult tkResult = (TKTestResult) testResult; - tkResult.notifyEndBatch(); - } - - // Clear up all the test threads, they hold references to their associated TestResult object and Test object, - // which may prevent them from being garbage collected as the TestResult and Test objects are long lived. - for (int j = 0; j < numThreads; j++) - { - threadHandlers[j].testResult = null; - threadHandlers[j].test = null; - threadHandlers[j] = null; - } - } - } - - /** - * Reports the number of tests that the scaled decorator is currently running concurrently. - * - * @return The number of tests that the scaled decorator is currently running concurrently. - */ - public int getConcurrencyLevel() - { - return concurrencyLevel; - } - - /** - * Executes all of the specifed runnable using the thread pool and waits for them all to complete. - * - * @param runnables The set of runnables to execute concurrently. - */ - private void executeAndWaitForRunnables(Runnable[] runnables) - { - int numThreads = runnables.length; - - // Used to keep track of the test threads in order to know when they have all completed. - Thread[] threads = new Thread[numThreads]; - - // Create all the test threads. - for (int j = 0; j < numThreads; j++) - { - threads[j] = new Thread(runnables[j]); - } - - // Start all the test threads. - for (int j = 0; j < numThreads; j++) - { - threads[j].start(); - } - - // Wait for all the test threads to complete. - for (int j = 0; j < numThreads; j++) - { - try - { - threads[j].join(); - } - catch (InterruptedException e) - { - // Restore the interrupted state of the thread. - Thread.currentThread().interrupt(); - } - } - } - - /** - * Supplies the shut-down hook. - * - * @return The shut-down hook. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - // log.debug("ScaledTestDecorator::ShutdownHook: called"); - - // Set the shutdown flag so that no new tests are started. - shutdown = true; - - // Check if tests are currently running, and ask them to complete as soon as possible. Allow - // a short pause for this to happen. - TKTestResult testResult = currentTestResult; - - if (testResult != null) - { - // log.debug("There is a test result currently running tests, asking it to terminate ASAP."); - testResult.shutdownNow(); - - try - { - Thread.sleep(SHUTDOWN_PAUSE); - } - catch (InterruptedException e) - { - // Restore the interrupted state of the thread. - Thread.currentThread().interrupt(); - } - } - } - }); - } - - /** - * Prints a string summarizing this test decorator, mainly for debugging purposes. - * - * @return String representation for debugging purposes. - */ - public String toString() - { - return "ScaledTestDecorator: [ test = " + test + ", concurrencyLevel = " + concurrencyLevel + " ]"; - } - - /** - * TestThreadHandler is a runnable used to execute a test in. This is static to avoid implicit 'this' reference to - * the longer lived ScaledTestDecorator class. The scaled test decorator may execute many repeats but creates fresh - * handlers for each one. It re-uses the threads in a pool but does not re-use these handlers. - */ - private static class TestThreadHandler implements Runnable - { - /** The test result object for the test to be run with. */ - TestResult testResult; - - /** The test to run. */ - WrappedSuiteTestDecorator test; - - /** Holds the cyclic barrier to synchronize on the end of the setups and before the tear downs. */ - CyclicBarrier barrier; - - /** - * Creates a new TestThreadHandler object. - * - * @param testResult The test result object for the test to be run with. - * @param test The test to run in a sperate thread. - * @param barrier The barrier implementation to use to synchronize per-thread setup completion and test - * completion before moving on through the setup, test, teardown phases. The barrier should - * be configured for the number of test threads. - */ - TestThreadHandler(TestResult testResult, WrappedSuiteTestDecorator test, CyclicBarrier barrier) - { - this.testResult = testResult; - this.test = test; - this.barrier = barrier; - } - - /** - * Runs the test associated with this pool. - */ - public void run() - { - try - { - // Call setup on all underlying tests in the suite that are thread aware. - for (Test childTest : test.getAllUnderlyingTests()) - { - // Check that the test is concurrency aware, so provides a setup method to call. - if (childTest instanceof TestThreadAware) - { - // Call the tests per thread setup. - TestThreadAware setupTest = (TestThreadAware) childTest; - setupTest.threadSetUp(); - } - } - - // Wait until all test threads have completed their setups. - barrier.await(); - - // Start timing the test batch, only after thread setups have completed. - if (testResult instanceof TKTestResult) - { - ((TKTestResult) testResult).notifyStartBatch(); - } - - // Run the tests. - test.run(testResult); - - // Wait unitl all test threads have completed their tests. - barrier.await(); - - // Call tear down on all underlying tests in the suite that are thread aware. - for (Test childTest : test.getAllUnderlyingTests()) - { - // Check that the test is concurrency aware, so provides a teardown method to call. - if (childTest instanceof TestThreadAware) - { - // Call the tests per thread tear down. - TestThreadAware setupTest = (TestThreadAware) childTest; - setupTest.threadTearDown(); - } - } - } - catch (InterruptedException e) - { - // Restore the interrupted state of the thread. - Thread.currentThread().interrupt(); - } - catch (BrokenBarrierException e) - { - // Set the interrupted state on the thread. The BrokenBarrierException may be caused where one thread - // waiting for the barrier is interrupted, causing the remaining threads correctly waiting on the - // barrier to fail. This condition is expected during test interruptions, and the response to it is to - // interrupt all the other threads running in the same scaled test. - Thread.currentThread().interrupt(); - } - } - - /** - * Prints the name of the test for debugging purposes. - * - * @return The name of the test. - */ - public String toString() - { - return "ScaledTestDecorator: [test = \"" + test + "\"]"; - } - } -} +/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +/** + * A test decorator that runs a test many times simultaneously in many threads. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Clone a test run into many threads and run them simultaneously. + *
      Inform the test results of the start and end of each concurrent test batch. {@link TKTestResult} + *
      Inform the test results of the concurrency level. {@link TKTestResult} + *
      + * + * @author Rupert Smith + */ +public class ScaledTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable // TestDecorator +{ + /** Used for logging. */ + // private static final Logger log = Logger.getLogger(ScaledTestDecorator.class); + + /** Determines how long to wait for tests to cleanly exit on shutdown. */ + private static final long SHUTDOWN_PAUSE = 3000; + + /** + * The stress levels or numbers of simultaneous threads to run the test in. The test is repeated at each of + * the concurrency levels specified here. Defaults to 1 thread. + */ + private int[] threads = new int[] { 1 }; + + /** Used to hold the number of tests currently being run in parallel. */ + private int concurrencyLevel; + + /** The test to run. */ + private WrappedSuiteTestDecorator test; + + /** + * Used to hold the current {@link TKTestResult} for the tests currently being run. This is made available so that + * the shutdown hook can ask it to cleanly end the current tests in the event of a shutdown. + */ + private TKTestResult currentTestResult; + + /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */ + private boolean shutdown = false; + + /** + * Creates an active test with default multiplier (1). + * + * @param test The target test. + */ + public ScaledTestDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Creates a concurrently scaled test with the specified number of threads. + * + * @param test The target test. + * @param numThreads The stress level. + */ + public ScaledTestDecorator(WrappedSuiteTestDecorator test, int numThreads) + { + this(test, new int[] { numThreads }); + } + + /** + * Creates a concurrently scaled test with the specified thread levels, the test is repeated at each level. + * + * @param test The target test. + * @param threads The concurrency levels. + */ + public ScaledTestDecorator(WrappedSuiteTestDecorator test, int[] threads) + { + super(test); + + /*log.debug("public ScaledTestDecorator(WrappedSuiteTestDecorator test = \"" + test + "\", int[] threads = " + + MathUtils.printArray(threads) + "): called");*/ + + this.test = test; + this.threads = threads; + } + + /** + * Runs the test simultaneously in at the specified concurrency levels. + * + * @param testResult The results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + // log.debug("public void run(TestResult testResult = " + testResult + "): called"); + + // Loop through all of the specified concurrent levels for the test, provided shutdown has not been called. + for (int i = 0; (i < threads.length) && !shutdown; i++) + { + // Get the number of threads for this run. + int numThreads = threads[i]; + + // Create test thread handlers for all the threads. + TestThreadHandler[] threadHandlers = new TestThreadHandler[numThreads]; + + // Create a cyclic barrier for the test threads to synch their setups and teardowns on. + CyclicBarrier barrier = new CyclicBarrier(numThreads); + + // Set up the test thread handlers to output results to the same test results object. + for (int j = 0; j < numThreads; j++) + { + threadHandlers[j] = new TestThreadHandler(testResult, test, barrier); + } + + // Ensure the concurrency level statistic is set up correctly. + concurrencyLevel = numThreads; + + // Begin batch. + if (testResult instanceof TKTestResult) + { + TKTestResult tkResult = (TKTestResult) testResult; + // tkResult.notifyStartBatch(); + tkResult.setConcurrencyLevel(numThreads); + + // Set the test result for the currently running tests, so that the shutdown hook can call it if necessary. + currentTestResult = tkResult; + } + + // Run all the tests and wait for them all to finish. + executeAndWaitForRunnables(threadHandlers); + + // Clear the test result for the currently running tests. + currentTestResult = null; + + // End batch. + if (testResult instanceof TKTestResult) + { + TKTestResult tkResult = (TKTestResult) testResult; + tkResult.notifyEndBatch(); + } + + // Clear up all the test threads, they hold references to their associated TestResult object and Test object, + // which may prevent them from being garbage collected as the TestResult and Test objects are long lived. + for (int j = 0; j < numThreads; j++) + { + threadHandlers[j].testResult = null; + threadHandlers[j].test = null; + threadHandlers[j] = null; + } + } + } + + /** + * Reports the number of tests that the scaled decorator is currently running concurrently. + * + * @return The number of tests that the scaled decorator is currently running concurrently. + */ + public int getConcurrencyLevel() + { + return concurrencyLevel; + } + + /** + * Executes all of the specifed runnable using the thread pool and waits for them all to complete. + * + * @param runnables The set of runnables to execute concurrently. + */ + private void executeAndWaitForRunnables(Runnable[] runnables) + { + int numThreads = runnables.length; + + // Used to keep track of the test threads in order to know when they have all completed. + Thread[] threads = new Thread[numThreads]; + + // Create all the test threads. + for (int j = 0; j < numThreads; j++) + { + threads[j] = new Thread(runnables[j]); + } + + // Start all the test threads. + for (int j = 0; j < numThreads; j++) + { + threads[j].start(); + } + + // Wait for all the test threads to complete. + for (int j = 0; j < numThreads; j++) + { + try + { + threads[j].join(); + } + catch (InterruptedException e) + { + // Restore the interrupted state of the thread. + Thread.currentThread().interrupt(); + } + } + } + + /** + * Supplies the shut-down hook. + * + * @return The shut-down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + // log.debug("ScaledTestDecorator::ShutdownHook: called"); + + // Set the shutdown flag so that no new tests are started. + shutdown = true; + + // Check if tests are currently running, and ask them to complete as soon as possible. Allow + // a short pause for this to happen. + TKTestResult testResult = currentTestResult; + + if (testResult != null) + { + // log.debug("There is a test result currently running tests, asking it to terminate ASAP."); + testResult.shutdownNow(); + + try + { + Thread.sleep(SHUTDOWN_PAUSE); + } + catch (InterruptedException e) + { + // Restore the interrupted state of the thread. + Thread.currentThread().interrupt(); + } + } + } + }); + } + + /** + * Prints a string summarizing this test decorator, mainly for debugging purposes. + * + * @return String representation for debugging purposes. + */ + public String toString() + { + return "ScaledTestDecorator: [ test = " + test + ", concurrencyLevel = " + concurrencyLevel + " ]"; + } + + /** + * TestThreadHandler is a runnable used to execute a test in. This is static to avoid implicit 'this' reference to + * the longer lived ScaledTestDecorator class. The scaled test decorator may execute many repeats but creates fresh + * handlers for each one. It re-uses the threads in a pool but does not re-use these handlers. + */ + private static class TestThreadHandler implements Runnable + { + /** The test result object for the test to be run with. */ + TestResult testResult; + + /** The test to run. */ + WrappedSuiteTestDecorator test; + + /** Holds the cyclic barrier to synchronize on the end of the setups and before the tear downs. */ + CyclicBarrier barrier; + + /** + * Creates a new TestThreadHandler object. + * + * @param testResult The test result object for the test to be run with. + * @param test The test to run in a sperate thread. + * @param barrier The barrier implementation to use to synchronize per-thread setup completion and test + * completion before moving on through the setup, test, teardown phases. The barrier should + * be configured for the number of test threads. + */ + TestThreadHandler(TestResult testResult, WrappedSuiteTestDecorator test, CyclicBarrier barrier) + { + this.testResult = testResult; + this.test = test; + this.barrier = barrier; + } + + /** + * Runs the test associated with this pool. + */ + public void run() + { + try + { + // Call setup on all underlying tests in the suite that are thread aware. + for (Test childTest : test.getAllUnderlyingTests()) + { + // Check that the test is concurrency aware, so provides a setup method to call. + if (childTest instanceof TestThreadAware) + { + // Call the tests per thread setup. + TestThreadAware setupTest = (TestThreadAware) childTest; + setupTest.threadSetUp(); + } + } + + // Wait until all test threads have completed their setups. + barrier.await(); + + // Start timing the test batch, only after thread setups have completed. + if (testResult instanceof TKTestResult) + { + ((TKTestResult) testResult).notifyStartBatch(); + } + + // Run the tests. + test.run(testResult); + + // Wait unitl all test threads have completed their tests. + barrier.await(); + + // Call tear down on all underlying tests in the suite that are thread aware. + for (Test childTest : test.getAllUnderlyingTests()) + { + // Check that the test is concurrency aware, so provides a teardown method to call. + if (childTest instanceof TestThreadAware) + { + // Call the tests per thread tear down. + TestThreadAware setupTest = (TestThreadAware) childTest; + setupTest.threadTearDown(); + } + } + } + catch (InterruptedException e) + { + // Restore the interrupted state of the thread. + Thread.currentThread().interrupt(); + } + catch (BrokenBarrierException e) + { + // Set the interrupted state on the thread. The BrokenBarrierException may be caused where one thread + // waiting for the barrier is interrupted, causing the remaining threads correctly waiting on the + // barrier to fail. This condition is expected during test interruptions, and the response to it is to + // interrupt all the other threads running in the same scaled test. + Thread.currentThread().interrupt(); + } + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "ScaledTestDecorator: [test = \"" + test + "\"]"; + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java index 0e8e1879b6..e462145d7d 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java @@ -1,55 +1,55 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.junit.extensions; - -/** - * SetupTaskAware is an interface that tests that can accept injectable setup tasks may implement. Typically this - * is used by configurable decorator stack to inject setup tasks into tests. It is then up to the test case to run - * the tasks in the setup or threadSetup methods as it chooses. - * - *

      Set up tasks should be chained so that they are executed in the order that they are applied. Tear down tasks - * should be chained so that they are executed in the reverse order to which they are applied. That way the set up and - * tear down tasks act as a 'task' stack, with nested setups and tear downs. - * - *

      - *
      CRC Card
      Responsibilities. - *
      Handle injection of set up tasks. - *
      Handle injection of tear down tasks. - *
      - * - * @author Rupert Smith - */ -public interface SetupTaskAware -{ - /** - * Adds the specified task to the tests setup. - * - * @param task The task to add to the tests setup. - */ - public void chainSetupTask(Runnable task); - - /** - * Adds the specified task to the tests tear down. - * - * @param task The task to add to the tests tear down. - */ - public void chainTearDownTask(Runnable task); -} +/* + * + * 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.junit.extensions; + +/** + * SetupTaskAware is an interface that tests that can accept injectable setup tasks may implement. Typically this + * is used by configurable decorator stack to inject setup tasks into tests. It is then up to the test case to run + * the tasks in the setup or threadSetup methods as it chooses. + * + *

      Set up tasks should be chained so that they are executed in the order that they are applied. Tear down tasks + * should be chained so that they are executed in the reverse order to which they are applied. That way the set up and + * tear down tasks act as a 'task' stack, with nested setups and tear downs. + * + *

      + *
      CRC Card
      Responsibilities. + *
      Handle injection of set up tasks. + *
      Handle injection of tear down tasks. + *
      + * + * @author Rupert Smith + */ +public interface SetupTaskAware +{ + /** + * Adds the specified task to the tests setup. + * + * @param task The task to add to the tests setup. + */ + public void chainSetupTask(Runnable task); + + /** + * Adds the specified task to the tests tear down. + * + * @param task The task to add to the tests tear down. + */ + public void chainTearDownTask(Runnable task); +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java index 00736c59e5..b91ce41ad3 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java @@ -1,92 +1,92 @@ -/* - * - * 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.junit.extensions; - -import org.apache.qpid.junit.extensions.util.StackQueue; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * SetupTaskHandler implements a task stack. It can be used, by delegation, as a base implementation for tests that want - * to have configurable setup/teardown task stacks. Typically it is up to the test implementation to decide whether the - * stack is executed in the setup/teardown methods or in the threadSetup/threadTeaddown methods. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Handle injection of set up tasks. - *
      Handle injection of tear down tasks. - *
      Run set up tasks in chain order. - *
      Run tear down tasks in reverse chain order. - *
      - * - * @author Rupert Smith - */ -public class SetupTaskHandler implements SetupTaskAware -{ - /** Holds the set up tasks. */ - Queue setups = new LinkedList(); - - /** Holds the tear down tasks. */ - Queue teardowns = new StackQueue(); - - /** - * Adds the specified task to the tests setup. - * - * @param task The task to add to the tests setup. - */ - public void chainSetupTask(Runnable task) - { - setups.offer(task); - } - - /** - * Adds the specified task to the tests tear down. - * - * @param task The task to add to the tests tear down. - */ - public void chainTearDownTask(Runnable task) - { - teardowns.offer(task); - } - - /** - * Runs the set up tasks in the order that they way chained. - */ - public void runSetupTasks() - { - while (!setups.isEmpty()) - { - setups.remove().run(); - } - } - - /** - * Runs the tear down tasks in the reverse of the order in which they were chained. - */ - public void runTearDownTasks() - { - while (!teardowns.isEmpty()) - { - teardowns.remove().run(); - } - } -} +/* + * + * 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.junit.extensions; + +import org.apache.qpid.junit.extensions.util.StackQueue; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * SetupTaskHandler implements a task stack. It can be used, by delegation, as a base implementation for tests that want + * to have configurable setup/teardown task stacks. Typically it is up to the test implementation to decide whether the + * stack is executed in the setup/teardown methods or in the threadSetup/threadTeaddown methods. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Handle injection of set up tasks. + *
      Handle injection of tear down tasks. + *
      Run set up tasks in chain order. + *
      Run tear down tasks in reverse chain order. + *
      + * + * @author Rupert Smith + */ +public class SetupTaskHandler implements SetupTaskAware +{ + /** Holds the set up tasks. */ + Queue setups = new LinkedList(); + + /** Holds the tear down tasks. */ + Queue teardowns = new StackQueue(); + + /** + * Adds the specified task to the tests setup. + * + * @param task The task to add to the tests setup. + */ + public void chainSetupTask(Runnable task) + { + setups.offer(task); + } + + /** + * Adds the specified task to the tests tear down. + * + * @param task The task to add to the tests tear down. + */ + public void chainTearDownTask(Runnable task) + { + teardowns.offer(task); + } + + /** + * Runs the set up tasks in the order that they way chained. + */ + public void runSetupTasks() + { + while (!setups.isEmpty()) + { + setups.remove().run(); + } + } + + /** + * Runs the tear down tasks in the reverse of the order in which they were chained. + */ + public void runTearDownTasks() + { + while (!teardowns.isEmpty()) + { + teardowns.remove().run(); + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java index 344d7abf82..dc6aa3c291 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java @@ -1,42 +1,42 @@ -/* - * - * 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.junit.extensions; - -/** - * Defines an interface that classes which supply shutdown hooks implement. Code that creates these classes can check - * if they supply a shutdown hook and register these hooks when the obejct are created. - * - *

      - *
      CRC Card
      Responsibilities - *
      Supply a shutdown hook. - *
      - * - * @author Rupert Smith - */ -public interface ShutdownHookable -{ - /** - * Supplies the shutdown hook. - * - * @return The shut down hook. - */ - public Thread getShutdownHook(); -} +/* + * + * 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.junit.extensions; + +/** + * Defines an interface that classes which supply shutdown hooks implement. Code that creates these classes can check + * if they supply a shutdown hook and register these hooks when the obejct are created. + * + *

      + *
      CRC Card
      Responsibilities + *
      Supply a shutdown hook. + *
      + * + * @author Rupert Smith + */ +public interface ShutdownHookable +{ + /** + * Supplies the shutdown hook. + * + * @return The shut down hook. + */ + public Thread getShutdownHook(); +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java index f7e350b1c7..2dc4c0e272 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java @@ -1,81 +1,81 @@ -/* - * - * 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.junit.extensions; - -/** - * SleepThrottle is a Throttle implementation that generates short pauses using the thread sleep methods. As the pauses - * get shorter, this technique gets more innacurate. In practice, around 100 Hz is the cap rate for accuracy. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Accept throttling rate in operations per second. - *
      Inject short pauses to fill out processing cycles to a specified rate. - *
      Check against a throttle speed without waiting. - *
      - * - * @author Rupert Smith - */ -public class SleepThrottle extends BaseThrottle implements Throttle -{ - /** Holds the time of the last call to the throttle method in nano seconds. */ - private long lastTimeNanos; - - /** - * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this - * it will inject short pauses to restrict the call rate to that rate. - */ - public void throttle() - { - // Get the current time in nanos. - long currentTimeNanos = System.nanoTime(); - - // Don't introduce any pause on the first call. - if (!firstCall) - { - // Check if there is any time left in the cycle since the last call to this method and introduce a short pause - // to fill that time if there is. - long remainingTimeNanos = cycleTimeNanos - (currentTimeNanos - lastTimeNanos); - - if (remainingTimeNanos > 0) - { - long milliPause = remainingTimeNanos / 1000000; - int nanoPause = (int) (remainingTimeNanos % 1000000); - - try - { - Thread.sleep(milliPause, nanoPause); - } - catch (InterruptedException e) - { - // Restore the interrupted thread, in-case the caller is checking for it. - Thread.currentThread().interrupt(); - } - } - } - else - { - firstCall = false; - } - - // Update the last time stamp. - lastTimeNanos = System.nanoTime(); - } -} +/* + * + * 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.junit.extensions; + +/** + * SleepThrottle is a Throttle implementation that generates short pauses using the thread sleep methods. As the pauses + * get shorter, this technique gets more innacurate. In practice, around 100 Hz is the cap rate for accuracy. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Accept throttling rate in operations per second. + *
      Inject short pauses to fill out processing cycles to a specified rate. + *
      Check against a throttle speed without waiting. + *
      + * + * @author Rupert Smith + */ +public class SleepThrottle extends BaseThrottle implements Throttle +{ + /** Holds the time of the last call to the throttle method in nano seconds. */ + private long lastTimeNanos; + + /** + * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this + * it will inject short pauses to restrict the call rate to that rate. + */ + public void throttle() + { + // Get the current time in nanos. + long currentTimeNanos = System.nanoTime(); + + // Don't introduce any pause on the first call. + if (!firstCall) + { + // Check if there is any time left in the cycle since the last call to this method and introduce a short pause + // to fill that time if there is. + long remainingTimeNanos = cycleTimeNanos - (currentTimeNanos - lastTimeNanos); + + if (remainingTimeNanos > 0) + { + long milliPause = remainingTimeNanos / 1000000; + int nanoPause = (int) (remainingTimeNanos % 1000000); + + try + { + Thread.sleep(milliPause, nanoPause); + } + catch (InterruptedException e) + { + // Restore the interrupted thread, in-case the caller is checking for it. + Thread.currentThread().interrupt(); + } + } + } + else + { + firstCall = false; + } + + // Update the last time stamp. + lastTimeNanos = System.nanoTime(); + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java index c9bcf3eb66..ae497c671b 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java @@ -1,625 +1,625 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestResult; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.listeners.TKTestListener; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Properties; - -/** - * TKTestResult extends TestResult in order to calculate test timings, to pass the variable integer parameter for - * parameterized test cases to those test cases and to introduce an optional delay before test starts. Interested - * {@link TKTestListener}s may be attached to this and will be informed of all relevant test statistics. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Calculate test timings. - *
      Inform timing listeners of timings. - *
      Inform memory listeners of memory readings. - *
      Inform parameters listeners of parameters. - *
      Pass the integer parameter to parameterized test cases. - *
      Provide verbose test information on test start and end. - *
      - * - * @todo Move the verbose test information on test start/end into a test listener instead. It confuses the intention - * of this class. Could also move the delay into a listener but that seems less appropriate as it would be a - * side-effecting listener. Delay and timing calculation are fundamental enough to this class. - * - * @todo The need for this class to act as a place-holder for the integer parameter for parameterized test cases is - * because this behaviour has been factored out into a test decorator class, see {@link AsymptoticTestDecorator}. - * The {@link AsymptoticTestDecorator#run} method takes a TestResult as an argument and cannot easily get to the - * {@link AsymptoticTestCase} class other than through this class. The option of using this class as a place hold - * for this value was chosen. Alternatively this class could provide a method for decorators to access the - * underlying test case through and then leave the setting of this parameter to the decorator which is a more - * natural home for this behaviour. It would also provide a more general framework for decorators. - * - * @todo The memory usage may need to be moved in closer to the test method invocation so that as little code as possible - * exists between it and the test or the results may be obscured. In fact it certainly does as the teardown method - * is getting called first. Wouldn't be a bad idea to move the timing code in closer too. - * - * @todo Get rid of the delay logic. Will be replaced by throttle control. - * - * @author Rupert Smith - */ -public class TKTestResult extends TestResult -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(TKTestResult.class); - - /** The delay between two tests. */ - private int delay = 0; - - /** - * This flag indicates that the #completeTest method of the timing controller has been called. Once this has - * been called once, the end test event for the whole test method should be ignored because tests have taken - * charge of outputing their own timings. - */ - private boolean completeTestUsed = false; - - /** - * Thread locals to hold test start time for non-instrumented tests. (Instrumented tests hold their own - * measurement data). - */ - // private Hashtable threadStartTimeMap = new Hashtable(); - private ThreadLocal threadLocals = new ThreadLocal(); - - /** Used to hold the current integer parameter to pass to parameterized tests. This defaults to 1. */ - private int n = 1; - - /** The timing listeners. */ - private Collection tkListeners; - - /** The test case name. */ - private String testCaseName; - - /** Used to hold the current concurrency level, set by the {@link ScaledTestDecorator}. */ - private int concurrencyLevel = 1; - - /** Flag used to indicate that this test result should attempt to complete its current tests as soon as possible. */ - private boolean shutdownNow = false; - - /** Holds the parametes that the test is run with. */ - private Properties testParameters; - - /** - * Creates a new TKTestResult object. - * - * @param delay A delay in milliseconds to introduce before every test start. - * @param testCaseName The name of the test case that this is the TestResult object for. - */ - public TKTestResult(int delay, String testCaseName) - { - super(); - - /*log.debug("public TKTestResult(PrintStream writer, int " + delay + ", boolean " + verbose + ", String " - + testCaseName + "): called");*/ - - // Keep all the parameters that this is created with. - this.delay = delay; - this.testCaseName = testCaseName; - } - - /** - * Callback method use to inform this test result that a test will be started. Waits for the configured delay time - * if one has been set, starts the timer, then delegates to the super class implementation. - * - * @param test The test to be started. - */ - public void startTest(Test test) - { - // log.debug("public void startTest(Test test): called"); - - // If a delay time has been specified then wait for that length of time. - if (this.delay > 0) - { - try - { - Thread.sleep(delay); - } - catch (InterruptedException e) - { - // Ignore, but restore the interrupted flag. - Thread.currentThread().interrupt(); - } - } - - // Create the thread local settings for the test. - ThreadLocalSettings threadLocalSettings = new ThreadLocalSettings(); - threadLocals.set(threadLocalSettings); - - // Record the test start time against this thread for calculating the test timing. (Consider using ThreadLocal - // instead?) - Long startTime = System.nanoTime(); - threadLocalSettings.startTime = startTime; - // log.debug("startTime = " + startTime); - - // Check if the test is timing controller aware, in which case set up a new timing controller and hold it - // in the thread local settings. - if (test instanceof TimingControllerAware) - { - TimingControllerAware controllerAware = (TimingControllerAware) test; - TimingControllerImpl controller = - new TimingControllerImpl(this, test, startTime, Thread.currentThread().getId()); - controllerAware.setTimingController(controller); - - threadLocalSettings.timingController = controller; - } - - // Delegate to the super method to notify test event listeners. - super.startTest(test); - } - - /** - * Callback method use to inform this result that a test was completed. This calculates how long the test took - * to run, then delegates to the super class implementation. - * - * @param test The test that has ended. - */ - public void endTest(Test test) - { - // log.debug("public void endTest(Test test): called"); - - long runTime = 0; - - // Recover the thread local settings. - ThreadLocalSettings threadLocalSettings = threadLocals.get(); - - // Check if the test is an instrumented test and get the timing information from the instrumentation as this - // will be more accurate. - if (test instanceof InstrumentedTest) - { - InstrumentedTest iTest = (InstrumentedTest) test; - - // Calculate the test run time. - runTime = iTest.getTestTime(); - // log.debug("runTime = " + runTime); - - // Calculate the test memory usage. - long startMem = iTest.getTestStartMemory(); - long endMem = iTest.getTestEndMemory(); - - // log.debug("startMem = " + startMem); - // log.debug("endMem = " + endMem); - - // Inform any memory listeners of the test memory. - if (tkListeners != null) - { - for (TKTestListener memoryListener : tkListeners) - { - memoryListener.memoryUsed(test, startMem, endMem, null); - } - } - } - else - { - // Calculate the test run time. - long endTime = System.nanoTime(); - Long startTime = threadLocalSettings.startTime; - runTime = endTime - startTime; - // log.debug("runTime = " + runTime); - - threadLocals.remove(); - } - - // Output end test stats. This is only done when the tests have not used the timing controller to output - // mutiple timings. - if (!completeTestUsed) - { - // Check if the test is an asymptotic test case and get its int parameter if so. - if (test instanceof AsymptoticTestCase) - { - AsymptoticTestCase pTest = (AsymptoticTestCase) test; - - // Set the parameter. - int paramValue = pTest.getN(); - - // Inform any parameter listeners of the test parameter. - if (tkListeners != null) - { - for (TKTestListener parameterListener : tkListeners) - { - parameterListener.parameterValue(test, paramValue, null); - } - } - } - - // Inform any timing listeners of the test timing and concurrency level. - if (tkListeners != null) - { - for (TKTestListener tkListener : tkListeners) - { - TKTestListener next = tkListener; - - next.timing(test, runTime, null); - next.concurrencyLevel(test, concurrencyLevel, null); - } - } - - // Call the super method to notify test event listeners of the end event. - super.endTest(test); - } - } - - /** - * Gets the integer parameter to pass to parameterized test cases. - * - * @return The value of the integer parameter. - */ - public int getN() - { - return n; - } - - /** - * Sets the integer parameter to pass to parameterized test cases. - * - * @param n The new value of the integer parameter. - */ - public void setN(int n) - { - // log.debug("public void setN(int " + n + "): called"); - - this.n = n; - } - - /** - * Adds a timing listener to pass all timing events to. - * - * @param listener The timing listener to register. - */ - public void addTKTestListener(TKTestListener listener) - { - // Create the collection to hold the timing listeners if it does not already exist. - if (tkListeners == null) - { - tkListeners = new ArrayList(); - } - - // Keep the new timing listener. - tkListeners.add(listener); - } - - /** - * Called by the test runner to notify this that a new test batch is being begun. This method forwards this - * notification to all batch listeners. - */ - public void notifyStartBatch() - { - if (tkListeners != null) - { - for (TKTestListener batchListener : tkListeners) - { - batchListener.startBatch(); - } - } - } - - /** - * Called by the test runner to notify this that the current test batch has been ended. This method forwards this - * notification to all batch listener. - */ - public void notifyEndBatch() - { - // log.debug("public void notifyEndBatch(): called"); - - if (tkListeners != null) - { - for (TKTestListener batchListener : tkListeners) - { - batchListener.endBatch(testParameters); - } - } - } - - /** - * Called by the test runner to notify this of the properties that the test is using. - * - * @param properties The tests set/read properties. - */ - public void notifyTestProperties(Properties properties) - { - // log.debug("public void notifyTestProperties(Properties properties): called"); - - this.testParameters = properties; - - /* - if (tkListeners != null) - { - for (TKTestListener batchListener : tkListeners) - { - batchListener.properties(properties); - } - } - */ - } - - /** - * Intercepts the execution of a test case to pass the variable integer parameter to a test if it is a parameterized - * test case. - * - * @param test The test to run. - */ - protected void run(final TestCase test) - { - // log.debug("protected void run(final TestCase test): called"); - - // Check if the test case is a parameterized test and set its integer parameter if so. - if (test instanceof AsymptoticTestCase) - { - AsymptoticTestCase pTest = (AsymptoticTestCase) test; - - // Set up the integer parameter. - pTest.setN(n); - } - - // Delegate to the super method to run the test. - super.run(test); - } - - /** - * Helper method that generats a String of verbose information about a test. This includes the thread name, test - * class name and test method name. - * - * @param test The test to generate the info string for. - * - * @return Returns a string with the thread name, test class name and test method name. - */ - protected String getTestInfo(Test test) - { - // log.debug("protected String getTestInfo(Test test): called"); - - return "[" + Thread.currentThread().getName() + "@" + test.getClass().getName() + "." - + ((test instanceof TestCase) ? ((TestCase) test).getName() : "") + "]"; - } - - /** - * Sets the concurrency level to pass into the test result. - * - * @param concurrencyLevel The concurrency level the tests are running out. - */ - public void setConcurrencyLevel(int concurrencyLevel) - { - this.concurrencyLevel = concurrencyLevel; - } - - /** - * Tells this test result that it should stop running tests. Once this method has been called this test result - * will not start any new tests, and any tests that use the timing controller will be passed interrupted exceptions, - * to indicate that they should end immediately. Usually the caller of this method will introduce a short wait - * to allow an opporunity for running tests to complete, before forcing the shutdown of the JVM. - */ - public void shutdownNow() - { - log.debug("public void shutdownNow(): called on " + this); - - shutdownNow = true; - } - - /** - * Prints a string summary of this class, mainly for debugging purposes. - * - * @return A string summary of this class, mainly for debugging purposes. - */ - public String toString() - { - return "TKTestResult@" + Integer.toString(hashCode(), 16) + ": [ testCaseName = " + testCaseName + ", n = " + n - + ", tkListeners = " + tkListeners + " ]"; - } - - /** - * Holds things that need to be kept on a per thread basis for each test invocation, such as the test start - * time and its timing controller. - */ - private static class ThreadLocalSettings - { - /** Holds the test start time. */ - Long startTime; - - /** Holds the test threads timing controller. */ - TimingController timingController; - } - - /** - * Provides an implementation of the {@link TimingController} interface that timing aware tests can use to call - * back to reset timers, and register additional test timings. - */ - private static class TimingControllerImpl implements TimingController - { - /** Holds an explicit reference to the test TKTestResult that created this. */ - TKTestResult testResult; - - /** Holds a reference to the test that this is the timing controller for. */ - Test test; - - /** Holds the start time for this timing controller. This gets reset to now on each completed test. */ - long startTime; - - /** - * Holds the thread id of the thread that started the test, so that this controller may be called from other - * threads but still identify itself correctly to {@link TKTestListener}s as being associated with the - * thread that called the test method. - */ - long threadId; - - /** - * Creates a timing controller on a specified TKTestResult and a test. - * - * @param testResult The TKTestResult that this controller interacts with. - * @param test The test that this is the timing controller for. - * @param startTime The test start time in nanoseconds. - * @param threadId The thread id of the thread that is calling the test method. - */ - public TimingControllerImpl(TKTestResult testResult, Test test, long startTime, long threadId) - { - this.testResult = testResult; - this.test = test; - this.startTime = startTime; - this.threadId = threadId; - } - - /** - * Gets the timing controller associated with the current test thread. Tests that use timing controller should - * always get the timing controller from this method in the same thread that called the setUp, tearDown or test - * method. The controller returned by this method may be called from any thread because it remembers the thread - * id of the original test thread. - * - * @return The timing controller associated with the current test thread. - */ - public TimingController getControllerForCurrentThread() - { - // Recover the thread local settings and extract the timing controller from them. - ThreadLocalSettings threadLocalSettings = testResult.threadLocals.get(); - - return threadLocalSettings.timingController; - } - - /** - * Not implemented yet. - * - * @return Nothing. - */ - public long suspend() - { - throw new RuntimeException("Method not implemented."); - } - - /** - * Not implemented yet. - * - * @return Nothing. - */ - public long resume() - { - throw new RuntimeException("Method not implemented."); - } - - /** - * Resets the timer start time to now. - * - * @return The new value of the start time. - */ - public long restart() - { - startTime = System.nanoTime(); - - return startTime; - } - - /** - * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of - * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters. - * - * @param testPassed Whether or not this timing is for a test pass or fail. - * - * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to - * indicate to the test method that it should stop immediately. - */ - public void completeTest(boolean testPassed) throws InterruptedException - { - completeTest(testPassed, 1); - } - - /** - * Register an additional pass/fail for the current test. The test result is applies to a test of the specified - * 'size' parmeter. - * - * @param testPassed Whether or not this timing is for a test pass or fail. - * @param param The test parameter size for parameterized tests. - * - * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to - * indicate to the test method that it should stop immediately. - */ - public void completeTest(boolean testPassed, int param) throws InterruptedException - { - /*log.debug("public long completeTest(boolean testPassed = " + testPassed + ", int param = " + param - + "): called");*/ - - // Calculate the test run time. - long endTime = System.nanoTime(); - long runTime = endTime - startTime; - // log.debug("runTime = " + runTime); - - // Reset the test start time to now, to reset the timer for the next result. - startTime = endTime; - - completeTest(testPassed, param, runTime); - } - - /** - * Register an additional pass/fail for the current test. The test result is applies to a test of the specified - * 'size' parmeter and allows the caller to sepecify the timing to log. - * - * @param testPassed Whether or not this timing is for a test pass or fail. - * @param param The test parameter size for parameterized tests. - * @param timeNanos The time in nano-seconds to log the test result with. - * - * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to - * indicate to the test method that it should stop immediately. - */ - public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException - { - log.debug("public void completeTest(boolean testPassed, int param, long timeNanos): called"); - log.debug("testResult = " + testResult); - - // Tell the test result that completeTest has been used, so to not register end test events for the whole - // test method. - testResult.completeTestUsed = true; - - // Inform any timing listeners of the test timings and parameters and send an end test notification using - // the thread id of the thread that started the test. - if (testResult.tkListeners != null) - { - for (TKTestListener listener : testResult.tkListeners) - { - listener.reset(test, threadId); - listener.timing(test, timeNanos, threadId); - listener.parameterValue(test, param, threadId); - listener.concurrencyLevel(test, testResult.concurrencyLevel, threadId); - - if (!testPassed) - { - listener.addFailure(test, null, threadId); - } - - listener.endTest(test, threadId); - } - } - - // log.debug("testResult.shutdownNow = " + testResult.shutdownNow); - - // Check if the test runner has been asked to shutdown and raise an interuppted exception if so. - if (testResult.shutdownNow) - { - // log.debug("The shutdown flag is set."); - - throw new InterruptedException("Attempting clean shutdown by suspending current test."); - } - } - } -} +/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.listeners.TKTestListener; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Properties; + +/** + * TKTestResult extends TestResult in order to calculate test timings, to pass the variable integer parameter for + * parameterized test cases to those test cases and to introduce an optional delay before test starts. Interested + * {@link TKTestListener}s may be attached to this and will be informed of all relevant test statistics. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Calculate test timings. + *
      Inform timing listeners of timings. + *
      Inform memory listeners of memory readings. + *
      Inform parameters listeners of parameters. + *
      Pass the integer parameter to parameterized test cases. + *
      Provide verbose test information on test start and end. + *
      + * + * @todo Move the verbose test information on test start/end into a test listener instead. It confuses the intention + * of this class. Could also move the delay into a listener but that seems less appropriate as it would be a + * side-effecting listener. Delay and timing calculation are fundamental enough to this class. + * + * @todo The need for this class to act as a place-holder for the integer parameter for parameterized test cases is + * because this behaviour has been factored out into a test decorator class, see {@link AsymptoticTestDecorator}. + * The {@link AsymptoticTestDecorator#run} method takes a TestResult as an argument and cannot easily get to the + * {@link AsymptoticTestCase} class other than through this class. The option of using this class as a place hold + * for this value was chosen. Alternatively this class could provide a method for decorators to access the + * underlying test case through and then leave the setting of this parameter to the decorator which is a more + * natural home for this behaviour. It would also provide a more general framework for decorators. + * + * @todo The memory usage may need to be moved in closer to the test method invocation so that as little code as possible + * exists between it and the test or the results may be obscured. In fact it certainly does as the teardown method + * is getting called first. Wouldn't be a bad idea to move the timing code in closer too. + * + * @todo Get rid of the delay logic. Will be replaced by throttle control. + * + * @author Rupert Smith + */ +public class TKTestResult extends TestResult +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(TKTestResult.class); + + /** The delay between two tests. */ + private int delay = 0; + + /** + * This flag indicates that the #completeTest method of the timing controller has been called. Once this has + * been called once, the end test event for the whole test method should be ignored because tests have taken + * charge of outputing their own timings. + */ + private boolean completeTestUsed = false; + + /** + * Thread locals to hold test start time for non-instrumented tests. (Instrumented tests hold their own + * measurement data). + */ + // private Hashtable threadStartTimeMap = new Hashtable(); + private ThreadLocal threadLocals = new ThreadLocal(); + + /** Used to hold the current integer parameter to pass to parameterized tests. This defaults to 1. */ + private int n = 1; + + /** The timing listeners. */ + private Collection tkListeners; + + /** The test case name. */ + private String testCaseName; + + /** Used to hold the current concurrency level, set by the {@link ScaledTestDecorator}. */ + private int concurrencyLevel = 1; + + /** Flag used to indicate that this test result should attempt to complete its current tests as soon as possible. */ + private boolean shutdownNow = false; + + /** Holds the parametes that the test is run with. */ + private Properties testParameters; + + /** + * Creates a new TKTestResult object. + * + * @param delay A delay in milliseconds to introduce before every test start. + * @param testCaseName The name of the test case that this is the TestResult object for. + */ + public TKTestResult(int delay, String testCaseName) + { + super(); + + /*log.debug("public TKTestResult(PrintStream writer, int " + delay + ", boolean " + verbose + ", String " + + testCaseName + "): called");*/ + + // Keep all the parameters that this is created with. + this.delay = delay; + this.testCaseName = testCaseName; + } + + /** + * Callback method use to inform this test result that a test will be started. Waits for the configured delay time + * if one has been set, starts the timer, then delegates to the super class implementation. + * + * @param test The test to be started. + */ + public void startTest(Test test) + { + // log.debug("public void startTest(Test test): called"); + + // If a delay time has been specified then wait for that length of time. + if (this.delay > 0) + { + try + { + Thread.sleep(delay); + } + catch (InterruptedException e) + { + // Ignore, but restore the interrupted flag. + Thread.currentThread().interrupt(); + } + } + + // Create the thread local settings for the test. + ThreadLocalSettings threadLocalSettings = new ThreadLocalSettings(); + threadLocals.set(threadLocalSettings); + + // Record the test start time against this thread for calculating the test timing. (Consider using ThreadLocal + // instead?) + Long startTime = System.nanoTime(); + threadLocalSettings.startTime = startTime; + // log.debug("startTime = " + startTime); + + // Check if the test is timing controller aware, in which case set up a new timing controller and hold it + // in the thread local settings. + if (test instanceof TimingControllerAware) + { + TimingControllerAware controllerAware = (TimingControllerAware) test; + TimingControllerImpl controller = + new TimingControllerImpl(this, test, startTime, Thread.currentThread().getId()); + controllerAware.setTimingController(controller); + + threadLocalSettings.timingController = controller; + } + + // Delegate to the super method to notify test event listeners. + super.startTest(test); + } + + /** + * Callback method use to inform this result that a test was completed. This calculates how long the test took + * to run, then delegates to the super class implementation. + * + * @param test The test that has ended. + */ + public void endTest(Test test) + { + // log.debug("public void endTest(Test test): called"); + + long runTime = 0; + + // Recover the thread local settings. + ThreadLocalSettings threadLocalSettings = threadLocals.get(); + + // Check if the test is an instrumented test and get the timing information from the instrumentation as this + // will be more accurate. + if (test instanceof InstrumentedTest) + { + InstrumentedTest iTest = (InstrumentedTest) test; + + // Calculate the test run time. + runTime = iTest.getTestTime(); + // log.debug("runTime = " + runTime); + + // Calculate the test memory usage. + long startMem = iTest.getTestStartMemory(); + long endMem = iTest.getTestEndMemory(); + + // log.debug("startMem = " + startMem); + // log.debug("endMem = " + endMem); + + // Inform any memory listeners of the test memory. + if (tkListeners != null) + { + for (TKTestListener memoryListener : tkListeners) + { + memoryListener.memoryUsed(test, startMem, endMem, null); + } + } + } + else + { + // Calculate the test run time. + long endTime = System.nanoTime(); + Long startTime = threadLocalSettings.startTime; + runTime = endTime - startTime; + // log.debug("runTime = " + runTime); + + threadLocals.remove(); + } + + // Output end test stats. This is only done when the tests have not used the timing controller to output + // mutiple timings. + if (!completeTestUsed) + { + // Check if the test is an asymptotic test case and get its int parameter if so. + if (test instanceof AsymptoticTestCase) + { + AsymptoticTestCase pTest = (AsymptoticTestCase) test; + + // Set the parameter. + int paramValue = pTest.getN(); + + // Inform any parameter listeners of the test parameter. + if (tkListeners != null) + { + for (TKTestListener parameterListener : tkListeners) + { + parameterListener.parameterValue(test, paramValue, null); + } + } + } + + // Inform any timing listeners of the test timing and concurrency level. + if (tkListeners != null) + { + for (TKTestListener tkListener : tkListeners) + { + TKTestListener next = tkListener; + + next.timing(test, runTime, null); + next.concurrencyLevel(test, concurrencyLevel, null); + } + } + + // Call the super method to notify test event listeners of the end event. + super.endTest(test); + } + } + + /** + * Gets the integer parameter to pass to parameterized test cases. + * + * @return The value of the integer parameter. + */ + public int getN() + { + return n; + } + + /** + * Sets the integer parameter to pass to parameterized test cases. + * + * @param n The new value of the integer parameter. + */ + public void setN(int n) + { + // log.debug("public void setN(int " + n + "): called"); + + this.n = n; + } + + /** + * Adds a timing listener to pass all timing events to. + * + * @param listener The timing listener to register. + */ + public void addTKTestListener(TKTestListener listener) + { + // Create the collection to hold the timing listeners if it does not already exist. + if (tkListeners == null) + { + tkListeners = new ArrayList(); + } + + // Keep the new timing listener. + tkListeners.add(listener); + } + + /** + * Called by the test runner to notify this that a new test batch is being begun. This method forwards this + * notification to all batch listeners. + */ + public void notifyStartBatch() + { + if (tkListeners != null) + { + for (TKTestListener batchListener : tkListeners) + { + batchListener.startBatch(); + } + } + } + + /** + * Called by the test runner to notify this that the current test batch has been ended. This method forwards this + * notification to all batch listener. + */ + public void notifyEndBatch() + { + // log.debug("public void notifyEndBatch(): called"); + + if (tkListeners != null) + { + for (TKTestListener batchListener : tkListeners) + { + batchListener.endBatch(testParameters); + } + } + } + + /** + * Called by the test runner to notify this of the properties that the test is using. + * + * @param properties The tests set/read properties. + */ + public void notifyTestProperties(Properties properties) + { + // log.debug("public void notifyTestProperties(Properties properties): called"); + + this.testParameters = properties; + + /* + if (tkListeners != null) + { + for (TKTestListener batchListener : tkListeners) + { + batchListener.properties(properties); + } + } + */ + } + + /** + * Intercepts the execution of a test case to pass the variable integer parameter to a test if it is a parameterized + * test case. + * + * @param test The test to run. + */ + protected void run(final TestCase test) + { + // log.debug("protected void run(final TestCase test): called"); + + // Check if the test case is a parameterized test and set its integer parameter if so. + if (test instanceof AsymptoticTestCase) + { + AsymptoticTestCase pTest = (AsymptoticTestCase) test; + + // Set up the integer parameter. + pTest.setN(n); + } + + // Delegate to the super method to run the test. + super.run(test); + } + + /** + * Helper method that generats a String of verbose information about a test. This includes the thread name, test + * class name and test method name. + * + * @param test The test to generate the info string for. + * + * @return Returns a string with the thread name, test class name and test method name. + */ + protected String getTestInfo(Test test) + { + // log.debug("protected String getTestInfo(Test test): called"); + + return "[" + Thread.currentThread().getName() + "@" + test.getClass().getName() + "." + + ((test instanceof TestCase) ? ((TestCase) test).getName() : "") + "]"; + } + + /** + * Sets the concurrency level to pass into the test result. + * + * @param concurrencyLevel The concurrency level the tests are running out. + */ + public void setConcurrencyLevel(int concurrencyLevel) + { + this.concurrencyLevel = concurrencyLevel; + } + + /** + * Tells this test result that it should stop running tests. Once this method has been called this test result + * will not start any new tests, and any tests that use the timing controller will be passed interrupted exceptions, + * to indicate that they should end immediately. Usually the caller of this method will introduce a short wait + * to allow an opporunity for running tests to complete, before forcing the shutdown of the JVM. + */ + public void shutdownNow() + { + log.debug("public void shutdownNow(): called on " + this); + + shutdownNow = true; + } + + /** + * Prints a string summary of this class, mainly for debugging purposes. + * + * @return A string summary of this class, mainly for debugging purposes. + */ + public String toString() + { + return "TKTestResult@" + Integer.toString(hashCode(), 16) + ": [ testCaseName = " + testCaseName + ", n = " + n + + ", tkListeners = " + tkListeners + " ]"; + } + + /** + * Holds things that need to be kept on a per thread basis for each test invocation, such as the test start + * time and its timing controller. + */ + private static class ThreadLocalSettings + { + /** Holds the test start time. */ + Long startTime; + + /** Holds the test threads timing controller. */ + TimingController timingController; + } + + /** + * Provides an implementation of the {@link TimingController} interface that timing aware tests can use to call + * back to reset timers, and register additional test timings. + */ + private static class TimingControllerImpl implements TimingController + { + /** Holds an explicit reference to the test TKTestResult that created this. */ + TKTestResult testResult; + + /** Holds a reference to the test that this is the timing controller for. */ + Test test; + + /** Holds the start time for this timing controller. This gets reset to now on each completed test. */ + long startTime; + + /** + * Holds the thread id of the thread that started the test, so that this controller may be called from other + * threads but still identify itself correctly to {@link TKTestListener}s as being associated with the + * thread that called the test method. + */ + long threadId; + + /** + * Creates a timing controller on a specified TKTestResult and a test. + * + * @param testResult The TKTestResult that this controller interacts with. + * @param test The test that this is the timing controller for. + * @param startTime The test start time in nanoseconds. + * @param threadId The thread id of the thread that is calling the test method. + */ + public TimingControllerImpl(TKTestResult testResult, Test test, long startTime, long threadId) + { + this.testResult = testResult; + this.test = test; + this.startTime = startTime; + this.threadId = threadId; + } + + /** + * Gets the timing controller associated with the current test thread. Tests that use timing controller should + * always get the timing controller from this method in the same thread that called the setUp, tearDown or test + * method. The controller returned by this method may be called from any thread because it remembers the thread + * id of the original test thread. + * + * @return The timing controller associated with the current test thread. + */ + public TimingController getControllerForCurrentThread() + { + // Recover the thread local settings and extract the timing controller from them. + ThreadLocalSettings threadLocalSettings = testResult.threadLocals.get(); + + return threadLocalSettings.timingController; + } + + /** + * Not implemented yet. + * + * @return Nothing. + */ + public long suspend() + { + throw new RuntimeException("Method not implemented."); + } + + /** + * Not implemented yet. + * + * @return Nothing. + */ + public long resume() + { + throw new RuntimeException("Method not implemented."); + } + + /** + * Resets the timer start time to now. + * + * @return The new value of the start time. + */ + public long restart() + { + startTime = System.nanoTime(); + + return startTime; + } + + /** + * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of + * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed) throws InterruptedException + { + completeTest(testPassed, 1); + } + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param) throws InterruptedException + { + /*log.debug("public long completeTest(boolean testPassed = " + testPassed + ", int param = " + param + + "): called");*/ + + // Calculate the test run time. + long endTime = System.nanoTime(); + long runTime = endTime - startTime; + // log.debug("runTime = " + runTime); + + // Reset the test start time to now, to reset the timer for the next result. + startTime = endTime; + + completeTest(testPassed, param, runTime); + } + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter and allows the caller to sepecify the timing to log. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * @param timeNanos The time in nano-seconds to log the test result with. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException + { + log.debug("public void completeTest(boolean testPassed, int param, long timeNanos): called"); + log.debug("testResult = " + testResult); + + // Tell the test result that completeTest has been used, so to not register end test events for the whole + // test method. + testResult.completeTestUsed = true; + + // Inform any timing listeners of the test timings and parameters and send an end test notification using + // the thread id of the thread that started the test. + if (testResult.tkListeners != null) + { + for (TKTestListener listener : testResult.tkListeners) + { + listener.reset(test, threadId); + listener.timing(test, timeNanos, threadId); + listener.parameterValue(test, param, threadId); + listener.concurrencyLevel(test, testResult.concurrencyLevel, threadId); + + if (!testPassed) + { + listener.addFailure(test, null, threadId); + } + + listener.endTest(test, threadId); + } + } + + // log.debug("testResult.shutdownNow = " + testResult.shutdownNow); + + // Check if the test runner has been asked to shutdown and raise an interuppted exception if so. + if (testResult.shutdownNow) + { + // log.debug("The shutdown flag is set."); + + throw new InterruptedException("Attempting clean shutdown by suspending current test."); + } + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java index 7955a2e2e9..671d33feed 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java @@ -1,694 +1,694 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.Test; -import junit.framework.TestResult; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.listeners.CSVTestListener; -import org.apache.qpid.junit.extensions.listeners.ConsoleTestListener; -import org.apache.qpid.junit.extensions.listeners.XMLTestListener; -import org.apache.qpid.junit.extensions.util.CommandLineParser; -import org.apache.qpid.junit.extensions.util.MathUtils; -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import java.io.*; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -/** - * TKTestRunner extends {@link junit.textui.TestRunner} with the ability to run tests multiple times, to execute a test - * simultaneously using many threads, to put a delay between test runs and adds support for tests that take integer - * parameters that can be 'stepped' through on multiple test runs. These features can be accessed by using this class - * as an entry point and passing command line arguments to specify which features to use: - * - *

      - * -w ms       The number of milliseconds between invocations of test cases.
      - * -c pattern  The number of tests to run concurrently.
      - * -r num      The number of times to repeat each test.
      - * -d duration The length of time to run the tests for.
      - * -t name     The name of the test case to execute.
      - * -s pattern  The size parameter to run tests with.
      - * -o dir      The name of the directory to output test timings to.
      - * --csv       Output test results in CSV format.
      - * --xml       Output test results in XML format.
      - * 
      - * - *

      This command line may also have trailing 'name=value' parameters added to it. All of these values are added - * to the test context properties and passed to the test, which can access them by name. - * - *

      The pattern arguments are of the form [lowest(: ...)(: highest)](:sample=s)(:exp), where round brackets - * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided - * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples. - * - *

      The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least - * one of the optional values must be present. - * - *

      When specifying optional test parameters on the command line, in 'name=value' format, it is also possible to use - * the format 'name=[value1:value2:value3:...]', to specify multiple values for a parameter. All permutations of all - * parameters with multiple values will be created and tested. If the values are numerical, it is also possible to use - * the sequence generation patterns instead of fully specifying all of the values. - * - *

      Here are some examples: - * - *

      - *
       -c [10:20:30:40:50] 
      Runs the test with 10,20,...,50 threads. - *
       -s [1:100]:samples=10 
      - *
      Runs the test with ten different size parameters evenly spaced between 1 and 100. - *
       -s [1:1000000]:samples=10:exp 
      - *
      Runs the test with ten different size parameters exponentially spaced between 1 and 1000000. - *
       -r 10 
      Runs each test ten times. - *
       -d 10H 
      Runs the test repeatedly for 10 hours. - *
       -d 1M, -r 10 
      - *
      Runs the test repeatedly for 1 minute but only takes a timing sample every 10 test runs. - *
       -r 10, -c [1:5:10:50], -s [100:1000:10000] 
      - *
      Runs 12 test cycles (4 concurrency samples * 3 size sample), with 10 repeats each. In total the test - * will be run 199 times (3 + 15 + 30 + 150) - *
       cache=true 
      Passes the 'cache' parameter with value 'true' to the test. - *
       cache=[true:false] 
      Runs the test with the 'cache' parameter set to 'true' and 'false'. - *
       cacheSize=[1000:1000000],samples=4,exp 
      - *
      Runs the test with the 'cache' parameter set to a series of exponentially increasing sizes. - *
      - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Create the test configuration specified by the command line parameters. - *
      - * - * @todo Verify that the output directory exists or can be created. - * - * @todo Verify that the specific named test case to execute exists. - * - * @todo Drop the delay parameter as it is being replaced by throttling. - * - * @todo Completely replace the test ui test runner, instead of having TKTestRunner inherit from it, its just not - * good code to extend. - * - * @author Rupert Smith - */ -public class TKTestRunner extends TestRunnerImprovedErrorHandling -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TKTestRunner.class); - - /** Used for displaying information on the console. */ - // private static final Logger console = Logger.getLogger("CONSOLE." + TKTestRunner.class.getName()); - - /** Used for generating the timestamp when naming output files. */ - protected static final DateFormat TIME_STAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); - - /** Number of times to rerun the test. */ - protected Integer repetitions = 1; - - /** The length of time to run the tests for. */ - protected Long duration; - - /** Number of threads running the tests. */ - protected int[] threads; - - /** Delay in ms to wait between two test cases. */ - protected int delay = 0; - - /** The parameter values to pass to parameterized tests. */ - protected int[] params; - - /** Name of the single test case to execute. */ - protected String testCaseName = null; - - /** Name of the test class. */ - protected String testClassName = null; - - /** Name of the test run. */ - protected String testRunName = null; - - /** Directory to output XML reports into, if specified. */ - protected String reportDir = null; - - /** Flag that indicates the CSV results listener should be used to output results. */ - protected boolean csvResults; - - /** Flag that indiciates the XML results listener should be used to output results. */ - protected boolean xmlResults; - - /** - * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} - * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. - */ - protected String currentTestClassName; - - /** Holds the test results object, which is reponsible for instrumenting tests/threads to record results. */ - protected TKTestResult result; - - /** Holds a list of factories for instantiating optional user specified test decorators. */ - protected List decoratorFactories; - - /** - * Constructs a TKTestRunner using System.out for all the output. - * - * @param repetitions The number of times to repeat the test, or test batch size. - * @param duration The length of time to run the tests for. -1 means no duration has been set. - * @param threads The concurrency levels to ramp up to. - * @param delay A delay in milliseconds between test runs. - * @param params The sets of 'size' parameters to pass to test. - * @param testCaseName The name of the test case to run. - * @param reportDir The directory to output the test results to. - * @param runName The name of the test run; used to name the output file. - * @param csvResults true if the CSV results listener should be attached. - * @param xmlResults true if the XML results listener should be attached. - * @param decoratorFactories List of factories for user specified decorators. - */ - public TKTestRunner(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName, - String reportDir, String runName, boolean csvResults, boolean xmlResults, - List decoratorFactories) - { - super(new NullResultPrinter(System.out)); - - log.debug("public TKTestRunner(): called"); - - // Keep all the test parameters. - this.repetitions = repetitions; - this.duration = duration; - this.threads = threads; - this.delay = delay; - this.params = params; - this.testCaseName = testCaseName; - this.reportDir = reportDir; - this.testRunName = runName; - this.csvResults = csvResults; - this.xmlResults = xmlResults; - this.decoratorFactories = decoratorFactories; - } - - /** - * The entry point for the toolkit test runner. - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - // Use the command line parser to evaluate the command line. - CommandLineParser commandLine = - new CommandLineParser( - new String[][] - { - { "w", "The number of milliseconds between invocations of test cases.", "ms", "false" }, - { "c", "The number of tests to run concurrently.", "num", "false", MathUtils.SEQUENCE_REGEXP }, - { "r", "The number of times to repeat each test.", "num", "false" }, - { "d", "The length of time to run the tests for.", "duration", "false", MathUtils.DURATION_REGEXP }, - { "f", "The maximum rate to call the tests at.", "frequency", "false", "^([1-9][0-9]*)/([1-9][0-9]*)$" }, - { "s", "The size parameter to run tests with.", "size", "false", MathUtils.SEQUENCE_REGEXP }, - { "t", "The name of the test case to execute.", "name", "false" }, - { "o", "The name of the directory to output test timings to.", "dir", "false" }, - { "n", "A name for this test run, used to name the output file.", "name", "true" }, - { - "X:decorators", "A list of additional test decorators to wrap the tests in.", - "\"class.name[:class.name]*\"", "false" - }, - { "1", "Test class.", "class", "true" }, - { "-csv", "Output test results in CSV format.", null, "false" }, - { "-xml", "Output test results in XML format.", null, "false" } - }); - - // Capture the command line arguments or display errors and correct usage and then exit. - ParsedProperties options = null; - - try - { - options = new ParsedProperties(commandLine.parseCommandLine(args)); - } - catch (IllegalArgumentException e) - { - System.out.println(commandLine.getErrors()); - System.out.println(commandLine.getUsage()); - System.exit(FAILURE_EXIT); - } - - // Extract the command line options. - Integer delay = options.getPropertyAsInteger("w"); - String threadsString = options.getProperty("c"); - Integer repetitions = options.getPropertyAsInteger("r"); - String durationString = options.getProperty("d"); - String paramsString = options.getProperty("s"); - String testCaseName = options.getProperty("t"); - String reportDir = options.getProperty("o"); - String testRunName = options.getProperty("n"); - String decorators = options.getProperty("X:decorators"); - String testClassName = options.getProperty("1"); - boolean csvResults = options.getPropertyAsBoolean("-csv"); - boolean xmlResults = options.getPropertyAsBoolean("-xml"); - - int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString); - int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString); - Long duration = (durationString == null) ? null : MathUtils.parseDuration(durationString); - - // The test run name defaults to the test class name unless a value was specified for it. - testRunName = (testRunName == null) ? testClassName : testRunName; - - // Add all the command line options and trailing settings to test context properties. Tests may pick up - // overridden values from there, and these values will be logged in the test results, for analysis and - // to make tests repeatable. - commandLine.addTrailingPairsToProperties(TestContextProperties.getInstance()); - commandLine.addOptionsToProperties(TestContextProperties.getInstance()); - - // Create and start the test runner. - try - { - // Create a list of test decorator factories for use specified decorators to be applied. - List decoratorFactories = parseDecorators(decorators); - - TKTestRunner testRunner = - new TKTestRunner(repetitions, duration, threads, (delay == null) ? 0 : delay, params, testCaseName, - reportDir, testRunName, csvResults, xmlResults, decoratorFactories); - - TestResult testResult = testRunner.start(testClassName); - - if (!testResult.wasSuccessful()) - { - System.exit(FAILURE_EXIT); - } - } - catch (Exception e) - { - System.err.println(e.getMessage()); - e.printStackTrace(new PrintStream(System.err)); - System.exit(EXCEPTION_EXIT); - } - } - - /** - * Parses a list of test decorators, in the form "class.name[:class.name]*", and creates factories for those - * TestDecorator classes , and returns a list of the factories. This list of factories will be in the same - * order as specified in the string. The factories can be used to succesively wrap tests in layers of - * decorators, as decorators themselves implement the 'Test' interface. - * - *

      If the string fails to parse, or if any of the decorators specified in it are cannot be loaded, or are not - * TestDecorators, a runtime exception with a suitable error message will be thrown. The factories themselves - * throw runtimes if the constructor method calls on the decorators fail. - * - * @param decorators The decorators list to be parsed. - * - * @return A list of instantiated decorators. - */ - protected static List parseDecorators(String decorators) - { - List result = new LinkedList(); - String toParse = decorators; - - // Check that the decorators string is not null or empty, returning an empty list of decorator factories it - // it is. - if ((decorators == null) || "".equals(decorators)) - { - return result; - } - - // Strip any leading and trailing quotes from the string. - if (toParse.charAt(0) == '\"') - { - toParse = toParse.substring(1, toParse.length() - 1); - } - - if (toParse.charAt(toParse.length() - 1) == '\"') - { - toParse = toParse.substring(0, toParse.length() - 2); - } - - // Instantiate all decorators. - for (String decoratorClassName : toParse.split(":")) - { - try - { - Class decoratorClass = Class.forName(decoratorClassName); - final Constructor decoratorConstructor = decoratorClass.getConstructor(WrappedSuiteTestDecorator.class); - - // Check that the decorator is an instance of WrappedSuiteTestDecorator. - if (!WrappedSuiteTestDecorator.class.isAssignableFrom(decoratorClass)) - { - throw new RuntimeException("The decorator class " + decoratorClassName - + " is not a sub-class of WrappedSuiteTestDecorator, which it needs to be."); - } - - result.add(new TestDecoratorFactory() - { - public WrappedSuiteTestDecorator decorateTest(Test test) - { - try - { - return (WrappedSuiteTestDecorator) decoratorConstructor.newInstance(test); - } - catch (InstantiationException e) - { - throw new RuntimeException( - "The decorator class " + decoratorConstructor.getDeclaringClass().getName() - + " cannot be instantiated.", e); - } - catch (IllegalAccessException e) - { - throw new RuntimeException( - "The decorator class " + decoratorConstructor.getDeclaringClass().getName() - + " does not have a publicly accessable constructor.", e); - } - catch (InvocationTargetException e) - { - throw new RuntimeException( - "The decorator class " + decoratorConstructor.getDeclaringClass().getName() - + " cannot be invoked.", e); - } - } - }); - } - catch (ClassNotFoundException e) - { - throw new RuntimeException("The decorator class " + decoratorClassName + " could not be found.", e); - } - catch (NoSuchMethodException e) - { - throw new RuntimeException("The decorator class " + decoratorClassName - + " does not have a constructor that accepts a single 'WrappedSuiteTestDecorator' argument.", e); - } - } - - return result; - } - - /** - * TestDecoratorFactory is a factory for creating test decorators from tests. - */ - protected interface TestDecoratorFactory - { - /** - * Decorates the specified test with a new decorator. - * - * @param test The test to decorate. - * - * @return The decorated test. - */ - public WrappedSuiteTestDecorator decorateTest(Test test); - } - - /** - * Runs a test or suite of tests, using the super class implemenation. This method wraps the test to be run - * in any test decorators needed to add in the configured toolkits enhanced junit functionality. - * - * @param test The test to run. - * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for. - * - * @return The results of the test run. - */ - public TestResult doRun(Test test, boolean wait) - { - log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called"); - - // Wrap the tests in decorators for duration, scaling, repetition, parameterization etc. - WrappedSuiteTestDecorator targetTest = decorateTests(test); - - // Delegate to the super method to run the decorated tests. - log.debug("About to call super.doRun"); - - TestResult result = super.doRun(targetTest, wait); - log.debug("super.doRun returned."); - - /*if (result instanceof TKTestResult) - { - TKTestResult tkResult = (TKTestResult) result; - - tkResult.notifyEndBatch(); - }*/ - - return result; - } - - /** - * Applies test decorators to the tests for parameterization, duration, scaling and repetition. - * - * @param test The test to decorat. - * - * @return The decorated test. - */ - protected WrappedSuiteTestDecorator decorateTests(Test test) - { - log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); - log.debug("repetitions = " + repetitions); - log.debug("threads = " + ((threads == null) ? null : MathUtils.printArray(threads))); - log.debug("duration = " + duration); - - // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling, - // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it. - WrappedSuiteTestDecorator targetTest = null; - - if (test instanceof TestSuite) - { - log.debug("targetTest is a TestSuite"); - - TestSuite suite = (TestSuite) test; - - int numTests = suite.countTestCases(); - log.debug("There are " + numTests + " in the suite."); - - for (int i = 0; i < numTests; i++) - { - Test nextTest = suite.testAt(i); - log.debug("suite.testAt(" + i + ") = " + nextTest); - - if (nextTest instanceof TimingControllerAware) - { - log.debug("nextTest is TimingControllerAware"); - } - - if (nextTest instanceof TestThreadAware) - { - log.debug("nextTest is TestThreadAware"); - } - } - - targetTest = new WrappedSuiteTestDecorator(suite); - log.debug("Wrapped with a WrappedSuiteTestDecorator."); - } - // If the test has already been wrapped, no need to do it again. - else if (test instanceof WrappedSuiteTestDecorator) - { - targetTest = (WrappedSuiteTestDecorator) test; - } - - // If size parameter values have been set, then wrap the test in an asymptotic test decorator. - if (params != null) - { - targetTest = new AsymptoticTestDecorator(targetTest, params, (repetitions == null) ? 1 : repetitions); - log.debug("Wrapped with asymptotic test decorator."); - log.debug("targetTest = " + targetTest); - } - - // If no size parameters are set but the repitions parameter is, then wrap the test in an asymptotic test decorator. - else if ((repetitions != null) && (repetitions > 1)) - { - targetTest = new AsymptoticTestDecorator(targetTest, new int[] { 1 }, repetitions); - log.debug("Wrapped with asymptotic test decorator."); - log.debug("targetTest = " + targetTest); - } - - // Apply any optional user specified decorators. - targetTest = applyOptionalUserDecorators(targetTest); - - // If a test run duration has been set then wrap the test in a duration test decorator. This will wrap on - // top of size, repeat or concurrency wrappings already applied. - if (duration != null) - { - DurationTestDecorator durationTest = new DurationTestDecorator(targetTest, duration); - targetTest = durationTest; - - log.debug("Wrapped with duration test decorator."); - log.debug("targetTest = " + targetTest); - - registerShutdownHook(durationTest); - } - - // ParameterVariationTestDecorator... - - // If a test thread concurrency level is set then wrap the test in a scaled test decorator. This will wrap on - // top of size scaling or repetition wrappings. - ScaledTestDecorator scaledDecorator; - - if ((threads != null) && ((threads.length > 1) || (MathUtils.maxInArray(threads) > 1))) - { - scaledDecorator = new ScaledTestDecorator(targetTest, threads); - targetTest = scaledDecorator; - log.debug("Wrapped with scaled test decorator."); - log.debug("targetTest = " + targetTest); - } - else - { - scaledDecorator = new ScaledTestDecorator(targetTest, new int[] { 1 }); - targetTest = scaledDecorator; - log.debug("Wrapped with scaled test decorator with default of 1 thread."); - log.debug("targetTest = " + targetTest); - } - - // Register the scaled test decorators shutdown hook. - registerShutdownHook(scaledDecorator); - - return targetTest; - } - - /** - * If there were any user specified test decorators on the command line, this method instantiates them and wraps - * the test in them, from inner-most to outer-most in the order in which the decorators were supplied on the - * command line. - * - * @param targetTest The test to wrap. - * - * @return A wrapped test. - */ - protected WrappedSuiteTestDecorator applyOptionalUserDecorators(WrappedSuiteTestDecorator targetTest) - { - // If there are user defined test decorators apply them in order now. - for (TestDecoratorFactory factory : decoratorFactories) - { - targetTest = factory.decorateTest(targetTest); - } - - return targetTest; - } - - /** - * Creates the TestResult object to be used for test runs. See {@link TKTestResult} for more information and the - * enhanced test result class that this uses. - * - * @return An instance of the enhanced test result object, {@link TKTestResult}. - */ - protected TestResult createTestResult() - { - log.debug("protected TestResult createTestResult(): called"); - - TKTestResult result = new TKTestResult(delay, testCaseName); - - // Check if a directory to output reports to has been specified and attach test listeners if so. - if (reportDir != null) - { - // Create the report directory if it does not already exist. - File reportDirFile = new File(reportDir); - - if (!reportDirFile.exists()) - { - reportDirFile.mkdir(); - } - - // Create the results file (make the name of this configurable as a command line parameter). - Writer timingsWriter; - - // Always set up a console feedback listener. - ConsoleTestListener feedbackListener = new ConsoleTestListener(); - result.addListener(feedbackListener); - result.addTKTestListener(feedbackListener); - - // Set up an XML results listener to output the timings to the results file, if requested on the command line. - if (xmlResults) - { - try - { - File timingsFile = new File(reportDirFile, "TEST-" + currentTestClassName + ".xml"); - timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); - } - catch (IOException e) - { - throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); - } - - XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName); - result.addListener(listener); - result.addTKTestListener(listener); - - registerShutdownHook(listener); - } - - // Set up an CSV results listener to output the timings to the results file, if requested on the command line. - if (csvResults) - { - try - { - File timingsFile = - new File(reportDirFile, testRunName + "-" + TIME_STAMP_FORMAT.format(new Date()) + "-timings.csv"); - timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); - } - catch (IOException e) - { - throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); - } - - CSVTestListener listener = new CSVTestListener(timingsWriter); - result.addListener(listener); - result.addTKTestListener(listener); - - // Register the results listeners shutdown hook to flush its data if the test framework is shutdown - // prematurely. - registerShutdownHook(listener); - } - - // Register the results listeners shutdown hook to flush its data if the test framework is shutdown - // prematurely. - // registerShutdownHook(listener); - - // Record the start time of the batch. - // result.notifyStartBatch(); - - // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters. - // Inform any test listers of the test properties. - result.notifyTestProperties(TestContextProperties.getAccessedProps()); - } - - return result; - } - - /** - * Registers the shutdown hook of a {@link ShutdownHookable}. - * - * @param hookable The hookable to register. - */ - protected void registerShutdownHook(ShutdownHookable hookable) - { - Runtime.getRuntime().addShutdownHook(hookable.getShutdownHook()); - } - - /** - * Initializes the test runner with the provided command line arguments and and starts the test run. - * - * @param testClassName The fully qualified name of the test class to run. - * - * @return The test results. - * - * @throws Exception Any exceptions from running the tests are allowed to fall through. - */ - protected TestResult start(String testClassName) throws Exception - { - // Record the current test class, so that the test results can be output to a file incorporating this name. - this.currentTestClassName = testClassName; - - // Delegate to the super method to run the tests. - return super.start(new String[] { testClassName }); - } -} +/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.listeners.CSVTestListener; +import org.apache.qpid.junit.extensions.listeners.ConsoleTestListener; +import org.apache.qpid.junit.extensions.listeners.XMLTestListener; +import org.apache.qpid.junit.extensions.util.CommandLineParser; +import org.apache.qpid.junit.extensions.util.MathUtils; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +/** + * TKTestRunner extends {@link junit.textui.TestRunner} with the ability to run tests multiple times, to execute a test + * simultaneously using many threads, to put a delay between test runs and adds support for tests that take integer + * parameters that can be 'stepped' through on multiple test runs. These features can be accessed by using this class + * as an entry point and passing command line arguments to specify which features to use: + * + *

      + * -w ms       The number of milliseconds between invocations of test cases.
      + * -c pattern  The number of tests to run concurrently.
      + * -r num      The number of times to repeat each test.
      + * -d duration The length of time to run the tests for.
      + * -t name     The name of the test case to execute.
      + * -s pattern  The size parameter to run tests with.
      + * -o dir      The name of the directory to output test timings to.
      + * --csv       Output test results in CSV format.
      + * --xml       Output test results in XML format.
      + * 
      + * + *

      This command line may also have trailing 'name=value' parameters added to it. All of these values are added + * to the test context properties and passed to the test, which can access them by name. + * + *

      The pattern arguments are of the form [lowest(: ...)(: highest)](:sample=s)(:exp), where round brackets + * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided + * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples. + * + *

      The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least + * one of the optional values must be present. + * + *

      When specifying optional test parameters on the command line, in 'name=value' format, it is also possible to use + * the format 'name=[value1:value2:value3:...]', to specify multiple values for a parameter. All permutations of all + * parameters with multiple values will be created and tested. If the values are numerical, it is also possible to use + * the sequence generation patterns instead of fully specifying all of the values. + * + *

      Here are some examples: + * + *

      + *
       -c [10:20:30:40:50] 
      Runs the test with 10,20,...,50 threads. + *
       -s [1:100]:samples=10 
      + *
      Runs the test with ten different size parameters evenly spaced between 1 and 100. + *
       -s [1:1000000]:samples=10:exp 
      + *
      Runs the test with ten different size parameters exponentially spaced between 1 and 1000000. + *
       -r 10 
      Runs each test ten times. + *
       -d 10H 
      Runs the test repeatedly for 10 hours. + *
       -d 1M, -r 10 
      + *
      Runs the test repeatedly for 1 minute but only takes a timing sample every 10 test runs. + *
       -r 10, -c [1:5:10:50], -s [100:1000:10000] 
      + *
      Runs 12 test cycles (4 concurrency samples * 3 size sample), with 10 repeats each. In total the test + * will be run 199 times (3 + 15 + 30 + 150) + *
       cache=true 
      Passes the 'cache' parameter with value 'true' to the test. + *
       cache=[true:false] 
      Runs the test with the 'cache' parameter set to 'true' and 'false'. + *
       cacheSize=[1000:1000000],samples=4,exp 
      + *
      Runs the test with the 'cache' parameter set to a series of exponentially increasing sizes. + *
      + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Create the test configuration specified by the command line parameters. + *
      + * + * @todo Verify that the output directory exists or can be created. + * + * @todo Verify that the specific named test case to execute exists. + * + * @todo Drop the delay parameter as it is being replaced by throttling. + * + * @todo Completely replace the test ui test runner, instead of having TKTestRunner inherit from it, its just not + * good code to extend. + * + * @author Rupert Smith + */ +public class TKTestRunner extends TestRunnerImprovedErrorHandling +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TKTestRunner.class); + + /** Used for displaying information on the console. */ + // private static final Logger console = Logger.getLogger("CONSOLE." + TKTestRunner.class.getName()); + + /** Used for generating the timestamp when naming output files. */ + protected static final DateFormat TIME_STAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); + + /** Number of times to rerun the test. */ + protected Integer repetitions = 1; + + /** The length of time to run the tests for. */ + protected Long duration; + + /** Number of threads running the tests. */ + protected int[] threads; + + /** Delay in ms to wait between two test cases. */ + protected int delay = 0; + + /** The parameter values to pass to parameterized tests. */ + protected int[] params; + + /** Name of the single test case to execute. */ + protected String testCaseName = null; + + /** Name of the test class. */ + protected String testClassName = null; + + /** Name of the test run. */ + protected String testRunName = null; + + /** Directory to output XML reports into, if specified. */ + protected String reportDir = null; + + /** Flag that indicates the CSV results listener should be used to output results. */ + protected boolean csvResults; + + /** Flag that indiciates the XML results listener should be used to output results. */ + protected boolean xmlResults; + + /** + * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult} + * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable. + */ + protected String currentTestClassName; + + /** Holds the test results object, which is reponsible for instrumenting tests/threads to record results. */ + protected TKTestResult result; + + /** Holds a list of factories for instantiating optional user specified test decorators. */ + protected List decoratorFactories; + + /** + * Constructs a TKTestRunner using System.out for all the output. + * + * @param repetitions The number of times to repeat the test, or test batch size. + * @param duration The length of time to run the tests for. -1 means no duration has been set. + * @param threads The concurrency levels to ramp up to. + * @param delay A delay in milliseconds between test runs. + * @param params The sets of 'size' parameters to pass to test. + * @param testCaseName The name of the test case to run. + * @param reportDir The directory to output the test results to. + * @param runName The name of the test run; used to name the output file. + * @param csvResults true if the CSV results listener should be attached. + * @param xmlResults true if the XML results listener should be attached. + * @param decoratorFactories List of factories for user specified decorators. + */ + public TKTestRunner(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName, + String reportDir, String runName, boolean csvResults, boolean xmlResults, + List decoratorFactories) + { + super(new NullResultPrinter(System.out)); + + log.debug("public TKTestRunner(): called"); + + // Keep all the test parameters. + this.repetitions = repetitions; + this.duration = duration; + this.threads = threads; + this.delay = delay; + this.params = params; + this.testCaseName = testCaseName; + this.reportDir = reportDir; + this.testRunName = runName; + this.csvResults = csvResults; + this.xmlResults = xmlResults; + this.decoratorFactories = decoratorFactories; + } + + /** + * The entry point for the toolkit test runner. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + // Use the command line parser to evaluate the command line. + CommandLineParser commandLine = + new CommandLineParser( + new String[][] + { + { "w", "The number of milliseconds between invocations of test cases.", "ms", "false" }, + { "c", "The number of tests to run concurrently.", "num", "false", MathUtils.SEQUENCE_REGEXP }, + { "r", "The number of times to repeat each test.", "num", "false" }, + { "d", "The length of time to run the tests for.", "duration", "false", MathUtils.DURATION_REGEXP }, + { "f", "The maximum rate to call the tests at.", "frequency", "false", "^([1-9][0-9]*)/([1-9][0-9]*)$" }, + { "s", "The size parameter to run tests with.", "size", "false", MathUtils.SEQUENCE_REGEXP }, + { "t", "The name of the test case to execute.", "name", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" }, + { "n", "A name for this test run, used to name the output file.", "name", "true" }, + { + "X:decorators", "A list of additional test decorators to wrap the tests in.", + "\"class.name[:class.name]*\"", "false" + }, + { "1", "Test class.", "class", "true" }, + { "-csv", "Output test results in CSV format.", null, "false" }, + { "-xml", "Output test results in XML format.", null, "false" } + }); + + // Capture the command line arguments or display errors and correct usage and then exit. + ParsedProperties options = null; + + try + { + options = new ParsedProperties(commandLine.parseCommandLine(args)); + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(FAILURE_EXIT); + } + + // Extract the command line options. + Integer delay = options.getPropertyAsInteger("w"); + String threadsString = options.getProperty("c"); + Integer repetitions = options.getPropertyAsInteger("r"); + String durationString = options.getProperty("d"); + String paramsString = options.getProperty("s"); + String testCaseName = options.getProperty("t"); + String reportDir = options.getProperty("o"); + String testRunName = options.getProperty("n"); + String decorators = options.getProperty("X:decorators"); + String testClassName = options.getProperty("1"); + boolean csvResults = options.getPropertyAsBoolean("-csv"); + boolean xmlResults = options.getPropertyAsBoolean("-xml"); + + int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString); + int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString); + Long duration = (durationString == null) ? null : MathUtils.parseDuration(durationString); + + // The test run name defaults to the test class name unless a value was specified for it. + testRunName = (testRunName == null) ? testClassName : testRunName; + + // Add all the command line options and trailing settings to test context properties. Tests may pick up + // overridden values from there, and these values will be logged in the test results, for analysis and + // to make tests repeatable. + commandLine.addTrailingPairsToProperties(TestContextProperties.getInstance()); + commandLine.addOptionsToProperties(TestContextProperties.getInstance()); + + // Create and start the test runner. + try + { + // Create a list of test decorator factories for use specified decorators to be applied. + List decoratorFactories = parseDecorators(decorators); + + TKTestRunner testRunner = + new TKTestRunner(repetitions, duration, threads, (delay == null) ? 0 : delay, params, testCaseName, + reportDir, testRunName, csvResults, xmlResults, decoratorFactories); + + TestResult testResult = testRunner.start(testClassName); + + if (!testResult.wasSuccessful()) + { + System.exit(FAILURE_EXIT); + } + } + catch (Exception e) + { + System.err.println(e.getMessage()); + e.printStackTrace(new PrintStream(System.err)); + System.exit(EXCEPTION_EXIT); + } + } + + /** + * Parses a list of test decorators, in the form "class.name[:class.name]*", and creates factories for those + * TestDecorator classes , and returns a list of the factories. This list of factories will be in the same + * order as specified in the string. The factories can be used to succesively wrap tests in layers of + * decorators, as decorators themselves implement the 'Test' interface. + * + *

      If the string fails to parse, or if any of the decorators specified in it are cannot be loaded, or are not + * TestDecorators, a runtime exception with a suitable error message will be thrown. The factories themselves + * throw runtimes if the constructor method calls on the decorators fail. + * + * @param decorators The decorators list to be parsed. + * + * @return A list of instantiated decorators. + */ + protected static List parseDecorators(String decorators) + { + List result = new LinkedList(); + String toParse = decorators; + + // Check that the decorators string is not null or empty, returning an empty list of decorator factories it + // it is. + if ((decorators == null) || "".equals(decorators)) + { + return result; + } + + // Strip any leading and trailing quotes from the string. + if (toParse.charAt(0) == '\"') + { + toParse = toParse.substring(1, toParse.length() - 1); + } + + if (toParse.charAt(toParse.length() - 1) == '\"') + { + toParse = toParse.substring(0, toParse.length() - 2); + } + + // Instantiate all decorators. + for (String decoratorClassName : toParse.split(":")) + { + try + { + Class decoratorClass = Class.forName(decoratorClassName); + final Constructor decoratorConstructor = decoratorClass.getConstructor(WrappedSuiteTestDecorator.class); + + // Check that the decorator is an instance of WrappedSuiteTestDecorator. + if (!WrappedSuiteTestDecorator.class.isAssignableFrom(decoratorClass)) + { + throw new RuntimeException("The decorator class " + decoratorClassName + + " is not a sub-class of WrappedSuiteTestDecorator, which it needs to be."); + } + + result.add(new TestDecoratorFactory() + { + public WrappedSuiteTestDecorator decorateTest(Test test) + { + try + { + return (WrappedSuiteTestDecorator) decoratorConstructor.newInstance(test); + } + catch (InstantiationException e) + { + throw new RuntimeException( + "The decorator class " + decoratorConstructor.getDeclaringClass().getName() + + " cannot be instantiated.", e); + } + catch (IllegalAccessException e) + { + throw new RuntimeException( + "The decorator class " + decoratorConstructor.getDeclaringClass().getName() + + " does not have a publicly accessable constructor.", e); + } + catch (InvocationTargetException e) + { + throw new RuntimeException( + "The decorator class " + decoratorConstructor.getDeclaringClass().getName() + + " cannot be invoked.", e); + } + } + }); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("The decorator class " + decoratorClassName + " could not be found.", e); + } + catch (NoSuchMethodException e) + { + throw new RuntimeException("The decorator class " + decoratorClassName + + " does not have a constructor that accepts a single 'WrappedSuiteTestDecorator' argument.", e); + } + } + + return result; + } + + /** + * TestDecoratorFactory is a factory for creating test decorators from tests. + */ + protected interface TestDecoratorFactory + { + /** + * Decorates the specified test with a new decorator. + * + * @param test The test to decorate. + * + * @return The decorated test. + */ + public WrappedSuiteTestDecorator decorateTest(Test test); + } + + /** + * Runs a test or suite of tests, using the super class implemenation. This method wraps the test to be run + * in any test decorators needed to add in the configured toolkits enhanced junit functionality. + * + * @param test The test to run. + * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for. + * + * @return The results of the test run. + */ + public TestResult doRun(Test test, boolean wait) + { + log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called"); + + // Wrap the tests in decorators for duration, scaling, repetition, parameterization etc. + WrappedSuiteTestDecorator targetTest = decorateTests(test); + + // Delegate to the super method to run the decorated tests. + log.debug("About to call super.doRun"); + + TestResult result = super.doRun(targetTest, wait); + log.debug("super.doRun returned."); + + /*if (result instanceof TKTestResult) + { + TKTestResult tkResult = (TKTestResult) result; + + tkResult.notifyEndBatch(); + }*/ + + return result; + } + + /** + * Applies test decorators to the tests for parameterization, duration, scaling and repetition. + * + * @param test The test to decorat. + * + * @return The decorated test. + */ + protected WrappedSuiteTestDecorator decorateTests(Test test) + { + log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params))); + log.debug("repetitions = " + repetitions); + log.debug("threads = " + ((threads == null) ? null : MathUtils.printArray(threads))); + log.debug("duration = " + duration); + + // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling, + // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it. + WrappedSuiteTestDecorator targetTest = null; + + if (test instanceof TestSuite) + { + log.debug("targetTest is a TestSuite"); + + TestSuite suite = (TestSuite) test; + + int numTests = suite.countTestCases(); + log.debug("There are " + numTests + " in the suite."); + + for (int i = 0; i < numTests; i++) + { + Test nextTest = suite.testAt(i); + log.debug("suite.testAt(" + i + ") = " + nextTest); + + if (nextTest instanceof TimingControllerAware) + { + log.debug("nextTest is TimingControllerAware"); + } + + if (nextTest instanceof TestThreadAware) + { + log.debug("nextTest is TestThreadAware"); + } + } + + targetTest = new WrappedSuiteTestDecorator(suite); + log.debug("Wrapped with a WrappedSuiteTestDecorator."); + } + // If the test has already been wrapped, no need to do it again. + else if (test instanceof WrappedSuiteTestDecorator) + { + targetTest = (WrappedSuiteTestDecorator) test; + } + + // If size parameter values have been set, then wrap the test in an asymptotic test decorator. + if (params != null) + { + targetTest = new AsymptoticTestDecorator(targetTest, params, (repetitions == null) ? 1 : repetitions); + log.debug("Wrapped with asymptotic test decorator."); + log.debug("targetTest = " + targetTest); + } + + // If no size parameters are set but the repitions parameter is, then wrap the test in an asymptotic test decorator. + else if ((repetitions != null) && (repetitions > 1)) + { + targetTest = new AsymptoticTestDecorator(targetTest, new int[] { 1 }, repetitions); + log.debug("Wrapped with asymptotic test decorator."); + log.debug("targetTest = " + targetTest); + } + + // Apply any optional user specified decorators. + targetTest = applyOptionalUserDecorators(targetTest); + + // If a test run duration has been set then wrap the test in a duration test decorator. This will wrap on + // top of size, repeat or concurrency wrappings already applied. + if (duration != null) + { + DurationTestDecorator durationTest = new DurationTestDecorator(targetTest, duration); + targetTest = durationTest; + + log.debug("Wrapped with duration test decorator."); + log.debug("targetTest = " + targetTest); + + registerShutdownHook(durationTest); + } + + // ParameterVariationTestDecorator... + + // If a test thread concurrency level is set then wrap the test in a scaled test decorator. This will wrap on + // top of size scaling or repetition wrappings. + ScaledTestDecorator scaledDecorator; + + if ((threads != null) && ((threads.length > 1) || (MathUtils.maxInArray(threads) > 1))) + { + scaledDecorator = new ScaledTestDecorator(targetTest, threads); + targetTest = scaledDecorator; + log.debug("Wrapped with scaled test decorator."); + log.debug("targetTest = " + targetTest); + } + else + { + scaledDecorator = new ScaledTestDecorator(targetTest, new int[] { 1 }); + targetTest = scaledDecorator; + log.debug("Wrapped with scaled test decorator with default of 1 thread."); + log.debug("targetTest = " + targetTest); + } + + // Register the scaled test decorators shutdown hook. + registerShutdownHook(scaledDecorator); + + return targetTest; + } + + /** + * If there were any user specified test decorators on the command line, this method instantiates them and wraps + * the test in them, from inner-most to outer-most in the order in which the decorators were supplied on the + * command line. + * + * @param targetTest The test to wrap. + * + * @return A wrapped test. + */ + protected WrappedSuiteTestDecorator applyOptionalUserDecorators(WrappedSuiteTestDecorator targetTest) + { + // If there are user defined test decorators apply them in order now. + for (TestDecoratorFactory factory : decoratorFactories) + { + targetTest = factory.decorateTest(targetTest); + } + + return targetTest; + } + + /** + * Creates the TestResult object to be used for test runs. See {@link TKTestResult} for more information and the + * enhanced test result class that this uses. + * + * @return An instance of the enhanced test result object, {@link TKTestResult}. + */ + protected TestResult createTestResult() + { + log.debug("protected TestResult createTestResult(): called"); + + TKTestResult result = new TKTestResult(delay, testCaseName); + + // Check if a directory to output reports to has been specified and attach test listeners if so. + if (reportDir != null) + { + // Create the report directory if it does not already exist. + File reportDirFile = new File(reportDir); + + if (!reportDirFile.exists()) + { + reportDirFile.mkdir(); + } + + // Create the results file (make the name of this configurable as a command line parameter). + Writer timingsWriter; + + // Always set up a console feedback listener. + ConsoleTestListener feedbackListener = new ConsoleTestListener(); + result.addListener(feedbackListener); + result.addTKTestListener(feedbackListener); + + // Set up an XML results listener to output the timings to the results file, if requested on the command line. + if (xmlResults) + { + try + { + File timingsFile = new File(reportDirFile, "TEST-" + currentTestClassName + ".xml"); + timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); + } + + XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName); + result.addListener(listener); + result.addTKTestListener(listener); + + registerShutdownHook(listener); + } + + // Set up an CSV results listener to output the timings to the results file, if requested on the command line. + if (csvResults) + { + try + { + File timingsFile = + new File(reportDirFile, testRunName + "-" + TIME_STAMP_FORMAT.format(new Date()) + "-timings.csv"); + timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create the log file to write test results to: " + e, e); + } + + CSVTestListener listener = new CSVTestListener(timingsWriter); + result.addListener(listener); + result.addTKTestListener(listener); + + // Register the results listeners shutdown hook to flush its data if the test framework is shutdown + // prematurely. + registerShutdownHook(listener); + } + + // Register the results listeners shutdown hook to flush its data if the test framework is shutdown + // prematurely. + // registerShutdownHook(listener); + + // Record the start time of the batch. + // result.notifyStartBatch(); + + // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters. + // Inform any test listers of the test properties. + result.notifyTestProperties(TestContextProperties.getAccessedProps()); + } + + return result; + } + + /** + * Registers the shutdown hook of a {@link ShutdownHookable}. + * + * @param hookable The hookable to register. + */ + protected void registerShutdownHook(ShutdownHookable hookable) + { + Runtime.getRuntime().addShutdownHook(hookable.getShutdownHook()); + } + + /** + * Initializes the test runner with the provided command line arguments and and starts the test run. + * + * @param testClassName The fully qualified name of the test class to run. + * + * @return The test results. + * + * @throws Exception Any exceptions from running the tests are allowed to fall through. + */ + protected TestResult start(String testClassName) throws Exception + { + // Record the current test class, so that the test results can be output to a file incorporating this name. + this.currentTestClassName = testClassName; + + // Delegate to the super method to run the tests. + return super.start(new String[] { testClassName }); + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java index 9b4a8707db..edd79b3697 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java @@ -1,131 +1,131 @@ -/* - * - * 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.junit.extensions; - -import junit.framework.Test; -import junit.framework.TestResult; - -import junit.runner.Version; - -import junit.textui.ResultPrinter; -import junit.textui.TestRunner; - -import org.apache.log4j.Logger; - -import java.io.PrintStream; - -/** - * The {@link junit.textui.TestRunner} does not provide very good error handling. It does not wrap exceptions and - * does not print out stack traces, losing valuable error tracing information. This class overrides methods in it - * in order to improve their error handling. The {@link TKTestRunner} is then built on top of this. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      - * - * @author Rupert Smith - */ -public class TestRunnerImprovedErrorHandling extends TestRunner -{ - /** Used for logging. */ - Logger log = Logger.getLogger(TestRunnerImprovedErrorHandling.class); - - /** - * Delegates to the super constructor. - */ - public TestRunnerImprovedErrorHandling() - { - super(); - } - - /** - * Delegates to the super constructor. - * - * @param printStream The location to write test results to. - */ - public TestRunnerImprovedErrorHandling(PrintStream printStream) - { - super(printStream); - } - - /** - * Delegates to the super constructor. - * - * @param resultPrinter The location to write test results to. - */ - public TestRunnerImprovedErrorHandling(ResultPrinter resultPrinter) - { - super(resultPrinter); - } - - /** - * Starts a test run. Analyzes the command line arguments - * and runs the given test suite. - * - * @param args The command line arguments. - * - * @return The test results. - * - * @throws Exception Any exceptions falling through the tests are wrapped in Exception and rethrown. - */ - protected TestResult start(String[] args) throws Exception - { - String testCase = ""; - boolean wait = false; - - for (int i = 0; i < args.length; i++) - { - if (args[i].equals("-wait")) - { - wait = true; - } - else if (args[i].equals("-c")) - { - testCase = extractClassName(args[++i]); - } - else if (args[i].equals("-v")) - { - System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma"); - } - else - { - testCase = args[i]; - } - } - - if (testCase.equals("")) - { - throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class"); - } - - try - { - Test suite = getTest(testCase); - - return doRun(suite, wait); - } - catch (Exception e) - { - log.warn("Got exception whilst creating and running test suite.", e); - throw new Exception("Could not create and run the test suite.", e); - } - } -} +/* + * + * 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.junit.extensions; + +import junit.framework.Test; +import junit.framework.TestResult; + +import junit.runner.Version; + +import junit.textui.ResultPrinter; +import junit.textui.TestRunner; + +import org.apache.log4j.Logger; + +import java.io.PrintStream; + +/** + * The {@link junit.textui.TestRunner} does not provide very good error handling. It does not wrap exceptions and + * does not print out stack traces, losing valuable error tracing information. This class overrides methods in it + * in order to improve their error handling. The {@link TKTestRunner} is then built on top of this. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      + * + * @author Rupert Smith + */ +public class TestRunnerImprovedErrorHandling extends TestRunner +{ + /** Used for logging. */ + Logger log = Logger.getLogger(TestRunnerImprovedErrorHandling.class); + + /** + * Delegates to the super constructor. + */ + public TestRunnerImprovedErrorHandling() + { + super(); + } + + /** + * Delegates to the super constructor. + * + * @param printStream The location to write test results to. + */ + public TestRunnerImprovedErrorHandling(PrintStream printStream) + { + super(printStream); + } + + /** + * Delegates to the super constructor. + * + * @param resultPrinter The location to write test results to. + */ + public TestRunnerImprovedErrorHandling(ResultPrinter resultPrinter) + { + super(resultPrinter); + } + + /** + * Starts a test run. Analyzes the command line arguments + * and runs the given test suite. + * + * @param args The command line arguments. + * + * @return The test results. + * + * @throws Exception Any exceptions falling through the tests are wrapped in Exception and rethrown. + */ + protected TestResult start(String[] args) throws Exception + { + String testCase = ""; + boolean wait = false; + + for (int i = 0; i < args.length; i++) + { + if (args[i].equals("-wait")) + { + wait = true; + } + else if (args[i].equals("-c")) + { + testCase = extractClassName(args[++i]); + } + else if (args[i].equals("-v")) + { + System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma"); + } + else + { + testCase = args[i]; + } + } + + if (testCase.equals("")) + { + throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class"); + } + + try + { + Test suite = getTest(testCase); + + return doRun(suite, wait); + } + catch (Exception e) + { + log.warn("Got exception whilst creating and running test suite.", e); + throw new Exception("Could not create and run the test suite.", e); + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java index aaa773260d..d7de2822a2 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java @@ -1,49 +1,49 @@ -/* - * - * 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.junit.extensions; - -/** - * This interface can be implemented by tests that want to know if they are being run concurrently. It provides - * lifecycle notification events to tell the test implementation when test threads are being created and destroyed. - * This can assist tests in creating and destroying resources that exist over the life of a test thread. A single - * test thread can excute the same test many times, and often it is convenient to keep resources, for example network - * connections, open over many test calls. - * - *

      - *
      CRC Card
      Responsibilities - *
      Set up per thread test fixtures. - *
      Clean up per thread test fixtures. - *
      - * - * @author Rupert Smith - */ -public interface TestThreadAware -{ - /** - * Called when a test thread is created. - */ - public void threadSetUp(); - - /** - * Called when a test thread is destroyed. - */ - public void threadTearDown(); -} +/* + * + * 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.junit.extensions; + +/** + * This interface can be implemented by tests that want to know if they are being run concurrently. It provides + * lifecycle notification events to tell the test implementation when test threads are being created and destroyed. + * This can assist tests in creating and destroying resources that exist over the life of a test thread. A single + * test thread can excute the same test many times, and often it is convenient to keep resources, for example network + * connections, open over many test calls. + * + *

      + *
      CRC Card
      Responsibilities + *
      Set up per thread test fixtures. + *
      Clean up per thread test fixtures. + *
      + * + * @author Rupert Smith + */ +public interface TestThreadAware +{ + /** + * Called when a test thread is created. + */ + public void threadSetUp(); + + /** + * Called when a test thread is destroyed. + */ + public void threadTearDown(); +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java index 955e47c25b..1ea8e8e2be 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java @@ -1,73 +1,73 @@ -/* - * - * 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.junit.extensions; - -/** - * Throttle is an interface that supplies a {@link #throttle} method, that can only be called at the rate specified - * in a call to the {@link #setRate} method. This can be used to restict processing to run at a certain number - * of operations per second. - * - *

      Throttle also supplies a method to check the throttle rate, without waiting. This could be used to update a user - * interface every time an event occurs, but only up to a maximum rate. For example, as elements are added to a list, - * a count of elements is updated for the user to see, but only up to a maximum rate of ten updates a second, as updating - * faster than that slows the processing of element-by-element additions to the list unnecessarily. - * - *

      - *
      CRC Card
      Responsibilities - *
      Accept throttling rate in operations per second. - *
      Inject short pauses to fill-out processing cycles to a specified rate. - *
      Check against a throttle speed without waiting. - *
      - * - * @author Rupert Smith - */ -public interface Throttle -{ - /** - * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse - * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer. - * The value must also be larger than zero. - * - * @param hertz The throttling rate in cycles per second. - */ - public void setRate(float hertz); - - /** - * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this - * it will inject short pauses to restrict the call rate to that rate. - * - *

      If the thread executing this method is interrupted, it must ensure that the threads interrupt thread - * remains set upon exit from the method. This method does not expose InterruptedException, to indicate interruption - * of the throttle during a timed wait. It may be changed so that it does. - */ - public void throttle(); - - /** - * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater - * than that equal to the inverse of the throttling rate has passed since it was last called and returned true - * - * @return true if a length of time greater than that equal to the inverse of the throttling rate has - * passed since this method was last called and returned true, false otherwise. The very - * first time this method is called on a throttle, it returns true as the base case to the above - * self-referential definition. - */ - public boolean checkThrottle(); -} +/* + * + * 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.junit.extensions; + +/** + * Throttle is an interface that supplies a {@link #throttle} method, that can only be called at the rate specified + * in a call to the {@link #setRate} method. This can be used to restict processing to run at a certain number + * of operations per second. + * + *

      Throttle also supplies a method to check the throttle rate, without waiting. This could be used to update a user + * interface every time an event occurs, but only up to a maximum rate. For example, as elements are added to a list, + * a count of elements is updated for the user to see, but only up to a maximum rate of ten updates a second, as updating + * faster than that slows the processing of element-by-element additions to the list unnecessarily. + * + *

      + *
      CRC Card
      Responsibilities + *
      Accept throttling rate in operations per second. + *
      Inject short pauses to fill-out processing cycles to a specified rate. + *
      Check against a throttle speed without waiting. + *
      + * + * @author Rupert Smith + */ +public interface Throttle +{ + /** + * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse + * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer. + * The value must also be larger than zero. + * + * @param hertz The throttling rate in cycles per second. + */ + public void setRate(float hertz); + + /** + * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this + * it will inject short pauses to restrict the call rate to that rate. + * + *

      If the thread executing this method is interrupted, it must ensure that the threads interrupt thread + * remains set upon exit from the method. This method does not expose InterruptedException, to indicate interruption + * of the throttle during a timed wait. It may be changed so that it does. + */ + public void throttle(); + + /** + * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater + * than that equal to the inverse of the throttling rate has passed since it was last called and returned true + * + * @return true if a length of time greater than that equal to the inverse of the throttling rate has + * passed since this method was last called and returned true, false otherwise. The very + * first time this method is called on a throttle, it returns true as the base case to the above + * self-referential definition. + */ + public boolean checkThrottle(); +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java index 7b5763f1de..b69df84045 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java @@ -1,175 +1,175 @@ -/* - * - * 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.junit.extensions; - -/** - * A TimingController is a interface that a test that is aware of the fact that it is being timed can use to manage - * the timer. Using this interface tests can suspend and resume test timers. This is usefull if you want to exclude - * some expensive preparation from being timed as part of a test. In general when timing tests to measure the - * performance of code, you should try to set up data in the #setUp where possible, or as static members in the test - * class. This is not always convenient, this interface gives you a way to suspend and resume, or event completely - * restart test timers, to get accurate measurements. - * - *

      The interface can also be used to register multiple test pass/fails and timings from a single test method. - * In some cases it is easier to write tests in this way. For example a concurrent and asynchronous test may make - * many asynchronous requests and then wait for replies to all its requests. Writing such a test with one send/reply - * per test method and trying to scale up using many threads will quickly run into limitations if more than about - * 100 asynchronous calls need to be made at once. A better way to write such a test is as a single method that sends - * many (perhaps thousands or millions) and waits for replies in two threads, one for send, one for replies. It can - * then log pass/fails and timings on each individual reply as they come back in, even though the test has been written - * to send thousands of requests per test method in order to do volume testing. - * - *

      If when the {@link #completeTest(boolean)} is called, the test runner decides that testing should stop (perhaps - * because a duration test has expired), it throws an InterruptedException to indicate that the test method should stop - * immediately. The test method can do this by allowing this exception to fall through, if no other clean-up handling - * is necessary, or it can simply return as soon as it possibly can. The test runner will still call the tearDown - * method in the usual way when this happens. - * - *

      Below are some examples of how this can be used. Not how checking that the timing controller is really available - * rather than assuming it is, means that the test can run as an ordinary JUnit test under the default test runners. In - * general code should be written to take advantage of the extended capabilities of junit toolkit, without assuming they - * are going to be run under its test runner. - * - *

      - * public class MyTest extends TestCase implements TimingControllerAware {
      - * ...
      - *
      - *    timingUtils = this.getTimingController();
      - *
      - *    // Do expensive data preparation here...
      - *
      - *    if (timingUtils != null)
      - *        timingUtils.restart();
      - * 
      - * - *
      - * public class MyTest extends TestCase implements TimingControllerAware {
      - * ...
      - *
      - *   public void myVolumeTest(int size) {
      - *
      - *    timingUtils = this.getTimingController();
      - *
      - *    boolean stopNow = false;
      - *
      - *    // In Sender thread.
      - *      for(int i = 0; !stopNow && i < size; i++)
      - *        // Send request i.
      - *        ...
      - *
      - *    // In Receiver thread.
      - *    onReceive(Object o) {
      - *      try {
      - *      // Check o is as expected.
      - *      if (....)
      - *      {
      - *        if (timingUtils != null)
      - *          timingUtils.completeTest(true);
      - *      }
      - *      else
      - *      {
      - *        if (timingUtils != null)
      - *          timingUtils.completeTest(false);
      - *      }
      - *      } catch (InterruptedException e) {
      - *        stopNow = true;
      - *        return;
      - *      }
      - *    }
      - * 
      - * - *

      - *
      CRC Card
      Responsibilities - *
      Allow test timers to be suspended, restarted or reset. - *
      Allow tests to register multiple pass/fails and timings. - *
      - * - * @author Rupert Smith - */ -public interface TimingController -{ - /** - * Gets the timing controller associated with the current test thread. Tests that use timing controller should - * always get the timing controller from this method in the same thread that called the setUp, tearDown or test - * method. The controller returned by this method may be called from any thread because it remembers the thread - * id of the original test thread. - * - * @return The timing controller associated with the current test thread. - */ - public TimingController getControllerForCurrentThread(); - - /** - * Suspends the test timer. - * - * @return The current time in nanoseconds. - */ - public long suspend(); - - /** - * Allows the test timer to continue running after a suspend. - * - * @return The current time in nanoseconds. - */ - public long resume(); - - /** - * Completely restarts the test timer from zero. - * - * @return The current time in nanoseconds. - */ - public long restart(); - - /** - * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of - * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters. - * - * @param testPassed Whether or not this timing is for a test pass or fail. - * - * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to - * indicate to the test method that it should stop immediately. - */ - public void completeTest(boolean testPassed) throws InterruptedException; - - /** - * Register an additional pass/fail for the current test. The test result is applies to a test of the specified - * 'size' parmeter. - * - * @param testPassed Whether or not this timing is for a test pass or fail. - * @param param The test parameter size for parameterized tests. - * - * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to - * indicate to the test method that it should stop immediately. - */ - public void completeTest(boolean testPassed, int param) throws InterruptedException; - - /** - * Register an additional pass/fail for the current test. The test result is applies to a test of the specified - * 'size' parmeter and allows the caller to sepecify the timing to log. - * - * @param testPassed Whether or not this timing is for a test pass or fail. - * @param param The test parameter size for parameterized tests. - * @param timeNanos The time in nano seconds to log the test result with. - * - * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to - * indicate to the test method that it should stop immediately. - */ - public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException; -} +/* + * + * 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.junit.extensions; + +/** + * A TimingController is a interface that a test that is aware of the fact that it is being timed can use to manage + * the timer. Using this interface tests can suspend and resume test timers. This is usefull if you want to exclude + * some expensive preparation from being timed as part of a test. In general when timing tests to measure the + * performance of code, you should try to set up data in the #setUp where possible, or as static members in the test + * class. This is not always convenient, this interface gives you a way to suspend and resume, or event completely + * restart test timers, to get accurate measurements. + * + *

      The interface can also be used to register multiple test pass/fails and timings from a single test method. + * In some cases it is easier to write tests in this way. For example a concurrent and asynchronous test may make + * many asynchronous requests and then wait for replies to all its requests. Writing such a test with one send/reply + * per test method and trying to scale up using many threads will quickly run into limitations if more than about + * 100 asynchronous calls need to be made at once. A better way to write such a test is as a single method that sends + * many (perhaps thousands or millions) and waits for replies in two threads, one for send, one for replies. It can + * then log pass/fails and timings on each individual reply as they come back in, even though the test has been written + * to send thousands of requests per test method in order to do volume testing. + * + *

      If when the {@link #completeTest(boolean)} is called, the test runner decides that testing should stop (perhaps + * because a duration test has expired), it throws an InterruptedException to indicate that the test method should stop + * immediately. The test method can do this by allowing this exception to fall through, if no other clean-up handling + * is necessary, or it can simply return as soon as it possibly can. The test runner will still call the tearDown + * method in the usual way when this happens. + * + *

      Below are some examples of how this can be used. Not how checking that the timing controller is really available + * rather than assuming it is, means that the test can run as an ordinary JUnit test under the default test runners. In + * general code should be written to take advantage of the extended capabilities of junit toolkit, without assuming they + * are going to be run under its test runner. + * + *

      + * public class MyTest extends TestCase implements TimingControllerAware {
      + * ...
      + *
      + *    timingUtils = this.getTimingController();
      + *
      + *    // Do expensive data preparation here...
      + *
      + *    if (timingUtils != null)
      + *        timingUtils.restart();
      + * 
      + * + *
      + * public class MyTest extends TestCase implements TimingControllerAware {
      + * ...
      + *
      + *   public void myVolumeTest(int size) {
      + *
      + *    timingUtils = this.getTimingController();
      + *
      + *    boolean stopNow = false;
      + *
      + *    // In Sender thread.
      + *      for(int i = 0; !stopNow && i < size; i++)
      + *        // Send request i.
      + *        ...
      + *
      + *    // In Receiver thread.
      + *    onReceive(Object o) {
      + *      try {
      + *      // Check o is as expected.
      + *      if (....)
      + *      {
      + *        if (timingUtils != null)
      + *          timingUtils.completeTest(true);
      + *      }
      + *      else
      + *      {
      + *        if (timingUtils != null)
      + *          timingUtils.completeTest(false);
      + *      }
      + *      } catch (InterruptedException e) {
      + *        stopNow = true;
      + *        return;
      + *      }
      + *    }
      + * 
      + * + *

      + *
      CRC Card
      Responsibilities + *
      Allow test timers to be suspended, restarted or reset. + *
      Allow tests to register multiple pass/fails and timings. + *
      + * + * @author Rupert Smith + */ +public interface TimingController +{ + /** + * Gets the timing controller associated with the current test thread. Tests that use timing controller should + * always get the timing controller from this method in the same thread that called the setUp, tearDown or test + * method. The controller returned by this method may be called from any thread because it remembers the thread + * id of the original test thread. + * + * @return The timing controller associated with the current test thread. + */ + public TimingController getControllerForCurrentThread(); + + /** + * Suspends the test timer. + * + * @return The current time in nanoseconds. + */ + public long suspend(); + + /** + * Allows the test timer to continue running after a suspend. + * + * @return The current time in nanoseconds. + */ + public long resume(); + + /** + * Completely restarts the test timer from zero. + * + * @return The current time in nanoseconds. + */ + public long restart(); + + /** + * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of + * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed) throws InterruptedException; + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param) throws InterruptedException; + + /** + * Register an additional pass/fail for the current test. The test result is applies to a test of the specified + * 'size' parmeter and allows the caller to sepecify the timing to log. + * + * @param testPassed Whether or not this timing is for a test pass or fail. + * @param param The test parameter size for parameterized tests. + * @param timeNanos The time in nano seconds to log the test result with. + * + * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to + * indicate to the test method that it should stop immediately. + */ + public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException; +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java index 1ccdc7dbad..11db87e073 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java @@ -1,43 +1,43 @@ -/* - * - * 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.junit.extensions; - -/** - * TimingControllerAware is an interface that tests that manipulate the timing controller should implement. It enables - * the TK test runner to set the test up with a handle on the timing controller which the test can use to call back - * to the test runner to manage the timers. - * - *

      - *
      CRC Card
      Responsibilities - *
      Provide timing controller insertion point for tests. - *
      - * - * @author Rupert Smith - */ -public interface TimingControllerAware -{ - /** - * Used by test runners that can supply a {@link TimingController} to set the controller on an aware test. - * - * @param controller The timing controller. - */ - public void setTimingController(TimingController controller); -} +/* + * + * 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.junit.extensions; + +/** + * TimingControllerAware is an interface that tests that manipulate the timing controller should implement. It enables + * the TK test runner to set the test up with a handle on the timing controller which the test can use to call back + * to the test runner to manage the timers. + * + *

      + *
      CRC Card
      Responsibilities + *
      Provide timing controller insertion point for tests. + *
      + * + * @author Rupert Smith + */ +public interface TimingControllerAware +{ + /** + * Used by test runners that can supply a {@link TimingController} to set the controller on an aware test. + * + * @param controller The timing controller. + */ + public void setTimingController(TimingController controller); +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java index 7a1e537b1c..d5690fc24a 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java @@ -1,134 +1,134 @@ -/* - * - * 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.junit.extensions; - -import junit.extensions.TestDecorator; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * WrappedSuiteTestDecorator is a test decorator that wraps a test suite, or another wrapped suite, but provides the - * same functionality for the {@link junit.extensions.TestDecorator#countTestCases()} and {@link TestSuite#testAt(int)} - * methods as the underlying suite. It returns the values that these methods provide, to enable classes using decorated - * tests to drill down to the underlying tests in the suite. That is to say that it indexes and reports the number of - * distinct tests in the suite, not the number of test runs that would result from, for example, wrapping the suite in a - * repeating decorator. - * - *

      - *
      CRC Card
      Responsibilities - *
      Provide access to the underlying tests in a suite. - *
      - * - * @author Rupert Smith - */ -public class WrappedSuiteTestDecorator extends TestDecorator -{ - /** Used for logging. */ - private static Logger log = Logger.getLogger(WrappedSuiteTestDecorator.class); - - /** Holds the test suite that this supplies access to. */ - protected Test suite; - - /** - * Creates a wrappred suite test decorator from a test suite. - * - * @param suite The test suite. - */ - public WrappedSuiteTestDecorator(TestSuite suite) - { - super(suite); - this.suite = suite; - } - - /** - * Creates a wrapped suite test decorator from another one. - * - * @param suite The test suite. - */ - public WrappedSuiteTestDecorator(WrappedSuiteTestDecorator suite) - { - super(suite); - this.suite = suite; - } - - /** - * Returns the test count of the wrapped suite. - * - * @return The test count of the wrapped suite. - */ - public int countTestCases() - { - return suite.countTestCases(); - } - - /** - * Gets the ith test from the test suite. - * - * @param i The index of the test within the suite to get. - * - * @return The test with the specified index. - */ - public Test testAt(int i) - { - log.debug("public Test testAt(int i = " + i + "): called"); - - if (suite instanceof WrappedSuiteTestDecorator) - { - return ((WrappedSuiteTestDecorator) suite).testAt(i); - } - else if (suite instanceof TestSuite) - { - return ((TestSuite) suite).testAt(i); - } - - // This should never happen. - return null; - } - - /** - * Gets all the tests from the underlying test suite. - * - * @return All the tests from the underlying test suite. - */ - public Collection getAllUnderlyingTests() - { - log.debug("public Collection getAllUnderlyingTests(): called"); - - List tests = new ArrayList(); - - int numTests = countTestCases(); - log.debug("numTests = " + numTests); - - for (int i = 0; i < numTests; i++) - { - tests.add(testAt(i)); - } - - return tests; - } -} +/* + * + * 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.junit.extensions; + +import junit.extensions.TestDecorator; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * WrappedSuiteTestDecorator is a test decorator that wraps a test suite, or another wrapped suite, but provides the + * same functionality for the {@link junit.extensions.TestDecorator#countTestCases()} and {@link TestSuite#testAt(int)} + * methods as the underlying suite. It returns the values that these methods provide, to enable classes using decorated + * tests to drill down to the underlying tests in the suite. That is to say that it indexes and reports the number of + * distinct tests in the suite, not the number of test runs that would result from, for example, wrapping the suite in a + * repeating decorator. + * + *

      + *
      CRC Card
      Responsibilities + *
      Provide access to the underlying tests in a suite. + *
      + * + * @author Rupert Smith + */ +public class WrappedSuiteTestDecorator extends TestDecorator +{ + /** Used for logging. */ + private static Logger log = Logger.getLogger(WrappedSuiteTestDecorator.class); + + /** Holds the test suite that this supplies access to. */ + protected Test suite; + + /** + * Creates a wrappred suite test decorator from a test suite. + * + * @param suite The test suite. + */ + public WrappedSuiteTestDecorator(TestSuite suite) + { + super(suite); + this.suite = suite; + } + + /** + * Creates a wrapped suite test decorator from another one. + * + * @param suite The test suite. + */ + public WrappedSuiteTestDecorator(WrappedSuiteTestDecorator suite) + { + super(suite); + this.suite = suite; + } + + /** + * Returns the test count of the wrapped suite. + * + * @return The test count of the wrapped suite. + */ + public int countTestCases() + { + return suite.countTestCases(); + } + + /** + * Gets the ith test from the test suite. + * + * @param i The index of the test within the suite to get. + * + * @return The test with the specified index. + */ + public Test testAt(int i) + { + log.debug("public Test testAt(int i = " + i + "): called"); + + if (suite instanceof WrappedSuiteTestDecorator) + { + return ((WrappedSuiteTestDecorator) suite).testAt(i); + } + else if (suite instanceof TestSuite) + { + return ((TestSuite) suite).testAt(i); + } + + // This should never happen. + return null; + } + + /** + * Gets all the tests from the underlying test suite. + * + * @return All the tests from the underlying test suite. + */ + public Collection getAllUnderlyingTests() + { + log.debug("public Collection getAllUnderlyingTests(): called"); + + List tests = new ArrayList(); + + int numTests = countTestCases(); + log.debug("numTests = " + numTests); + + for (int i = 0; i < numTests; i++) + { + tests.add(testAt(i)); + } + + return tests; + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java index a771e08cf7..f93212e0c5 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java @@ -1,532 +1,532 @@ -/* - * - * 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.junit.extensions.listeners; - -import junit.framework.AssertionFailedError; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestListener; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.ShutdownHookable; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import java.io.IOException; -import java.io.Writer; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.TreeSet; - -/** - * CSVTestListener is both a test listener, a timings listener, a memory listener and a parameter listener. It listens for test completion events and - * then writes out all the data that it has listened to into a '.csv' (comma seperated values) file. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Listen to test events; start, end, fail, error. - *
      Listen to test timings. - *
      Listen to test memory usage. - *
      Listen to parameterized test parameters. - *
      Output all test data to a CSV file. - *
      - * - * @author Rupert Smith - * - * @todo Write an XML output class. Write a transform to convert it into an HTML page with timings as graphs. - */ -public class CSVTestListener implements TestListener, TKTestListener, ShutdownHookable -{ - /** Used for logging. */ - private static final Logger log = Logger.getLogger(CSVTestListener.class); - - /** The timings file writer. */ - private Writer timingsWriter; - - /** - * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an - * explicit thread id must be used, where notifications come from different threads than the ones that called - * the test method. - */ - Map threadLocalResults = Collections.synchronizedMap(new HashMap()); - - /** Used to record the start time of a complete test run, for outputing statistics at the end of the test run. */ - private long batchStartTime; - - /** Used to record the number of errors accross a complete test run. */ - private int numError; - - /** Used to record the number of failures accross a complete test run. */ - private int numFailed; - - /** Used to record the number of passes accross a complete test run. */ - private int numPassed; - - /** Used to record the total tests run accross a complete test run. Always equal to passes + errors + fails. */ - private int totalTests; - - /** Used to recrod the current concurrency level for the test batch. */ - private int concurrencyLevel; - - /** - * Used to record the total 'size' of the tests run, this is the number run times the average value of the test - * size parameters. - */ - private int totalSize; - - /** - * Used to record the summation of all of the individual test timgings. Note that total time and summed time - * are unlikely to be in agreement, exception for a single threaded test (with no setup time). Total time is - * the time taken to run all the tests, summed time is the added up time that each individual test took. So if - * two tests run in parallel and take one second each, total time will be one seconds, summed time will be two - * seconds. - */ - private long summedTime; - - /** Flag to indicate when batch has been started but not ended to ensure end batch stats are output only once. */ - private boolean batchStarted = false; - - /** - * Creates a new CSVTestListener object. - * - * @param writer A writer where this CSV listener should write out its output to. - */ - public CSVTestListener(Writer writer) - { - // log.debug("public CSVTestListener(Writer writer): called"); - - // Keep the writer. - this.timingsWriter = writer; - } - - /** - * Resets the test results to the default state of time zero, memory usage zero, test passed. - * - * @param test The test to resest any results for. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void reset(Test test, Long threadId) - { - // log.debug("public void reset(Test test = \"" + test + "\", Long threadId = " + threadId + "): called"); - - TestResult r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.testTime = 0L; - r.testStartMem = 0L; - r.testEndMem = 0L; - r.testState = "Pass"; - r.testParam = 0; - } - - /** - * Called when a test results in an error. - * - * @param test The test which is in error. - * @param t Any Throwable raised by the test in error. - */ - public void addError(Test test, Throwable t) - { - // log.debug("public void addError(Test test, Throwable t): called"); - - TestResult r = threadLocalResults.get(Thread.currentThread().getId()); - r.testState = "Error"; - } - - /** - * Called when a test results in a failure. - * - * @param test The test which failed. - * @param t The AssertionFailedError that encapsulates the test failure. - */ - public void addFailure(Test test, AssertionFailedError t) - { - // log.debug("public void addFailure(Test \"" + test + "\", AssertionFailedError t): called"); - - TestResult r = threadLocalResults.get(Thread.currentThread().getId()); - r.testState = "Failure"; - } - - /** - * Called when a test completes to mark it as a test fail. This method should be used when registering a - * failure from a different thread than the one that started the test. - * - * @param test The test which failed. - * @param e The assertion that failed the test. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void addFailure(Test test, AssertionFailedError e, Long threadId) - { - // log.debug("public void addFailure(Test test = \"" + test + "\", AssertionFailedError e, Long threadId = " + threadId - // + "): called"); - - TestResult r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.testState = "Failure"; - } - - /** - * Called when a test completes. Success, failure and errors. - * - * @param test The test which completed. - */ - public void endTest(Test test) - { - // log.debug("public void endTest(Test \"" + test + "\"): called"); - - TestResult r = threadLocalResults.get(Thread.currentThread().getId()); - - writeTestResults(r, test); - - // Clear all the test results for the thread. - threadLocalResults.remove(Thread.currentThread().getId()); - } - - /** - * Called when a test starts. - * - * @param test The test wich has started. - */ - public void startTest(Test test) - { - // log.debug("public void startTest(Test \"" + test + "\"): called"); - - // Initialize the thread local test results. - threadLocalResults.put(Thread.currentThread().getId(), new TestResult()); - } - - /** - * Should be called every time a test completes with the run time of that test. - * - * @param test The name of the test. - * @param nanos The run time of the test in nanoseconds. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void timing(Test test, long nanos, Long threadId) - { - // log.debug("public void timing(String \"" + test + "\", long " + nanos + "): called"); - - TestResult r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.testTime = nanos; - summedTime += nanos; - } - - /** - * Should be called every time a test completed with the amount of memory used before and after the test was run. - * - * @param test The test which memory was measured for. - * @param memStart The total JVM memory used before the test was run. - * @param memEnd The total JVM memory used after the test was run. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) - { - // log.debug("public void memoryUsed(Test \"" + test + "\", long " + memStart + ", long " + memEnd + ", Long " - // + threadId + "): called"); - - TestResult r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.testStartMem = memStart; - r.testEndMem = memEnd; - } - - /** - * Should be called every time a parameterized test completed with the int value of its test parameter. - * - * @param test The test which memory was measured for. - * @param parameter The int parameter value. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void parameterValue(Test test, int parameter, Long threadId) - { - // log.debug("public void parameterValue(Test test = \"" + test + "\", int parameter = " + parameter + "): called"); - - TestResult r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.testParam = parameter; - totalSize += parameter; - } - - /** - * Should be called every time a test completes with the current number of test threads running. This should not - * change within a test batch, therefore it is safe to take this as a batch level property value too. - * - * @param test The test for which the measurement is being generated. - * @param threads The number of tests being run concurrently. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void concurrencyLevel(Test test, int threads, Long threadId) - { - // log.debug("public void concurrencyLevel(Test test = \"" + test + "\", int threads = " + threads + "): called"); - - TestResult r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.testConcurrency = threads; - concurrencyLevel = threads; - - } - - /** - * Called when a test completes. Success, failure and errors. This method should be used when registering an - * end test from a different thread than the one that started the test. - * - * @param test The test which completed. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void endTest(Test test, Long threadId) - { - // log.debug("public void endTest(Test test = \"" + test + "\", Long threadId " + threadId + "): called"); - - TestResult r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - writeTestResults(r, test); - } - - /** - * Takes a time stamp for the beginning of the batch and resets stats counted for the batch. - */ - public synchronized void startBatch() - { - numError = 0; - numFailed = 0; - numPassed = 0; - totalTests = 0; - totalSize = 0; - batchStartTime = System.nanoTime(); - summedTime = 0; - batchStarted = true; - - // Write out the column headers for the batch. - writeColumnHeaders(); - } - - /** - * Takes a time stamp for the end of the batch to calculate the total run time. - * Write this and other stats out to the tail of the csv file. - * - * @param parameters The optional test parameters, may be null. - */ - public synchronized void endBatch(Properties parameters) - { - boolean noParams = (parameters == null) || (parameters.size() == 0); - - // Check that a batch has been started but not ended. - if (batchStarted) - { - long batchEndTime = System.nanoTime(); - float totalTimeMillis = ((float) (batchEndTime - batchStartTime)) / 1000000f; - float summedTimeMillis = ((float) summedTime) / 1000000f; - - // Write the stats for the batch out. - try - { - synchronized (this.getClass()) - { - timingsWriter.write("Total Tests:, " + totalTests + ", "); - timingsWriter.write("Total Passed:, " + numPassed + ", "); - timingsWriter.write("Total Failed:, " + numFailed + ", "); - timingsWriter.write("Total Error:, " + numError + ", "); - timingsWriter.write("Total Size:, " + totalSize + ", "); - timingsWriter.write("Summed Time:, " + summedTimeMillis + ", "); - timingsWriter.write("Concurrency Level:, " + concurrencyLevel + ", "); - timingsWriter.write("Total Time:, " + totalTimeMillis + ", "); - timingsWriter.write("Test Throughput:, " + (((float) totalTests) / totalTimeMillis) + ", "); - timingsWriter.write("Test * Size Throughput:, " + (((float) totalSize) / totalTimeMillis) - + (noParams ? "\n\n" : ", ")); - - // Write out the test parameters if there are any specified. - if (!noParams) - { - properties(parameters); - } - - timingsWriter.flush(); - } - } - catch (IOException e) - { - throw new RuntimeException("Unable to write out end batch statistics: " + e, e); - } - } - - // Reset the batch started flag to ensure stats are only output once. - batchStarted = false; - } - - /** - * Notifies listeners of the tests read/set properties. - * - * @param properties The tests read/set properties. - */ - public void properties(Properties properties) - { - // log.debug("public void properties(Properties properties): called"); - - // Write the properties out to the results file. - try - { - synchronized (this.getClass()) - { - Set keySet = new TreeSet(properties.keySet()); - - // timingsWriter.write("\n"); - - for (Object key : keySet) - { - timingsWriter.write(key + " = , " + properties.getProperty((String) key) + ", "); - } - - timingsWriter.write("\n\n"); - // timingsWriter.flush(); - } - } - catch (IOException e) - { - throw new RuntimeException("Unable to write out test parameters: " + e, e); - } - - // Write out the column headers after the properties. - // writeColumnHeaders(); - } - - /** - * Writes out and flushes the column headers for raw test data. - */ - private void writeColumnHeaders() - { - // Write the column headers for the CSV file. Any IO exceptions are ignored. - try - { - timingsWriter.write("Class, "); - timingsWriter.write("Method, "); - timingsWriter.write("Thread, "); - timingsWriter.write("Test Outcome, "); - timingsWriter.write("Time (milliseconds), "); - timingsWriter.write("Memory Used (bytes), "); - timingsWriter.write("Concurrency level, "); - timingsWriter.write("Test Size\n"); - - timingsWriter.flush(); - } - catch (IOException e) - { - throw new RuntimeException("Unable to write out column headers: " + e, e); - } - } - - /** - * Writes out the test results for the specified test. This outputs a single line of results to the csv file. - * - * @param r The test results to write out. - * @param test The test to write them out for. - */ - private void writeTestResults(TestResult r, Test test) - { - // Update the running stats for this batch. - if ("Error".equals(r.testState)) - { - numError++; - } - else if ("Failure".equals(r.testState)) - { - numFailed++; - } - else if ("Pass".equals(r.testState)) - { - numPassed++; - } - - totalTests++; - - // Write the test name and thread information plus all instrumenation a line of the CSV ouput. Any IO - // exceptions are ignored. - try - { - synchronized (this.getClass()) - { - timingsWriter.write(test.getClass().getName() + ", "); - timingsWriter.write(((test instanceof TestCase) ? ((TestCase) test).getName() : "") + ", "); - timingsWriter.write(Thread.currentThread().getName() + ", "); - timingsWriter.write(r.testState + ", "); - timingsWriter.write((((float) r.testTime) / 1000000f) + ", "); - timingsWriter.write((r.testEndMem - r.testStartMem) + ", "); - timingsWriter.write(r.testConcurrency + ", "); - timingsWriter.write(r.testParam + "\n"); - } - } - catch (IOException e) - { - throw new RuntimeException("Unable to write out test results: " + e, e); - } - } - - /** - * Supplies the shutdown hook. This attempts to flush the results in the event of the test runner being prematurely - * suspended before the end of the current test batch. - * - * @return The shut down hook. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - log.debug("CSVTestListener::ShutdownHook: called"); - - // Complete the current test batch stats. - endBatch(TestContextProperties.getInstance()); - } - }); - } - - /** Captures test results packaged into a single object, so that it can be set up as a thread local. */ - private static class TestResult - { - /** Used to hold the test timing. */ - public long testTime; - - /** Used to hold the test start memory usage. */ - public long testStartMem; - - /** Used to hold the test end memory usage. */ - public long testEndMem; - - /** Used to hold the test pass/fail/error state. */ - public String testState = "Pass"; - - /** Used to hold the test parameter value. */ - public int testParam; - - /** Used to hold the concurrency level under which the test was run. */ - public int testConcurrency; - } -} +/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestListener; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.ShutdownHookable; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import java.io.IOException; +import java.io.Writer; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; + +/** + * CSVTestListener is both a test listener, a timings listener, a memory listener and a parameter listener. It listens for test completion events and + * then writes out all the data that it has listened to into a '.csv' (comma seperated values) file. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Listen to test events; start, end, fail, error. + *
      Listen to test timings. + *
      Listen to test memory usage. + *
      Listen to parameterized test parameters. + *
      Output all test data to a CSV file. + *
      + * + * @author Rupert Smith + * + * @todo Write an XML output class. Write a transform to convert it into an HTML page with timings as graphs. + */ +public class CSVTestListener implements TestListener, TKTestListener, ShutdownHookable +{ + /** Used for logging. */ + private static final Logger log = Logger.getLogger(CSVTestListener.class); + + /** The timings file writer. */ + private Writer timingsWriter; + + /** + * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an + * explicit thread id must be used, where notifications come from different threads than the ones that called + * the test method. + */ + Map threadLocalResults = Collections.synchronizedMap(new HashMap()); + + /** Used to record the start time of a complete test run, for outputing statistics at the end of the test run. */ + private long batchStartTime; + + /** Used to record the number of errors accross a complete test run. */ + private int numError; + + /** Used to record the number of failures accross a complete test run. */ + private int numFailed; + + /** Used to record the number of passes accross a complete test run. */ + private int numPassed; + + /** Used to record the total tests run accross a complete test run. Always equal to passes + errors + fails. */ + private int totalTests; + + /** Used to recrod the current concurrency level for the test batch. */ + private int concurrencyLevel; + + /** + * Used to record the total 'size' of the tests run, this is the number run times the average value of the test + * size parameters. + */ + private int totalSize; + + /** + * Used to record the summation of all of the individual test timgings. Note that total time and summed time + * are unlikely to be in agreement, exception for a single threaded test (with no setup time). Total time is + * the time taken to run all the tests, summed time is the added up time that each individual test took. So if + * two tests run in parallel and take one second each, total time will be one seconds, summed time will be two + * seconds. + */ + private long summedTime; + + /** Flag to indicate when batch has been started but not ended to ensure end batch stats are output only once. */ + private boolean batchStarted = false; + + /** + * Creates a new CSVTestListener object. + * + * @param writer A writer where this CSV listener should write out its output to. + */ + public CSVTestListener(Writer writer) + { + // log.debug("public CSVTestListener(Writer writer): called"); + + // Keep the writer. + this.timingsWriter = writer; + } + + /** + * Resets the test results to the default state of time zero, memory usage zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { + // log.debug("public void reset(Test test = \"" + test + "\", Long threadId = " + threadId + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testTime = 0L; + r.testStartMem = 0L; + r.testEndMem = 0L; + r.testState = "Pass"; + r.testParam = 0; + } + + /** + * Called when a test results in an error. + * + * @param test The test which is in error. + * @param t Any Throwable raised by the test in error. + */ + public void addError(Test test, Throwable t) + { + // log.debug("public void addError(Test test, Throwable t): called"); + + TestResult r = threadLocalResults.get(Thread.currentThread().getId()); + r.testState = "Error"; + } + + /** + * Called when a test results in a failure. + * + * @param test The test which failed. + * @param t The AssertionFailedError that encapsulates the test failure. + */ + public void addFailure(Test test, AssertionFailedError t) + { + // log.debug("public void addFailure(Test \"" + test + "\", AssertionFailedError t): called"); + + TestResult r = threadLocalResults.get(Thread.currentThread().getId()); + r.testState = "Failure"; + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + // log.debug("public void addFailure(Test test = \"" + test + "\", AssertionFailedError e, Long threadId = " + threadId + // + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testState = "Failure"; + } + + /** + * Called when a test completes. Success, failure and errors. + * + * @param test The test which completed. + */ + public void endTest(Test test) + { + // log.debug("public void endTest(Test \"" + test + "\"): called"); + + TestResult r = threadLocalResults.get(Thread.currentThread().getId()); + + writeTestResults(r, test); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * Called when a test starts. + * + * @param test The test wich has started. + */ + public void startTest(Test test) + { + // log.debug("public void startTest(Test \"" + test + "\"): called"); + + // Initialize the thread local test results. + threadLocalResults.put(Thread.currentThread().getId(), new TestResult()); + } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { + // log.debug("public void timing(String \"" + test + "\", long " + nanos + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testTime = nanos; + summedTime += nanos; + } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { + // log.debug("public void memoryUsed(Test \"" + test + "\", long " + memStart + ", long " + memEnd + ", Long " + // + threadId + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testStartMem = memStart; + r.testEndMem = memEnd; + } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { + // log.debug("public void parameterValue(Test test = \"" + test + "\", int parameter = " + parameter + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testParam = parameter; + totalSize += parameter; + } + + /** + * Should be called every time a test completes with the current number of test threads running. This should not + * change within a test batch, therefore it is safe to take this as a batch level property value too. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { + // log.debug("public void concurrencyLevel(Test test = \"" + test + "\", int threads = " + threads + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.testConcurrency = threads; + concurrencyLevel = threads; + + } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + // log.debug("public void endTest(Test test = \"" + test + "\", Long threadId " + threadId + "): called"); + + TestResult r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + writeTestResults(r, test); + } + + /** + * Takes a time stamp for the beginning of the batch and resets stats counted for the batch. + */ + public synchronized void startBatch() + { + numError = 0; + numFailed = 0; + numPassed = 0; + totalTests = 0; + totalSize = 0; + batchStartTime = System.nanoTime(); + summedTime = 0; + batchStarted = true; + + // Write out the column headers for the batch. + writeColumnHeaders(); + } + + /** + * Takes a time stamp for the end of the batch to calculate the total run time. + * Write this and other stats out to the tail of the csv file. + * + * @param parameters The optional test parameters, may be null. + */ + public synchronized void endBatch(Properties parameters) + { + boolean noParams = (parameters == null) || (parameters.size() == 0); + + // Check that a batch has been started but not ended. + if (batchStarted) + { + long batchEndTime = System.nanoTime(); + float totalTimeMillis = ((float) (batchEndTime - batchStartTime)) / 1000000f; + float summedTimeMillis = ((float) summedTime) / 1000000f; + + // Write the stats for the batch out. + try + { + synchronized (this.getClass()) + { + timingsWriter.write("Total Tests:, " + totalTests + ", "); + timingsWriter.write("Total Passed:, " + numPassed + ", "); + timingsWriter.write("Total Failed:, " + numFailed + ", "); + timingsWriter.write("Total Error:, " + numError + ", "); + timingsWriter.write("Total Size:, " + totalSize + ", "); + timingsWriter.write("Summed Time:, " + summedTimeMillis + ", "); + timingsWriter.write("Concurrency Level:, " + concurrencyLevel + ", "); + timingsWriter.write("Total Time:, " + totalTimeMillis + ", "); + timingsWriter.write("Test Throughput:, " + (((float) totalTests) / totalTimeMillis) + ", "); + timingsWriter.write("Test * Size Throughput:, " + (((float) totalSize) / totalTimeMillis) + + (noParams ? "\n\n" : ", ")); + + // Write out the test parameters if there are any specified. + if (!noParams) + { + properties(parameters); + } + + timingsWriter.flush(); + } + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out end batch statistics: " + e, e); + } + } + + // Reset the batch started flag to ensure stats are only output once. + batchStarted = false; + } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { + // log.debug("public void properties(Properties properties): called"); + + // Write the properties out to the results file. + try + { + synchronized (this.getClass()) + { + Set keySet = new TreeSet(properties.keySet()); + + // timingsWriter.write("\n"); + + for (Object key : keySet) + { + timingsWriter.write(key + " = , " + properties.getProperty((String) key) + ", "); + } + + timingsWriter.write("\n\n"); + // timingsWriter.flush(); + } + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out test parameters: " + e, e); + } + + // Write out the column headers after the properties. + // writeColumnHeaders(); + } + + /** + * Writes out and flushes the column headers for raw test data. + */ + private void writeColumnHeaders() + { + // Write the column headers for the CSV file. Any IO exceptions are ignored. + try + { + timingsWriter.write("Class, "); + timingsWriter.write("Method, "); + timingsWriter.write("Thread, "); + timingsWriter.write("Test Outcome, "); + timingsWriter.write("Time (milliseconds), "); + timingsWriter.write("Memory Used (bytes), "); + timingsWriter.write("Concurrency level, "); + timingsWriter.write("Test Size\n"); + + timingsWriter.flush(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out column headers: " + e, e); + } + } + + /** + * Writes out the test results for the specified test. This outputs a single line of results to the csv file. + * + * @param r The test results to write out. + * @param test The test to write them out for. + */ + private void writeTestResults(TestResult r, Test test) + { + // Update the running stats for this batch. + if ("Error".equals(r.testState)) + { + numError++; + } + else if ("Failure".equals(r.testState)) + { + numFailed++; + } + else if ("Pass".equals(r.testState)) + { + numPassed++; + } + + totalTests++; + + // Write the test name and thread information plus all instrumenation a line of the CSV ouput. Any IO + // exceptions are ignored. + try + { + synchronized (this.getClass()) + { + timingsWriter.write(test.getClass().getName() + ", "); + timingsWriter.write(((test instanceof TestCase) ? ((TestCase) test).getName() : "") + ", "); + timingsWriter.write(Thread.currentThread().getName() + ", "); + timingsWriter.write(r.testState + ", "); + timingsWriter.write((((float) r.testTime) / 1000000f) + ", "); + timingsWriter.write((r.testEndMem - r.testStartMem) + ", "); + timingsWriter.write(r.testConcurrency + ", "); + timingsWriter.write(r.testParam + "\n"); + } + } + catch (IOException e) + { + throw new RuntimeException("Unable to write out test results: " + e, e); + } + } + + /** + * Supplies the shutdown hook. This attempts to flush the results in the event of the test runner being prematurely + * suspended before the end of the current test batch. + * + * @return The shut down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + log.debug("CSVTestListener::ShutdownHook: called"); + + // Complete the current test batch stats. + endBatch(TestContextProperties.getInstance()); + } + }); + } + + /** Captures test results packaged into a single object, so that it can be set up as a thread local. */ + private static class TestResult + { + /** Used to hold the test timing. */ + public long testTime; + + /** Used to hold the test start memory usage. */ + public long testStartMem; + + /** Used to hold the test end memory usage. */ + public long testEndMem; + + /** Used to hold the test pass/fail/error state. */ + public String testState = "Pass"; + + /** Used to hold the test parameter value. */ + public int testParam; + + /** Used to hold the concurrency level under which the test was run. */ + public int testConcurrency; + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java index 5c328a8814..2955fba2bd 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java @@ -1,264 +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.junit.extensions.listeners; - -import junit.framework.AssertionFailedError; -import junit.framework.Test; -import junit.framework.TestListener; - -import org.apache.qpid.junit.extensions.SleepThrottle; -import org.apache.qpid.junit.extensions.Throttle; - -import java.util.Properties; - -/** - * ConsoleTestListener provides feedback to the console, as test timings are taken, by drawing a '.', or an 'E', or an - * 'F', for each test that passes, is in error or fails. It does this for every test result registered with the framework, - * not just on the completion of each test method as the JUnit one does. It also uses a throttle to cap the rate of - * dot drawing, as exessively high rates can degrade test performance without providing much usefull feedback to the user. - * Unlike the JUnit dot drawing feedback, this one will correctly wrap lines when tests are run concurrently (the - * rate capping ensures that this does not become a hot-spot for thread contention). - * - *

      Where rate capping causes the conflation of multiple requested dots into a single dot, the dot that is actually - * drawn will be the worst result within the conflation period, that is, error is worse than fail which is worse than pass. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Draw dots as each test result completes, at a capped rate. - *
      - * - * @author Rupert Smith - */ -public class ConsoleTestListener implements TestListener, TKTestListener -{ - /** Used to indicate a test pass. */ - private static final int PASS = 1; - - /** Used to indicate a test failure. */ - private static final int FAIL = 2; - - /** Used to indicate a test error. */ - private static final int ERROR = 3; - - /** Defines the maximum number of columns of dots to print. */ - private static final int MAX_COLUMNS = 80; - - /** Used to throttle the dot writing rate. */ - Throttle throttle; - - /** Tracks the worst test result so far, when the throttled print method must conflate results due to throttling. */ - private int conflatedResult = 0; - - /** Tracks the column count as dots are printed, so that newlines can be inserted at the right margin. */ - private int columnCount = 0; - - /** Used as a monitor on the print method criticial section, to ensure that line ends always happen in the right place. */ - private final Object printMonitor = new Object(); - - /** - * Creates a dot drawing feedback test listener, set by default to 80 columns at 80 dots per second capped rate. - */ - public ConsoleTestListener() - { - throttle = new SleepThrottle(); - throttle.setRate(80f); - } - - /** - * Prints dots at a capped rate, conflating the requested type of dot to draw if this method is called at a rate - * higher than the capped rate. The conflation works by always printing the worst result that occurs within the - * conflation period, that is, error is worse than fail which is worse than a pass. - * - * @param result The type of dot to draw, {@link #PASS}, {@link #FAIL} or {@link #ERROR}. - */ - private void throttledPrint(int result) - { - conflatedResult = (result > conflatedResult) ? result : conflatedResult; - - if (throttle.checkThrottle()) - { - synchronized (printMonitor) - { - switch (conflatedResult) - { - default: - case PASS: - System.out.print('.'); - break; - - case FAIL: - System.out.print('F'); - break; - - case ERROR: - System.out.print('E'); - break; - } - - columnCount = (columnCount >= MAX_COLUMNS) ? 0 : (columnCount + 1); - - if (columnCount == 0) - { - System.out.print('\n'); - } - - conflatedResult = 0; - } - } - } - - /** - * An error occurred. - * - * @param test The test in error. Ignored. - * @param t The error that the test threw. Ignored. - */ - public void addError(Test test, Throwable t) - { - throttledPrint(ERROR); - } - - /** - * A failure occurred. - * - * @param test The test that failed. Ignored. - * @param t The assertion failure that the test threw. Ignored. - */ - public void addFailure(Test test, AssertionFailedError t) - { - throttledPrint(FAIL); - } - - /** - * A test ended. - * - * @param test The test that ended. Ignored. - */ - public void endTest(Test test) - { - throttledPrint(PASS); - } - - /** - * A test started. - * - * @param test The test that started. Ignored. - */ - public void startTest(Test test) - { } - - /** - * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. - * - * @param test The test to resest any results for. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void reset(Test test, Long threadId) - { } - - /** - * Should be called every time a test completes with the run time of that test. - * - * @param test The name of the test. - * @param nanos The run time of the test in nanoseconds. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void timing(Test test, long nanos, Long threadId) - { } - - /** - * Should be called every time a test completed with the amount of memory used before and after the test was run. - * - * @param test The test which memory was measured for. - * @param memStart The total JVM memory used before the test was run. - * @param memEnd The total JVM memory used after the test was run. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) - { } - - /** - * Should be called every time a parameterized test completed with the int value of its test parameter. - * - * @param test The test which memory was measured for. - * @param parameter The int parameter value. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void parameterValue(Test test, int parameter, Long threadId) - { } - - /** - * Should be called every time a test completes with the current number of test threads running. - * - * @param test The test for which the measurement is being generated. - * @param threads The number of tests being run concurrently. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void concurrencyLevel(Test test, int threads, Long threadId) - { } - - /** - * Called when a test completes. Success, failure and errors. This method should be used when registering an - * end test from a different thread than the one that started the test. - * - * @param test The test which completed. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void endTest(Test test, Long threadId) - { - throttledPrint(PASS); - } - - /** - * Called when a test completes to mark it as a test fail. This method should be used when registering a - * failure from a different thread than the one that started the test. - * - * @param test The test which failed. - * @param e The assertion that failed the test. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void addFailure(Test test, AssertionFailedError e, Long threadId) - { - throttledPrint(FAIL); - } - - /** - * Notifies listeners of the start of a complete run of tests. - */ - public void startBatch() - { } - - /** - * Notifies listeners of the end of a complete run of tests. - * - * @param parameters The optional test parameters to log out with the batch results. - */ - public void endBatch(Properties parameters) - { } - - /** - * Notifies listeners of the tests read/set properties. - * - * @param properties The tests read/set properties. - */ - public void properties(Properties properties) - { } -} +/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestListener; + +import org.apache.qpid.junit.extensions.SleepThrottle; +import org.apache.qpid.junit.extensions.Throttle; + +import java.util.Properties; + +/** + * ConsoleTestListener provides feedback to the console, as test timings are taken, by drawing a '.', or an 'E', or an + * 'F', for each test that passes, is in error or fails. It does this for every test result registered with the framework, + * not just on the completion of each test method as the JUnit one does. It also uses a throttle to cap the rate of + * dot drawing, as exessively high rates can degrade test performance without providing much usefull feedback to the user. + * Unlike the JUnit dot drawing feedback, this one will correctly wrap lines when tests are run concurrently (the + * rate capping ensures that this does not become a hot-spot for thread contention). + * + *

      Where rate capping causes the conflation of multiple requested dots into a single dot, the dot that is actually + * drawn will be the worst result within the conflation period, that is, error is worse than fail which is worse than pass. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Draw dots as each test result completes, at a capped rate. + *
      + * + * @author Rupert Smith + */ +public class ConsoleTestListener implements TestListener, TKTestListener +{ + /** Used to indicate a test pass. */ + private static final int PASS = 1; + + /** Used to indicate a test failure. */ + private static final int FAIL = 2; + + /** Used to indicate a test error. */ + private static final int ERROR = 3; + + /** Defines the maximum number of columns of dots to print. */ + private static final int MAX_COLUMNS = 80; + + /** Used to throttle the dot writing rate. */ + Throttle throttle; + + /** Tracks the worst test result so far, when the throttled print method must conflate results due to throttling. */ + private int conflatedResult = 0; + + /** Tracks the column count as dots are printed, so that newlines can be inserted at the right margin. */ + private int columnCount = 0; + + /** Used as a monitor on the print method criticial section, to ensure that line ends always happen in the right place. */ + private final Object printMonitor = new Object(); + + /** + * Creates a dot drawing feedback test listener, set by default to 80 columns at 80 dots per second capped rate. + */ + public ConsoleTestListener() + { + throttle = new SleepThrottle(); + throttle.setRate(80f); + } + + /** + * Prints dots at a capped rate, conflating the requested type of dot to draw if this method is called at a rate + * higher than the capped rate. The conflation works by always printing the worst result that occurs within the + * conflation period, that is, error is worse than fail which is worse than a pass. + * + * @param result The type of dot to draw, {@link #PASS}, {@link #FAIL} or {@link #ERROR}. + */ + private void throttledPrint(int result) + { + conflatedResult = (result > conflatedResult) ? result : conflatedResult; + + if (throttle.checkThrottle()) + { + synchronized (printMonitor) + { + switch (conflatedResult) + { + default: + case PASS: + System.out.print('.'); + break; + + case FAIL: + System.out.print('F'); + break; + + case ERROR: + System.out.print('E'); + break; + } + + columnCount = (columnCount >= MAX_COLUMNS) ? 0 : (columnCount + 1); + + if (columnCount == 0) + { + System.out.print('\n'); + } + + conflatedResult = 0; + } + } + } + + /** + * An error occurred. + * + * @param test The test in error. Ignored. + * @param t The error that the test threw. Ignored. + */ + public void addError(Test test, Throwable t) + { + throttledPrint(ERROR); + } + + /** + * A failure occurred. + * + * @param test The test that failed. Ignored. + * @param t The assertion failure that the test threw. Ignored. + */ + public void addFailure(Test test, AssertionFailedError t) + { + throttledPrint(FAIL); + } + + /** + * A test ended. + * + * @param test The test that ended. Ignored. + */ + public void endTest(Test test) + { + throttledPrint(PASS); + } + + /** + * A test started. + * + * @param test The test that started. Ignored. + */ + public void startTest(Test test) + { } + + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { } + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + throttledPrint(PASS); + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + throttledPrint(FAIL); + } + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch() + { } + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters) + { } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java index 4f08e8bf2d..11fc6a7451 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java @@ -1,132 +1,132 @@ -/* - * - * 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.junit.extensions.listeners; - -import junit.framework.AssertionFailedError; -import junit.framework.Test; -import junit.framework.TestListener; - -import java.util.Properties; - -/** - * TKTestListener is a listener interface for listeners that want to be informed of the run times of tests, the memory - * usage of tests, the 'size' parameters of parameterized tests and the begin and end events of complete test runs. - * {@link org.apache.qpid.junit.extensions.TKTestResult} is an example of a test result class that listeners - * interested in these events can be attached to. - * - * The {@link #timing(junit.framework.Test, long, Long)}, {@link #memoryUsed(junit.framework.Test, long, long, Long)}, - * {@link #parameterValue(junit.framework.Test, int, Long)} and {@link #endTest(junit.framework.Test, Long)} methods - * all accept on optional thread id parameter. - * - *

      - *
      CRC Card
      Responsibilities - *
      Listen to test timings. - *
      Listen to test memory usages. - *
      Listen to parameterized test parameters. - *
      - * - * @author Rupert Smith - */ -public interface TKTestListener extends TestListener -{ - /** - * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. - * - * @param test The test to resest any results for. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void reset(Test test, Long threadId); - - /** - * Should be called every time a test completes with the run time of that test. - * - * @param test The name of the test. - * @param nanos The run time of the test in nanoseconds. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void timing(Test test, long nanos, Long threadId); - - /** - * Should be called every time a test completed with the amount of memory used before and after the test was run. - * - * @param test The test which memory was measured for. - * @param memStart The total JVM memory used before the test was run. - * @param memEnd The total JVM memory used after the test was run. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void memoryUsed(Test test, long memStart, long memEnd, Long threadId); - - /** - * Should be called every time a parameterized test completed with the int value of its test parameter. - * - * @param test The test which memory was measured for. - * @param parameter The int parameter value. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void parameterValue(Test test, int parameter, Long threadId); - - /** - * Should be called every time a test completes with the current number of test threads running. - * - * @param test The test for which the measurement is being generated. - * @param threads The number of tests being run concurrently. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void concurrencyLevel(Test test, int threads, Long threadId); - - /** - * Called when a test completes. Success, failure and errors. This method should be used when registering an - * end test from a different thread than the one that started the test. - * - * @param test The test which completed. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void endTest(Test test, Long threadId); - - /** - * Called when a test completes to mark it as a test fail. This method should be used when registering a - * failure from a different thread than the one that started the test. - * - * @param test The test which failed. - * @param e The assertion that failed the test. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void addFailure(Test test, AssertionFailedError e, Long threadId); - - /** - * Notifies listeners of the start of a complete run of tests. - */ - public void startBatch(); - - /** - * Notifies listeners of the end of a complete run of tests. - * - * @param parameters The optional test parameters to log out with the batch results. - */ - public void endBatch(Properties parameters); - - /** - * Notifies listeners of the tests read/set properties. - * - * @param properties The tests read/set properties. - */ - public void properties(Properties properties); -} +/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestListener; + +import java.util.Properties; + +/** + * TKTestListener is a listener interface for listeners that want to be informed of the run times of tests, the memory + * usage of tests, the 'size' parameters of parameterized tests and the begin and end events of complete test runs. + * {@link org.apache.qpid.junit.extensions.TKTestResult} is an example of a test result class that listeners + * interested in these events can be attached to. + * + * The {@link #timing(junit.framework.Test, long, Long)}, {@link #memoryUsed(junit.framework.Test, long, long, Long)}, + * {@link #parameterValue(junit.framework.Test, int, Long)} and {@link #endTest(junit.framework.Test, Long)} methods + * all accept on optional thread id parameter. + * + *

      + *
      CRC Card
      Responsibilities + *
      Listen to test timings. + *
      Listen to test memory usages. + *
      Listen to parameterized test parameters. + *
      + * + * @author Rupert Smith + */ +public interface TKTestListener extends TestListener +{ + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId); + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId); + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId); + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId); + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId); + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId); + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId); + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch(); + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters); + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties); +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java index a88837e323..ded07ef5bb 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java @@ -1,400 +1,400 @@ -/* - * - * 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.junit.extensions.listeners; - -import junit.framework.AssertionFailedError; -import junit.framework.Test; -import junit.framework.TestCase; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.ShutdownHookable; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.*; - -/** - * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified - * writer. - * - *

      The API for this listener accepts notifications about different aspects of a tests results through different - * methods, so some assumption needs to be made as to which test result a notification refers to. For example - * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is - * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may - * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used - * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest} - * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur - * between the start and end and will be given with the same thread id as the start and end, so the thread id provides - * a unqiue value to identify a particular test run against. - * - *

      - *
      CRC Card
      Responsibilities Collaborations - *
      Listen to test lifecycle notifications. - *
      Listen to test errors and failures. - *
      Listen to test timings. - *
      Listen to test memory usages. - *
      Listen to parameterized test parameters. - *
      Responsibilities - *
      - * - * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring - * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as - * the ant XML formatter, and a more structured one for outputing results with timings and summaries from - * performance tests. - * - * @author Rupert Smith - */ -public class XMLTestListener implements TKTestListener, ShutdownHookable -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(XMLTestListener.class); - - /** The results file writer. */ - protected Writer writer; - - /** Holds the results for individual tests. */ - // protected Map results = new LinkedHashMap(); - // protected List results = new ArrayList(); - - /** - * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an - * explicit thread id must be used, where notifications come from different threads than the ones that called - * the test method. - */ - Map threadLocalResults = Collections.synchronizedMap(new LinkedHashMap()); - - /** - * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means - * that the thread id is freed for the thread to generate more results. - */ - List results = new ArrayList(); - - /** Holds the overall error count. */ - protected int errors = 0; - - /** Holds the overall failure count. */ - protected int failures = 0; - - /** Holds the overall tests run count. */ - protected int runs = 0; - - /** Holds the name of the class that tests are being run for. */ - String testClassName; - - /** - * Creates a new XML results output listener that writes to the specified location. - * - * @param writer The location to write results to. - * @param testClassName The name of the test class to include in the test results. - */ - public XMLTestListener(Writer writer, String testClassName) - { - log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called"); - - this.writer = writer; - this.testClassName = testClassName; - } - - /** - * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. - * - * @param test The test to resest any results for. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void reset(Test test, Long threadId) - { - log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called"); - - XMLTestListener.Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - - r.error = null; - r.failure = null; - - } - - /** - * Notification that a test started. - * - * @param test The test that started. - */ - public void startTest(Test test) - { - log.debug("public void startTest(Test test = " + test + "): called"); - - Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName()); - - // Initialize the thread local test results. - threadLocalResults.put(Thread.currentThread().getId(), newResult); - runs++; - } - - /** - * Should be called every time a test completes with the run time of that test. - * - * @param test The name of the test. - * @param nanos The run time of the test in nanoseconds. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void timing(Test test, long nanos, Long threadId) - { } - - /** - * Should be called every time a test completed with the amount of memory used before and after the test was run. - * - * @param test The test which memory was measured for. - * @param memStart The total JVM memory used before the test was run. - * @param memEnd The total JVM memory used after the test was run. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) - { } - - /** - * Should be called every time a parameterized test completed with the int value of its test parameter. - * - * @param test The test which memory was measured for. - * @param parameter The int parameter value. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void parameterValue(Test test, int parameter, Long threadId) - { } - - /** - * Should be called every time a test completes with the current number of test threads running. - * - * @param test The test for which the measurement is being generated. - * @param threads The number of tests being run concurrently. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void concurrencyLevel(Test test, int threads, Long threadId) - { } - - /** - * Notifies listeners of the tests read/set properties. - * - * @param properties The tests read/set properties. - */ - public void properties(Properties properties) - { } - - /** - * Notification that a test ended. - * - * @param test The test that ended. - */ - public void endTest(Test test) - { - log.debug("public void endTest(Test test = " + test + "): called"); - - // Move complete test results into the completed tests list. - Result r = threadLocalResults.get(Thread.currentThread().getId()); - results.add(r); - - // Clear all the test results for the thread. - threadLocalResults.remove(Thread.currentThread().getId()); - } - - /** - * Called when a test completes. Success, failure and errors. This method should be used when registering an - * end test from a different thread than the one that started the test. - * - * @param test The test which completed. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void endTest(Test test, Long threadId) - { - log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called"); - - // Move complete test results into the completed tests list. - Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - results.add(r); - - // Clear all the test results for the thread. - threadLocalResults.remove(Thread.currentThread().getId()); - } - - /** - * An error occurred. - * - * @param test The test in which the error occurred. - * @param t The throwable that resulted from the error. - */ - public void addError(Test test, Throwable t) - { - log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called"); - - Result r = threadLocalResults.get(Thread.currentThread().getId()); - r.error = t; - errors++; - } - - /** - * A failure occurred. - * - * @param test The test in which the failure occurred. - * @param t The JUnit assertions that led to the failure. - */ - public void addFailure(Test test, AssertionFailedError t) - { - log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called"); - - Result r = threadLocalResults.get(Thread.currentThread().getId()); - r.failure = t; - failures++; - } - - /** - * Called when a test completes to mark it as a test fail. This method should be used when registering a - * failure from a different thread than the one that started the test. - * - * @param test The test which failed. - * @param e The assertion that failed the test. - * @param threadId Optional thread id if not calling from thread that started the test method. May be null. - */ - public void addFailure(Test test, AssertionFailedError e, Long threadId) - { - log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called"); - - Result r = - (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); - r.failure = e; - failures++; - } - - /** - * Notifies listeners of the start of a complete run of tests. - */ - public void startBatch() - { - log.debug("public void startBatch(): called"); - - // Reset all results counts. - threadLocalResults = Collections.synchronizedMap(new HashMap()); - errors = 0; - failures = 0; - runs = 0; - - // Write out the file header. - try - { - writer.write("\n"); - } - catch (IOException e) - { - throw new RuntimeException("Unable to write the test results.", e); - } - } - - /** - * Notifies listeners of the end of a complete run of tests. - * - * @param parameters The optional test parameters to log out with the batch results. - */ - public void endBatch(Properties parameters) - { - log.debug("public void endBatch(Properties parameters = " + parameters + "): called"); - - // Write out the results. - try - { - // writer.write("\n"); - writer.write("\n"); - - for (Result result : results) - { - writer.write(" \n"); - - if (result.error != null) - { - writer.write(" "); - result.error.printStackTrace(new PrintWriter(writer)); - writer.write(" "); - } - else if (result.failure != null) - { - writer.write(" "); - result.failure.printStackTrace(new PrintWriter(writer)); - writer.write(" "); - } - - writer.write(" \n"); - } - - writer.write("\n"); - writer.flush(); - } - catch (IOException e) - { - throw new RuntimeException("Unable to write the test results.", e); - } - } - - /** - * Supplies the shutdown hook. - * - * @return The shut down hook. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - log.debug("XMLTestListener::ShutdownHook: called"); - } - }); - } - - /** - * Used to capture the results of a particular test run. - */ - protected static class Result - { - /** Holds the name of the test class. */ - public String testClass; - - /** Holds the name of the test method. */ - public String testName; - - /** Holds the exception that caused error in this test. */ - public Throwable error; - - /** Holds the assertion exception that caused failure in this test. */ - public AssertionFailedError failure; - - /** - * Creates a placeholder for the results of a test. - * - * @param testClass The test class. - * @param testName The name of the test that was run. - */ - public Result(String testClass, String testName) - { - this.testClass = testClass; - this.testName = testName; - } - } -} +/* + * + * 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.junit.extensions.listeners; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.ShutdownHookable; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.*; + +/** + * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified + * writer. + * + *

      The API for this listener accepts notifications about different aspects of a tests results through different + * methods, so some assumption needs to be made as to which test result a notification refers to. For example + * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is + * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may + * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used + * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest} + * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur + * between the start and end and will be given with the same thread id as the start and end, so the thread id provides + * a unqiue value to identify a particular test run against. + * + *

      + *
      CRC Card
      Responsibilities Collaborations + *
      Listen to test lifecycle notifications. + *
      Listen to test errors and failures. + *
      Listen to test timings. + *
      Listen to test memory usages. + *
      Listen to parameterized test parameters. + *
      Responsibilities + *
      + * + * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring + * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as + * the ant XML formatter, and a more structured one for outputing results with timings and summaries from + * performance tests. + * + * @author Rupert Smith + */ +public class XMLTestListener implements TKTestListener, ShutdownHookable +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(XMLTestListener.class); + + /** The results file writer. */ + protected Writer writer; + + /** Holds the results for individual tests. */ + // protected Map results = new LinkedHashMap(); + // protected List results = new ArrayList(); + + /** + * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an + * explicit thread id must be used, where notifications come from different threads than the ones that called + * the test method. + */ + Map threadLocalResults = Collections.synchronizedMap(new LinkedHashMap()); + + /** + * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means + * that the thread id is freed for the thread to generate more results. + */ + List results = new ArrayList(); + + /** Holds the overall error count. */ + protected int errors = 0; + + /** Holds the overall failure count. */ + protected int failures = 0; + + /** Holds the overall tests run count. */ + protected int runs = 0; + + /** Holds the name of the class that tests are being run for. */ + String testClassName; + + /** + * Creates a new XML results output listener that writes to the specified location. + * + * @param writer The location to write results to. + * @param testClassName The name of the test class to include in the test results. + */ + public XMLTestListener(Writer writer, String testClassName) + { + log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called"); + + this.writer = writer; + this.testClassName = testClassName; + } + + /** + * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed. + * + * @param test The test to resest any results for. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void reset(Test test, Long threadId) + { + log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + XMLTestListener.Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + + r.error = null; + r.failure = null; + + } + + /** + * Notification that a test started. + * + * @param test The test that started. + */ + public void startTest(Test test) + { + log.debug("public void startTest(Test test = " + test + "): called"); + + Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName()); + + // Initialize the thread local test results. + threadLocalResults.put(Thread.currentThread().getId(), newResult); + runs++; + } + + /** + * Should be called every time a test completes with the run time of that test. + * + * @param test The name of the test. + * @param nanos The run time of the test in nanoseconds. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void timing(Test test, long nanos, Long threadId) + { } + + /** + * Should be called every time a test completed with the amount of memory used before and after the test was run. + * + * @param test The test which memory was measured for. + * @param memStart The total JVM memory used before the test was run. + * @param memEnd The total JVM memory used after the test was run. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void memoryUsed(Test test, long memStart, long memEnd, Long threadId) + { } + + /** + * Should be called every time a parameterized test completed with the int value of its test parameter. + * + * @param test The test which memory was measured for. + * @param parameter The int parameter value. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void parameterValue(Test test, int parameter, Long threadId) + { } + + /** + * Should be called every time a test completes with the current number of test threads running. + * + * @param test The test for which the measurement is being generated. + * @param threads The number of tests being run concurrently. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void concurrencyLevel(Test test, int threads, Long threadId) + { } + + /** + * Notifies listeners of the tests read/set properties. + * + * @param properties The tests read/set properties. + */ + public void properties(Properties properties) + { } + + /** + * Notification that a test ended. + * + * @param test The test that ended. + */ + public void endTest(Test test) + { + log.debug("public void endTest(Test test = " + test + "): called"); + + // Move complete test results into the completed tests list. + Result r = threadLocalResults.get(Thread.currentThread().getId()); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * Called when a test completes. Success, failure and errors. This method should be used when registering an + * end test from a different thread than the one that started the test. + * + * @param test The test which completed. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void endTest(Test test, Long threadId) + { + log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called"); + + // Move complete test results into the completed tests list. + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + results.add(r); + + // Clear all the test results for the thread. + threadLocalResults.remove(Thread.currentThread().getId()); + } + + /** + * An error occurred. + * + * @param test The test in which the error occurred. + * @param t The throwable that resulted from the error. + */ + public void addError(Test test, Throwable t) + { + log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.error = t; + errors++; + } + + /** + * A failure occurred. + * + * @param test The test in which the failure occurred. + * @param t The JUnit assertions that led to the failure. + */ + public void addFailure(Test test, AssertionFailedError t) + { + log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called"); + + Result r = threadLocalResults.get(Thread.currentThread().getId()); + r.failure = t; + failures++; + } + + /** + * Called when a test completes to mark it as a test fail. This method should be used when registering a + * failure from a different thread than the one that started the test. + * + * @param test The test which failed. + * @param e The assertion that failed the test. + * @param threadId Optional thread id if not calling from thread that started the test method. May be null. + */ + public void addFailure(Test test, AssertionFailedError e, Long threadId) + { + log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called"); + + Result r = + (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId); + r.failure = e; + failures++; + } + + /** + * Notifies listeners of the start of a complete run of tests. + */ + public void startBatch() + { + log.debug("public void startBatch(): called"); + + // Reset all results counts. + threadLocalResults = Collections.synchronizedMap(new HashMap()); + errors = 0; + failures = 0; + runs = 0; + + // Write out the file header. + try + { + writer.write("\n"); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Notifies listeners of the end of a complete run of tests. + * + * @param parameters The optional test parameters to log out with the batch results. + */ + public void endBatch(Properties parameters) + { + log.debug("public void endBatch(Properties parameters = " + parameters + "): called"); + + // Write out the results. + try + { + // writer.write("\n"); + writer.write("\n"); + + for (Result result : results) + { + writer.write(" \n"); + + if (result.error != null) + { + writer.write(" "); + result.error.printStackTrace(new PrintWriter(writer)); + writer.write(" "); + } + else if (result.failure != null) + { + writer.write(" "); + result.failure.printStackTrace(new PrintWriter(writer)); + writer.write(" "); + } + + writer.write(" \n"); + } + + writer.write("\n"); + writer.flush(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to write the test results.", e); + } + } + + /** + * Supplies the shutdown hook. + * + * @return The shut down hook. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + log.debug("XMLTestListener::ShutdownHook: called"); + } + }); + } + + /** + * Used to capture the results of a particular test run. + */ + protected static class Result + { + /** Holds the name of the test class. */ + public String testClass; + + /** Holds the name of the test method. */ + public String testName; + + /** Holds the exception that caused error in this test. */ + public Throwable error; + + /** Holds the assertion exception that caused failure in this test. */ + public AssertionFailedError failure; + + /** + * Creates a placeholder for the results of a test. + * + * @param testClass The test class. + * @param testName The name of the test that was run. + */ + public Result(String testClass, String testName) + { + this.testClass = testClass; + this.testName = testName; + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java index 61c58bf3ba..f158090e96 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java @@ -1,787 +1,787 @@ -/* - * - * 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.junit.extensions.util; - -import java.text.CharacterIterator; -import java.text.StringCharacterIterator; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure - * that they fit their specified format. A command line is made up of flags and options, both may be refered to as - * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not - * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so - * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify - * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified. - * - *

      Some examples command line are: - * - *

        - *
      • This one has two options that expect arguments: - *
        - * cruisecontrol -configfile cruisecontrol.xml -port 9000
        - * 
        - *
      • This has one no-arg flag and two 'free' arguments: - *
        - * zip -r project.zip project/*
        - * 
        - *
      • This one concatenates multiple flags into a single block with only one '-': - *
        - * jar -tvf mytar.tar
        - * 
        - * - *

        The parsing rules are: - * - *

          - *
        1. Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter - * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own. - *
        2. Options expecting arguments must always be on their own. - *
        3. The argument to an option may be seperated from it by whitespace or appended directly onto the option. - *
        4. The argument to an option may never begin with a '-' character. - *
        5. All other arguments not beginning with a '-' character are free arguments that do not belong to any option. - *
        6. The second or later of a set of duplicate or repeated flags override earlier ones. - *
        7. Options are matched up to the shortest matching option. This is because of the possibility of having no space - * between an option and its argument. This rules out the possibility of using two options where one is an opening - * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because - * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with - * the "bar" argument. - *
        - * - *

        By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed - * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method. - * - *

        - *
        CRC Card
        Responsibilities Collaborations - *
        Accept a command line specification. - *
        Parse a command line into properties, validating it against its specification. - *
        Report all errors between a command line and its specification. - *
        Provide a formatted usage string for a command line. - *
        Provide a formatted options in force string for a command line. - *
        Allow errors on unknowns behaviour to be turned on or off. - *
        - * - * @author Rupert Smith - */ -public class CommandLineParser -{ - /** - * Holds a mapping from command line option names to detailed information about those options. - * Use of a tree map ensures that the options are easy to print in alphabetical order as a usage string. - * An alternative might be to use a LinkedHashMap to print them in the order they are specified. - */ - private Map optionMap = new TreeMap(); - - /** Holds a list of parsing errors. */ - private List parsingErrors = new ArrayList(); - - /** Holds the regular head matcher to match command line options with. */ - private Matcher optionMatcher = null; - - /** Holds the parsed command line properties after parsing. */ - private Properties parsedProperties = null; - - /** Holds any trailing name=value pairs specified in the free arguments. */ - private Properties trailingProperties = null; - - /** Flag used to indicate that errors should be created for unknown options. False by default. */ - private boolean errorsOnUnknowns = false; - - /** - * Creates a command line options parser from a command line specification. This is passed to this constructor - * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static - * array may therefore easily be used to configure the command line parser in a single method call with an easily - * readable format. - * - *

        Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they - * are assumed to be null. The elements specify the following parameters: - *

          - *
        1. The name of the option without the leading '-'. For example, "file". To specify the format of the 'free' - * arguments use the option names "1", "2", ... and so on. - *
        2. The option comment. A line of text describing the usage of the option. For example, "The file to be processed." - *
        3. The options argument. This is a very short description of the argument to the option, often a single word - * or a reminder as to the arguments format. When this element is null the option is a flag and does not - * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified - * is only used to print in the usage message to remind the user of the usage of the option. - *
        4. The mandatory flag. When set to "true" an option must always be specified. Any other value, including null, - * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so - * this is ignored for flags. - *
        5. A regular head describing the format that the argument must take. Ignored if null. - *
        - *

        An example call to this constructor is: - * - *

        -     * CommandLineParser commandLine = new CommandLineParser(
        -     *     new String[][] {{"file", "The file to be processed. ", "filename", "true"},
        -     *                     {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
        -     *                     {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
        -     *                     {"v", "Verbose mode. Prints information about the processing as it goes."},
        -     *                     {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
        -     * 
        - * - * @param config The configuration as an array of arrays of strings. - */ - public CommandLineParser(String[][] config) - { - // Loop through all the command line option specifications creating details for each in the options map. - for (String[] nextOptionSpec : config) - { - addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null, - (nextOptionSpec.length > 3) && ("true".equals(nextOptionSpec[3])), - (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null); - } - } - - /** - * Extracts all name=value pairs from the command line, sets them all as system properties and also returns - * a map of properties containing them. - * - * @param args The command line. - * @param commandLine The command line parser. - * @param properties The properties object to inject all parsed properties into (optional may be null). - * - * @return A set of properties containing all name=value pairs from the command line. - */ - public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties) - { - // Capture the command line arguments or display errors and correct usage and then exit. - Properties options = null; - - try - { - options = commandLine.parseCommandLine(args); - - // Add all the command line options and trailing settings to properties if the optional properties object - // to copy them into has been set. - if (properties != null) - { - commandLine.addTrailingPairsToProperties(properties); - commandLine.addOptionsToProperties(properties); - } - } - catch (IllegalArgumentException e) - { - System.out.println(commandLine.getErrors()); - System.out.println(commandLine.getUsage()); - System.exit(1); - } - - return options; - } - - /** - * Lists all the parsing errors from the most recent parsing in a string. - * - * @return All the parsing errors from the most recent parsing. - */ - public String getErrors() - { - // Return the empty string if there are no errors. - if (parsingErrors.isEmpty()) - { - return ""; - } - - // Concatenate all the parsing errors together. - String result = ""; - - for (String s : parsingErrors) - { - result += s; - } - - return result; - } - - /** - * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet. - * - * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet. - */ - public String getOptionsInForce() - { - // Check if there are no properties to report and return and empty string if so. - if (parsedProperties == null) - { - return ""; - } - - // List all the properties. - String result = "Options in force:\n"; - - for (Map.Entry property : parsedProperties.entrySet()) - { - result += property.getKey() + " = " + property.getValue() + "\n"; - } - - return result; - } - - /** - * Generates a usage string consisting of the name of each option and each options argument description and - * comment. - * - * @return A usage string for all the options. - */ - public String getUsage() - { - String result = "Options:\n"; - - int optionWidth = 0; - int argumentWidth = 0; - - // Calculate the column widths required for aligned layout. - for (CommandLineOption optionInfo : optionMap.values()) - { - int oWidth = optionInfo.option.length(); - int aWidth = (optionInfo.argument != null) ? (optionInfo.argument.length()) : 0; - - optionWidth = (oWidth > optionWidth) ? oWidth : optionWidth; - argumentWidth = (aWidth > argumentWidth) ? aWidth : argumentWidth; - } - - // Print usage on each of the command line options. - for (CommandLineOption optionInfo : optionMap.values()) - { - String argString = ((optionInfo.argument != null) ? (optionInfo.argument) : ""); - String optionString = optionInfo.option; - - argString = rightPad(argString, " ", argumentWidth); - optionString = rightPad(optionString, " ", optionWidth); - - result += "-" + optionString + " " + argString + " " + optionInfo.comment + "\n"; - } - - return result; - } - - /** - * Right pads a string with a given string to a given size. This method will repeat the padder string as many - * times as is necessary until the exact specified size is reached. If the specified size is less than the size - * of the original string then the original string is returned unchanged. - * - *
        -     * Example1 - original string "cat", padder string "white", size 8 gives "catwhite".
        -     * Example2 - original string "cat", padder string "white", size 15 gives "catwhitewhitewh".
        -     * Example3 - original string "cat", padder string "white", size 2 gives "cat".
        -     * 
        - * - * @param stringToPad The original string. - * @param padder The string to pad onto the original string. - * @param size The required size of the new string. - * - * @return The newly padded string. - */ - public static String rightPad(String stringToPad, String padder, int size) - { - if (padder.length() == 0) - { - return stringToPad; - } - - StringBuffer strb = new StringBuffer(stringToPad); - StringCharacterIterator sci = new StringCharacterIterator(padder); - - while (strb.length() < size) - { - for (char ch = sci.first(); ch != CharacterIterator.DONE; ch = sci.next()) - { - if (strb.length() < size) - { - strb.append(String.valueOf(ch)); - } - } - } - - return strb.toString(); - } - - /** - * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options - * as errors. When turned off, all unknowns are simply ignored. - * - * @param errors The setting of the errors on unkown flag. True to turn it on. - */ - public void setErrorsOnUnknowns(boolean errors) - { - errorsOnUnknowns = errors; - } - - /** - * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments - * are keyed by integers as strings starting at "1" and then "2", ... and so on. - * - *

        See the class level comment for a description of the parsing rules. - * - * @param args The command line arguments. - * - * @return The arguments as a set of properties. - * - * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception - * is thrown a call to {@link #getErrors} will provide a diagnostic of the command - * line errors. - */ - public Properties parseCommandLine(String[] args) throws IllegalArgumentException - { - Properties options = new Properties(); - - // Used to keep count of the current 'free' argument. - int free = 1; - - // Used to indicate that the most recently parsed option is expecting arguments. - boolean expectingArgs = false; - - // The option that is expecting arguments from the next element of the command line. - String optionExpectingArgs = null; - - // Used to indicate that the most recently parsed option is a duplicate and should be ignored. - // boolean ignore = false; - - // Create the regular head matcher for the command line options. - String regexp = "^("; - int optionsAdded = 0; - - for (Iterator i = optionMap.keySet().iterator(); i.hasNext();) - { - String nextOption = i.next(); - - // Check that the option is not a free argument definition. - boolean notFree = false; - - try - { - Integer.parseInt(nextOption); - } - catch (NumberFormatException e) - { - notFree = true; - } - - // Add the option to the regular head matcher if it is not a free argument definition. - if (notFree) - { - regexp += nextOption + (i.hasNext() ? "|" : ""); - optionsAdded++; - } - } - - // There has to be more that one option in the regular head or else the compiler complains that the close - // cannot be nullable if the '?' token is used to make the matched option string optional. - regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)"; - Pattern pattern = Pattern.compile(regexp); - - // Loop through all the command line arguments. - for (String arg1 : args) - { - // Check if the next command line argument begins with a '-' character and is therefore the start of - // an option. - if (arg1.startsWith("-")) - { - // Extract the value of the option without the leading '-'. - String arg = arg1.substring(1); - - // Match up to the longest matching option. - optionMatcher = pattern.matcher(arg); - optionMatcher.matches(); - - String matchedOption = optionMatcher.group(1); - - // Match any argument directly appended onto the longest matching option. - String matchedArg = optionMatcher.group(2); - - // Check that a known option was matched. - if ((matchedOption != null) && !"".equals(matchedOption)) - { - // Get the command line option information for the matched option. - CommandLineOption optionInfo = optionMap.get(matchedOption); - - // Check if this option is expecting arguments. - if (optionInfo.expectsArgs) - { - // The option is expecting arguments so swallow the next command line argument as an - // argument to this option. - expectingArgs = true; - optionExpectingArgs = matchedOption; - - // In the mean time set this options argument to the empty string in case no argument is ever - // supplied. - // options.put(matchedOption, ""); - } - - // Check if the option was matched on its own and is a flag in which case set that flag. - if ("".equals(matchedArg) && !optionInfo.expectsArgs) - { - options.put(matchedOption, "true"); - } - // The option was matched as a substring with its argument appended to it or is a flag that is - // condensed together with other flags. - else if (!"".equals(matchedArg)) - { - // Check if the option is a flag and therefore is allowed to be condensed together - // with other flags. - if (!optionInfo.expectsArgs) - { - // Set the first matched flag. - options.put(matchedOption, "true"); - - // Repeat the longest matching process on the remainder but ensure that the remainder - // consists only of flags as only flags may be condensed together in this fashion. - do - { - // Match the remainder against the options. - optionMatcher = pattern.matcher(matchedArg); - optionMatcher.matches(); - - matchedOption = optionMatcher.group(1); - matchedArg = optionMatcher.group(2); - - // Check that an option was matched. - if (matchedOption != null) - { - // Get the command line option information for the next matched option. - optionInfo = optionMap.get(matchedOption); - - // Ensure that the next option is a flag or raise an error if not. - if (optionInfo.expectsArgs) - { - parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n"); - } - - options.put(matchedOption, "true"); - } - // The remainder could not be matched against a flag it is either an unknown flag - // or an illegal argument to a flag. - else - { - parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n"); - - break; - } - } - // Continue until the remainder of the argument has all been matched with flags. - while (!"".equals(matchedArg)); - } - // The option is expecting an argument, so store the unmatched portion against it - // as its argument. - else - { - // Check the arguments format is correct against any specified format. - checkArgumentFormat(optionInfo, matchedArg); - - // Store the argument against its option (regardless of its format). - options.put(matchedOption, matchedArg); - - // The argument to this flag has already been supplied to it. Do not swallow the - // next command line argument as an argument to this flag. - expectingArgs = false; - } - } - } - else // No matching option was found. - { - // Add this to the list of parsing errors if errors on unkowns is being used. - if (errorsOnUnknowns) - { - parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n"); - } - } - } - // The command line argument did not being with a '-' so it is an argument to the previous flag or it - // is a free argument. - else - { - // Check if a previous flag is expecting to swallow this next argument as its argument. - if (expectingArgs) - { - // Get the option info for the option waiting for arguments. - CommandLineOption optionInfo = optionMap.get(optionExpectingArgs); - - // Check the arguments format is correct against any specified format. - checkArgumentFormat(optionInfo, arg1); - - // Store the argument against its option (regardless of its format). - options.put(optionExpectingArgs, arg1); - - // Clear the expecting args flag now that the argument has been swallowed. - expectingArgs = false; - optionExpectingArgs = null; - } - // This command line option is not an argument to any option. Add it to the set of 'free' options. - else - { - // Get the option info for the free option, if there is any. - CommandLineOption optionInfo = optionMap.get(Integer.toString(free)); - - if (optionInfo != null) - { - // Check the arguments format is correct against any specified format. - checkArgumentFormat(optionInfo, arg1); - } - - // Add to the list of free options. - options.put(Integer.toString(free), arg1); - - // Move on to the next free argument. - free++; - } - } - } - - // Scan through all the specified options to check that all mandatory options have been set and that all flags - // that were not set are set to false in the set of properties. - for (CommandLineOption optionInfo : optionMap.values()) - { - // Check if this is a flag. - if (!optionInfo.expectsArgs) - { - // Check if the flag is not set in the properties and set it to false if so. - if (!options.containsKey(optionInfo.option)) - { - options.put(optionInfo.option, "false"); - } - } - // Check if this is a mandatory option and was not set. - else if (optionInfo.mandatory && !options.containsKey(optionInfo.option)) - { - // Create an error for the missing option. - parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n"); - } - } - - // Check if there were any errors. - if (!parsingErrors.isEmpty()) - { - // Throw an illegal argument exception to signify that there were parsing errors. - throw new IllegalArgumentException(); - } - - // Convert any name/value pairs in the free arguments into properties in the parsed options. - trailingProperties = takeFreeArgsAsProperties(options, 1); - - parsedProperties = options; - - return options; - } - - /** - * If a command line has been parsed, calling this method sets all of its free arguments that were name=value pairs - * on the specified properties. - * - * @param properties The property set to add the name=value pairs to. - */ - public void addTrailingPairsToProperties(Properties properties) - { - if (trailingProperties != null) - { - for (Object propKey : trailingProperties.keySet()) - { - String name = (String) propKey; - String value = trailingProperties.getProperty(name); - - properties.setProperty(name, value); - } - } - } - - /** - * If a command line has been parsed, calling this method sets all of its options that were set to the specified - * properties. - * - * @param properties The property set to the options to. - */ - public void addOptionsToProperties(Properties properties) - { - if (parsedProperties != null) - { - for (Object propKey : parsedProperties.keySet()) - { - String name = (String) propKey; - String value = parsedProperties.getProperty(name); - - // This filters out all trailing items. - if (!name.matches("^[0-9]+$")) - { - properties.setProperty(name, value); - } - } - } - } - - /** - * Resets this command line parser after it has been used to parse a command line. This method will only need - * to be called to use this parser a second time which is not likely seeing as a command line is usually only - * specified once. However, it is exposed as a public method for the rare case where this may be done. - * - *

        Cleans the internal state of this parser, removing all stored errors and information about the options in - * force. - */ - public void reset() - { - parsingErrors = new ArrayList(); - parsedProperties = null; - } - - /** - * Adds the option to list of available command line options. - * - * @param option The option to add as an available command line option. - * @param comment A comment for the option. - * @param argument The text that appears after the option in the usage string. - * @param mandatory When true, indicates that this option is mandatory. - * @param formatRegexp The format that the argument must take, defined as a regular head. - */ - protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp) - { - // Check if usage text has been set in which case this option is expecting arguments. - boolean expectsArgs = (!((argument == null) || argument.equals(""))); - - // Add the option to the map of command line options. - CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp); - optionMap.put(option, opt); - } - - /** - * Converts the free arguments into property declarations. After parsing the command line the free arguments - * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method - * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value - * 'value'. - * - *

        For example the comand line: - *

        -     * ... debug=true
        -     * 
        - * - *

        After parsing has properties: - *

        [[1, debug=true]]
        - * - *

        After applying this method the properties are: - *

        [[1, debug=true], [debug, true]]
        - * - * @param properties The parsed command line properties. - * @param from The free argument index to convert to properties from. - * - * @return The parsed command line properties, with free argument name value pairs too. - */ - private Properties takeFreeArgsAsProperties(Properties properties, int from) - { - Properties result = new Properties(); - - for (int i = from; true; i++) - { - String nextFreeArg = properties.getProperty(Integer.toString(i)); - - // Terminate the loop once all free arguments have been consumed. - if (nextFreeArg == null) - { - break; - } - - // Split it on the =, strip any whitespace and set it as a system property. - String[] nameValuePair = nextFreeArg.split("="); - - if (nameValuePair.length == 2) - { - result.setProperty(nameValuePair[0], nameValuePair[1]); - } - } - - return result; - } - - /** - * Checks the format of an argument to an option against its specified regular head format if one has - * been set. Any errors are added to the list of parsing errors. - * - * @param optionInfo The command line option information for the option which is havings its argument checked. - * @param matchedArg The string argument to the option. - */ - private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg) - { - // Check if this option enforces a format for its argument. - if (optionInfo.argumentFormatRegexp != null) - { - Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp); - Matcher argumentMatcher = pattern.matcher(matchedArg); - - // Check if the argument does not meet its required format. - if (!argumentMatcher.matches()) - { - // Create an error for this badly formed argument. - parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n"); - } - } - } - - /** - * Holds information about a command line options. This includes what its name is, whether or not it is a flag, - * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its - * regular head format is. - * - *

        - *
        CRC Card
        Responsibilities Collaborations - *
        Hold details of a command line option. - *
        - * - * @author Rupert Smith - */ - protected class CommandLineOption - { - /** Holds the text for the flag to match this argument with. */ - public String option = null; - - /** Holds a string describing how to use this command line argument. */ - public String argument = null; - - /** Flag that determines whether or not this command line argument can take arguments. */ - public boolean expectsArgs = false; - - /** Holds a short comment describing what this command line argument is for. */ - public String comment = null; - - /** Flag that determines whether or not this is an mandatory command line argument. */ - public boolean mandatory = false; - - /** A regular head describing what format the argument to this option muist have. */ - public String argumentFormatRegexp = null; - - /** - * Create a command line option object that holds specific information about a command line option. - * - * @param option The text that matches the option. - * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false. - * @param comment A comment explaining how to use this option. - * @param argument A short reminder of the format of the argument to this option/ - * @param mandatory Set to true if this option is mandatory. - * @param formatRegexp The regular head that the argument to this option must meet to be valid. - */ - public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory, - String formatRegexp) - { - this.option = option; - this.expectsArgs = expectsArgs; - this.comment = comment; - this.argument = argument; - this.mandatory = mandatory; - this.argumentFormatRegexp = formatRegexp; - } - } -} +/* + * + * 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.junit.extensions.util; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure + * that they fit their specified format. A command line is made up of flags and options, both may be refered to as + * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not + * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so + * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify + * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified. + * + *

        Some examples command line are: + * + *

          + *
        • This one has two options that expect arguments: + *
          + * cruisecontrol -configfile cruisecontrol.xml -port 9000
          + * 
          + *
        • This has one no-arg flag and two 'free' arguments: + *
          + * zip -r project.zip project/*
          + * 
          + *
        • This one concatenates multiple flags into a single block with only one '-': + *
          + * jar -tvf mytar.tar
          + * 
          + * + *

          The parsing rules are: + * + *

            + *
          1. Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter + * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own. + *
          2. Options expecting arguments must always be on their own. + *
          3. The argument to an option may be seperated from it by whitespace or appended directly onto the option. + *
          4. The argument to an option may never begin with a '-' character. + *
          5. All other arguments not beginning with a '-' character are free arguments that do not belong to any option. + *
          6. The second or later of a set of duplicate or repeated flags override earlier ones. + *
          7. Options are matched up to the shortest matching option. This is because of the possibility of having no space + * between an option and its argument. This rules out the possibility of using two options where one is an opening + * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because + * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with + * the "bar" argument. + *
          + * + *

          By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed + * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Accept a command line specification. + *
          Parse a command line into properties, validating it against its specification. + *
          Report all errors between a command line and its specification. + *
          Provide a formatted usage string for a command line. + *
          Provide a formatted options in force string for a command line. + *
          Allow errors on unknowns behaviour to be turned on or off. + *
          + * + * @author Rupert Smith + */ +public class CommandLineParser +{ + /** + * Holds a mapping from command line option names to detailed information about those options. + * Use of a tree map ensures that the options are easy to print in alphabetical order as a usage string. + * An alternative might be to use a LinkedHashMap to print them in the order they are specified. + */ + private Map optionMap = new TreeMap(); + + /** Holds a list of parsing errors. */ + private List parsingErrors = new ArrayList(); + + /** Holds the regular head matcher to match command line options with. */ + private Matcher optionMatcher = null; + + /** Holds the parsed command line properties after parsing. */ + private Properties parsedProperties = null; + + /** Holds any trailing name=value pairs specified in the free arguments. */ + private Properties trailingProperties = null; + + /** Flag used to indicate that errors should be created for unknown options. False by default. */ + private boolean errorsOnUnknowns = false; + + /** + * Creates a command line options parser from a command line specification. This is passed to this constructor + * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static + * array may therefore easily be used to configure the command line parser in a single method call with an easily + * readable format. + * + *

          Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they + * are assumed to be null. The elements specify the following parameters: + *

            + *
          1. The name of the option without the leading '-'. For example, "file". To specify the format of the 'free' + * arguments use the option names "1", "2", ... and so on. + *
          2. The option comment. A line of text describing the usage of the option. For example, "The file to be processed." + *
          3. The options argument. This is a very short description of the argument to the option, often a single word + * or a reminder as to the arguments format. When this element is null the option is a flag and does not + * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified + * is only used to print in the usage message to remind the user of the usage of the option. + *
          4. The mandatory flag. When set to "true" an option must always be specified. Any other value, including null, + * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so + * this is ignored for flags. + *
          5. A regular head describing the format that the argument must take. Ignored if null. + *
          + *

          An example call to this constructor is: + * + *

          +     * CommandLineParser commandLine = new CommandLineParser(
          +     *     new String[][] {{"file", "The file to be processed. ", "filename", "true"},
          +     *                     {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
          +     *                     {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
          +     *                     {"v", "Verbose mode. Prints information about the processing as it goes."},
          +     *                     {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
          +     * 
          + * + * @param config The configuration as an array of arrays of strings. + */ + public CommandLineParser(String[][] config) + { + // Loop through all the command line option specifications creating details for each in the options map. + for (String[] nextOptionSpec : config) + { + addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null, + (nextOptionSpec.length > 3) && ("true".equals(nextOptionSpec[3])), + (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null); + } + } + + /** + * Extracts all name=value pairs from the command line, sets them all as system properties and also returns + * a map of properties containing them. + * + * @param args The command line. + * @param commandLine The command line parser. + * @param properties The properties object to inject all parsed properties into (optional may be null). + * + * @return A set of properties containing all name=value pairs from the command line. + */ + public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties) + { + // Capture the command line arguments or display errors and correct usage and then exit. + Properties options = null; + + try + { + options = commandLine.parseCommandLine(args); + + // Add all the command line options and trailing settings to properties if the optional properties object + // to copy them into has been set. + if (properties != null) + { + commandLine.addTrailingPairsToProperties(properties); + commandLine.addOptionsToProperties(properties); + } + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(1); + } + + return options; + } + + /** + * Lists all the parsing errors from the most recent parsing in a string. + * + * @return All the parsing errors from the most recent parsing. + */ + public String getErrors() + { + // Return the empty string if there are no errors. + if (parsingErrors.isEmpty()) + { + return ""; + } + + // Concatenate all the parsing errors together. + String result = ""; + + for (String s : parsingErrors) + { + result += s; + } + + return result; + } + + /** + * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet. + * + * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet. + */ + public String getOptionsInForce() + { + // Check if there are no properties to report and return and empty string if so. + if (parsedProperties == null) + { + return ""; + } + + // List all the properties. + String result = "Options in force:\n"; + + for (Map.Entry property : parsedProperties.entrySet()) + { + result += property.getKey() + " = " + property.getValue() + "\n"; + } + + return result; + } + + /** + * Generates a usage string consisting of the name of each option and each options argument description and + * comment. + * + * @return A usage string for all the options. + */ + public String getUsage() + { + String result = "Options:\n"; + + int optionWidth = 0; + int argumentWidth = 0; + + // Calculate the column widths required for aligned layout. + for (CommandLineOption optionInfo : optionMap.values()) + { + int oWidth = optionInfo.option.length(); + int aWidth = (optionInfo.argument != null) ? (optionInfo.argument.length()) : 0; + + optionWidth = (oWidth > optionWidth) ? oWidth : optionWidth; + argumentWidth = (aWidth > argumentWidth) ? aWidth : argumentWidth; + } + + // Print usage on each of the command line options. + for (CommandLineOption optionInfo : optionMap.values()) + { + String argString = ((optionInfo.argument != null) ? (optionInfo.argument) : ""); + String optionString = optionInfo.option; + + argString = rightPad(argString, " ", argumentWidth); + optionString = rightPad(optionString, " ", optionWidth); + + result += "-" + optionString + " " + argString + " " + optionInfo.comment + "\n"; + } + + return result; + } + + /** + * Right pads a string with a given string to a given size. This method will repeat the padder string as many + * times as is necessary until the exact specified size is reached. If the specified size is less than the size + * of the original string then the original string is returned unchanged. + * + *
          +     * Example1 - original string "cat", padder string "white", size 8 gives "catwhite".
          +     * Example2 - original string "cat", padder string "white", size 15 gives "catwhitewhitewh".
          +     * Example3 - original string "cat", padder string "white", size 2 gives "cat".
          +     * 
          + * + * @param stringToPad The original string. + * @param padder The string to pad onto the original string. + * @param size The required size of the new string. + * + * @return The newly padded string. + */ + public static String rightPad(String stringToPad, String padder, int size) + { + if (padder.length() == 0) + { + return stringToPad; + } + + StringBuffer strb = new StringBuffer(stringToPad); + StringCharacterIterator sci = new StringCharacterIterator(padder); + + while (strb.length() < size) + { + for (char ch = sci.first(); ch != CharacterIterator.DONE; ch = sci.next()) + { + if (strb.length() < size) + { + strb.append(String.valueOf(ch)); + } + } + } + + return strb.toString(); + } + + /** + * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options + * as errors. When turned off, all unknowns are simply ignored. + * + * @param errors The setting of the errors on unkown flag. True to turn it on. + */ + public void setErrorsOnUnknowns(boolean errors) + { + errorsOnUnknowns = errors; + } + + /** + * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments + * are keyed by integers as strings starting at "1" and then "2", ... and so on. + * + *

          See the class level comment for a description of the parsing rules. + * + * @param args The command line arguments. + * + * @return The arguments as a set of properties. + * + * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception + * is thrown a call to {@link #getErrors} will provide a diagnostic of the command + * line errors. + */ + public Properties parseCommandLine(String[] args) throws IllegalArgumentException + { + Properties options = new Properties(); + + // Used to keep count of the current 'free' argument. + int free = 1; + + // Used to indicate that the most recently parsed option is expecting arguments. + boolean expectingArgs = false; + + // The option that is expecting arguments from the next element of the command line. + String optionExpectingArgs = null; + + // Used to indicate that the most recently parsed option is a duplicate and should be ignored. + // boolean ignore = false; + + // Create the regular head matcher for the command line options. + String regexp = "^("; + int optionsAdded = 0; + + for (Iterator i = optionMap.keySet().iterator(); i.hasNext();) + { + String nextOption = i.next(); + + // Check that the option is not a free argument definition. + boolean notFree = false; + + try + { + Integer.parseInt(nextOption); + } + catch (NumberFormatException e) + { + notFree = true; + } + + // Add the option to the regular head matcher if it is not a free argument definition. + if (notFree) + { + regexp += nextOption + (i.hasNext() ? "|" : ""); + optionsAdded++; + } + } + + // There has to be more that one option in the regular head or else the compiler complains that the close + // cannot be nullable if the '?' token is used to make the matched option string optional. + regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)"; + Pattern pattern = Pattern.compile(regexp); + + // Loop through all the command line arguments. + for (String arg1 : args) + { + // Check if the next command line argument begins with a '-' character and is therefore the start of + // an option. + if (arg1.startsWith("-")) + { + // Extract the value of the option without the leading '-'. + String arg = arg1.substring(1); + + // Match up to the longest matching option. + optionMatcher = pattern.matcher(arg); + optionMatcher.matches(); + + String matchedOption = optionMatcher.group(1); + + // Match any argument directly appended onto the longest matching option. + String matchedArg = optionMatcher.group(2); + + // Check that a known option was matched. + if ((matchedOption != null) && !"".equals(matchedOption)) + { + // Get the command line option information for the matched option. + CommandLineOption optionInfo = optionMap.get(matchedOption); + + // Check if this option is expecting arguments. + if (optionInfo.expectsArgs) + { + // The option is expecting arguments so swallow the next command line argument as an + // argument to this option. + expectingArgs = true; + optionExpectingArgs = matchedOption; + + // In the mean time set this options argument to the empty string in case no argument is ever + // supplied. + // options.put(matchedOption, ""); + } + + // Check if the option was matched on its own and is a flag in which case set that flag. + if ("".equals(matchedArg) && !optionInfo.expectsArgs) + { + options.put(matchedOption, "true"); + } + // The option was matched as a substring with its argument appended to it or is a flag that is + // condensed together with other flags. + else if (!"".equals(matchedArg)) + { + // Check if the option is a flag and therefore is allowed to be condensed together + // with other flags. + if (!optionInfo.expectsArgs) + { + // Set the first matched flag. + options.put(matchedOption, "true"); + + // Repeat the longest matching process on the remainder but ensure that the remainder + // consists only of flags as only flags may be condensed together in this fashion. + do + { + // Match the remainder against the options. + optionMatcher = pattern.matcher(matchedArg); + optionMatcher.matches(); + + matchedOption = optionMatcher.group(1); + matchedArg = optionMatcher.group(2); + + // Check that an option was matched. + if (matchedOption != null) + { + // Get the command line option information for the next matched option. + optionInfo = optionMap.get(matchedOption); + + // Ensure that the next option is a flag or raise an error if not. + if (optionInfo.expectsArgs) + { + parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n"); + } + + options.put(matchedOption, "true"); + } + // The remainder could not be matched against a flag it is either an unknown flag + // or an illegal argument to a flag. + else + { + parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n"); + + break; + } + } + // Continue until the remainder of the argument has all been matched with flags. + while (!"".equals(matchedArg)); + } + // The option is expecting an argument, so store the unmatched portion against it + // as its argument. + else + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, matchedArg); + + // Store the argument against its option (regardless of its format). + options.put(matchedOption, matchedArg); + + // The argument to this flag has already been supplied to it. Do not swallow the + // next command line argument as an argument to this flag. + expectingArgs = false; + } + } + } + else // No matching option was found. + { + // Add this to the list of parsing errors if errors on unkowns is being used. + if (errorsOnUnknowns) + { + parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n"); + } + } + } + // The command line argument did not being with a '-' so it is an argument to the previous flag or it + // is a free argument. + else + { + // Check if a previous flag is expecting to swallow this next argument as its argument. + if (expectingArgs) + { + // Get the option info for the option waiting for arguments. + CommandLineOption optionInfo = optionMap.get(optionExpectingArgs); + + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, arg1); + + // Store the argument against its option (regardless of its format). + options.put(optionExpectingArgs, arg1); + + // Clear the expecting args flag now that the argument has been swallowed. + expectingArgs = false; + optionExpectingArgs = null; + } + // This command line option is not an argument to any option. Add it to the set of 'free' options. + else + { + // Get the option info for the free option, if there is any. + CommandLineOption optionInfo = optionMap.get(Integer.toString(free)); + + if (optionInfo != null) + { + // Check the arguments format is correct against any specified format. + checkArgumentFormat(optionInfo, arg1); + } + + // Add to the list of free options. + options.put(Integer.toString(free), arg1); + + // Move on to the next free argument. + free++; + } + } + } + + // Scan through all the specified options to check that all mandatory options have been set and that all flags + // that were not set are set to false in the set of properties. + for (CommandLineOption optionInfo : optionMap.values()) + { + // Check if this is a flag. + if (!optionInfo.expectsArgs) + { + // Check if the flag is not set in the properties and set it to false if so. + if (!options.containsKey(optionInfo.option)) + { + options.put(optionInfo.option, "false"); + } + } + // Check if this is a mandatory option and was not set. + else if (optionInfo.mandatory && !options.containsKey(optionInfo.option)) + { + // Create an error for the missing option. + parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n"); + } + } + + // Check if there were any errors. + if (!parsingErrors.isEmpty()) + { + // Throw an illegal argument exception to signify that there were parsing errors. + throw new IllegalArgumentException(); + } + + // Convert any name/value pairs in the free arguments into properties in the parsed options. + trailingProperties = takeFreeArgsAsProperties(options, 1); + + parsedProperties = options; + + return options; + } + + /** + * If a command line has been parsed, calling this method sets all of its free arguments that were name=value pairs + * on the specified properties. + * + * @param properties The property set to add the name=value pairs to. + */ + public void addTrailingPairsToProperties(Properties properties) + { + if (trailingProperties != null) + { + for (Object propKey : trailingProperties.keySet()) + { + String name = (String) propKey; + String value = trailingProperties.getProperty(name); + + properties.setProperty(name, value); + } + } + } + + /** + * If a command line has been parsed, calling this method sets all of its options that were set to the specified + * properties. + * + * @param properties The property set to the options to. + */ + public void addOptionsToProperties(Properties properties) + { + if (parsedProperties != null) + { + for (Object propKey : parsedProperties.keySet()) + { + String name = (String) propKey; + String value = parsedProperties.getProperty(name); + + // This filters out all trailing items. + if (!name.matches("^[0-9]+$")) + { + properties.setProperty(name, value); + } + } + } + } + + /** + * Resets this command line parser after it has been used to parse a command line. This method will only need + * to be called to use this parser a second time which is not likely seeing as a command line is usually only + * specified once. However, it is exposed as a public method for the rare case where this may be done. + * + *

          Cleans the internal state of this parser, removing all stored errors and information about the options in + * force. + */ + public void reset() + { + parsingErrors = new ArrayList(); + parsedProperties = null; + } + + /** + * Adds the option to list of available command line options. + * + * @param option The option to add as an available command line option. + * @param comment A comment for the option. + * @param argument The text that appears after the option in the usage string. + * @param mandatory When true, indicates that this option is mandatory. + * @param formatRegexp The format that the argument must take, defined as a regular head. + */ + protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp) + { + // Check if usage text has been set in which case this option is expecting arguments. + boolean expectsArgs = (!((argument == null) || argument.equals(""))); + + // Add the option to the map of command line options. + CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp); + optionMap.put(option, opt); + } + + /** + * Converts the free arguments into property declarations. After parsing the command line the free arguments + * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method + * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value + * 'value'. + * + *

          For example the comand line: + *

          +     * ... debug=true
          +     * 
          + * + *

          After parsing has properties: + *

          [[1, debug=true]]
          + * + *

          After applying this method the properties are: + *

          [[1, debug=true], [debug, true]]
          + * + * @param properties The parsed command line properties. + * @param from The free argument index to convert to properties from. + * + * @return The parsed command line properties, with free argument name value pairs too. + */ + private Properties takeFreeArgsAsProperties(Properties properties, int from) + { + Properties result = new Properties(); + + for (int i = from; true; i++) + { + String nextFreeArg = properties.getProperty(Integer.toString(i)); + + // Terminate the loop once all free arguments have been consumed. + if (nextFreeArg == null) + { + break; + } + + // Split it on the =, strip any whitespace and set it as a system property. + String[] nameValuePair = nextFreeArg.split("="); + + if (nameValuePair.length == 2) + { + result.setProperty(nameValuePair[0], nameValuePair[1]); + } + } + + return result; + } + + /** + * Checks the format of an argument to an option against its specified regular head format if one has + * been set. Any errors are added to the list of parsing errors. + * + * @param optionInfo The command line option information for the option which is havings its argument checked. + * @param matchedArg The string argument to the option. + */ + private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg) + { + // Check if this option enforces a format for its argument. + if (optionInfo.argumentFormatRegexp != null) + { + Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp); + Matcher argumentMatcher = pattern.matcher(matchedArg); + + // Check if the argument does not meet its required format. + if (!argumentMatcher.matches()) + { + // Create an error for this badly formed argument. + parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n"); + } + } + } + + /** + * Holds information about a command line options. This includes what its name is, whether or not it is a flag, + * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its + * regular head format is. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Hold details of a command line option. + *
          + * + * @author Rupert Smith + */ + protected class CommandLineOption + { + /** Holds the text for the flag to match this argument with. */ + public String option = null; + + /** Holds a string describing how to use this command line argument. */ + public String argument = null; + + /** Flag that determines whether or not this command line argument can take arguments. */ + public boolean expectsArgs = false; + + /** Holds a short comment describing what this command line argument is for. */ + public String comment = null; + + /** Flag that determines whether or not this is an mandatory command line argument. */ + public boolean mandatory = false; + + /** A regular head describing what format the argument to this option muist have. */ + public String argumentFormatRegexp = null; + + /** + * Create a command line option object that holds specific information about a command line option. + * + * @param option The text that matches the option. + * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false. + * @param comment A comment explaining how to use this option. + * @param argument A short reminder of the format of the argument to this option/ + * @param mandatory Set to true if this option is mandatory. + * @param formatRegexp The regular head that the argument to this option must meet to be valid. + */ + public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory, + String formatRegexp) + { + this.option = option; + this.expectsArgs = expectsArgs; + this.comment = comment; + this.argument = argument; + this.mandatory = mandatory; + this.argumentFormatRegexp = formatRegexp; + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java index cabbf7869a..14de96d165 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java @@ -1,494 +1,494 @@ -/* - * - * 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.junit.extensions.util; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Properties; - -/** - * ContextualProperties is an extension of {@link java.util.Properties} that automatically selects properties based on an - * environment parameter (defined by the system property {@link #ENV_SYS_PROPERTY}), the name of a class, plus a modifier - * (which can be used to name a method of a class) and a property key. It also supports the definition of arrays of - * property values using indexes. The properties are searched in the following order until a match is found: - * - *

            - *
          1. environment + class name with package name + modifier + key - *
          2. environment + class name with package name + key - *
          3. environment + key - *
          4. class name with package name + modifier + key - *
          5. class name with package name + key - *
          6. key - *
          - * - *

          To create arrays of property values add index numbers onto the end of the property keys. An array of string values - * will be created with the elements of the array set to the value of the property at the matching index. Ideally the - * index values will be contiguous, starting at 0. This does not need to be the case however. If an array definition - * begins at index n, and ends at index m, Then an array big enough to hold m + 1 elements will be created and populated - * with values from n to m. Values before n and any missing values between n and m will be null in the array. - * - *

          To give an example, suppose you have two different environments 'DEVELOPMENT' and 'PRODUCTION' and they each need - * the same properties but set to different values for each environment and some properties the same in both, you could - * create a properties file like: - * - *

          - * # Project configuration properties file.
          - *
          - * # These properties are environment specific.
          - * DEVELOPMENT.debug=true
          - * PRODUCTION.debug=false
          - *
          - * # Always debug MyClass in all environments but not the myMethod method.
          - * MyClass.debug=true
          - * MyClass.myMethod.debug=false
          - *
          - * # Set up an array of my ten favourite animals. Leave elements 3 to 8 as null as I haven't decided on them yet.
          - * animals.0=cat
          - * animals.1=dog
          - * animals.2=elephant
          - * animals.9=lion
          - *
          - * # This is a default value that will be used when the environment is not known.
          - * debug=false
          - *
          - * - *

          The most specific definition of a property is searched for first moving out to the most general. This allows - * general property defaults to be set and then overiden for specific uses by some classes and modifiers. - * - *

          A ContextualProperties object can be loaded in the same way as a java.utils.Properties. A recommended way to do - * this that does not assume that the properties file is a file (it could be in a jar) is to load the properties from the - * url for the resource lookup up on the classpath: - * - *

          - * Properties configProperties = new ContextualProperties();
          - * configProperties.load(this.getClass().getClassLoader().getResourceAsStream("config.properties"));
          - *
          - * - *

          EnvironmentProperties will load the 'DEVELOPMENT.debug' property or 'PROUCTION.debug' property based on the setting - * of the system environment property. If a matching property for the environment cannot be found then the simple property - * name without the environment pre-pended onto it will be used instead. This 'use of default environments' behaviour is - * turned on initially but it can be disabled by calling the {@link #useDefaultEnvironments} method. - * - *

          When a property matching a key cannot be found then the property accessor methods will always return null. If a - * default value for a property exists but the 'use of default environments' behavious prevents it being used then the - * accessor methods will return null. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Automatically select properties dependant on environment, class name and modifier as well as property key. - *
          Convert indexed properties into arrays. - *
          - * - * @author Rupert Smith - */ -public class ContextualProperties extends ParsedProperties -{ - /** The name of the system property that is used to define the environment. */ - public static final String ENV_SYS_PROPERTY = "environment"; - - /** - *

          Holds the iteration count down order. - * - *

          If e = 4, b = 2, m = 1 then the iteration order or i is 7,6,4 and then if using environment defaults 3,2,0 - * where the accessor key is: - * (i & e != 0 ? environment : "") + (i & b != 0 ? base : "") + (1 + m != 0 ? modifier : "") + key - * - *

          In other words the presence or otherwise of the three least significant bits when counting down from 7 - * specifies which of the environment, base and modifier are to be included in the key where the environment, base - * and modifier stand for the bits in positions 2, 1 and 0. The numbers 5 and 1 are missed out of the count because - * they stand for the case where the modifier is used without the base which is not done. - */ - private static final int[] ORDER = new int[] { 7, 6, 4, 3, 2, 0 }; - - /** - * Defines the point in the iteration count order below which the 'use environment defaults' feature is being used. - */ - private static final int ENVIRONMENT_DEFAULTS_CUTOFF = 4; - - /** Defines the bit representation for the environment in the key ordering. See {@link #ORDER}. */ - private static final int E = 4; - - /** Defines the bit representation for the base in the key ordering. See {@link #ORDER}. */ - private static final int B = 2; - - /** Defines the bit representation for the modifier in the key ordering. See {@link #ORDER}. */ - private static final int M = 1; - - /** Used to hold the value of the environment system property. */ - private String environment; - - /** Used to indicate that the 'use of defaults' behaviour should be used. */ - private boolean useDefaults = true; - - /** Used to hold all the array properties. This is a mapping from property names to ArrayLists of Strings. */ - protected Map arrayProperties = new HashMap(); - - /** - * Default constructor that builds a ContextualProperties that uses environment defaults. - */ - public ContextualProperties() - { - super(); - - // Keep the value of the system environment property. - environment = System.getProperty(ENV_SYS_PROPERTY); - } - - /** - * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties. - * - * @param props The properties to initialize this with. - */ - public ContextualProperties(Properties props) - { - super(props); - - // Keep the value of the system environment property. - environment = System.getProperty(ENV_SYS_PROPERTY); - - // Extract any array properties as arrays. - createArrayProperties(); - } - - /** - * Parses an input stream as properties. - * - * @param inStream The input stream to read the properties from. - * - * @exception IOException If there is an IO error during reading from the input stream. - */ - public void load(InputStream inStream) throws IOException - { - super.load(inStream); - - // Extract any array properties as arrays. - createArrayProperties(); - } - - /** - * Tells this environment aware properties object whether it should use default environment properties without a - * pre-pended environment when a property for the current environment cannot be found. - * - * @param flag True to use defaults, false to not use defaults. - */ - public void useDefaultEnvironments(boolean flag) - { - useDefaults = flag; - } - - /** - * Looks up a property value relative to the environment, callers class and method. The default environment will be - * checked for a matching property if defaults are being used. In order to work out the callers class and method this - * method throws an exception and then searches one level up its stack frames. - * - * @param key The property key. - * - * @return The value of this property searching from the most specific definition (environment, class, method, key) - * to the most general (key only), unless use of default environments is turned off in which case the most general - * proeprty searched is (environment, key). - */ - public String getProperty(String key) - { - // Try to get the callers class name and method name by examing the stack. - String className = null; - String methodName = null; - - // Java 1.4 onwards only. - /*try - { - throw new Exception(); - } - catch (Exception e) - { - StackTraceElement[] stack = e.getStackTrace(); - - // Check that the stack trace contains at least two elements, one for this method and one for the caller. - if (stack.length >= 2) - { - className = stack[1].getClassName(); - methodName = stack[1].getMethodName(); - } - }*/ - - // Java 1.5 onwards only. - StackTraceElement[] stack = Thread.currentThread().getStackTrace(); - - // Check that the stack trace contains at least two elements, one for this method and one for the caller. - if (stack.length >= 2) - { - className = stack[1].getClassName(); - methodName = stack[1].getMethodName(); - } - - // Java 1.3 and before? Not sure, some horrible thing that parses the text spat out by printStackTrace? - - return getProperty(className, methodName, key); - } - - /** - * Looks up a property value relative to the environment, base class and modifier. The default environment will be - * checked for a matching property if defaults are being used. - * - * @param base An object of the class to retrieve properties relative to. - * @param modifier The modifier (which may stand for a method of the class). - * @param key The property key. - * - * @return The value of this property searching from the most specific definition (environment, class, modifier, key) - * to the most general (key only), unless use of default environments is turned off in which case the most general - * property searched is (environment, key). - */ - public String getProperty(Object base, String modifier, String key) - { - return getProperty(base.getClass().getName(), modifier, key); - } - - /** - * Looks up a property value relative to the environment, base class and modifier. The default environment will be - * checked for a matching property if defaults are being used. - * - * @param base The name of the class to retrieve properties relative to. - * @param modifier The modifier (which may stand for a method of the class). - * @param key The property key. - * - * @return The value of this property searching from the most specific definition (environment, class, modifier, key) - * to the most general (key only), unless use of default environments is turned off in which case the most general - * property searched is (environment, key). - */ - public String getProperty(String base, String modifier, String key) - { - String result = null; - - // Loop over the key orderings, from the most specific to the most general, until a matching value is found. - for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();) - { - String nextKey = (String) i.next(); - - result = super.getProperty(nextKey); - - if (result != null) - { - break; - } - } - - return result; - } - - /** - * Looks up an array property value relative to the environment, callers class and method. The default environment - * will be checked for a matching array property if defaults are being used. In order to work out the callers class - * and method this method throws an exception and then searches one level up its stack frames. - * - * @param key The property key. - * - * @return The array value of this indexed property searching from the most specific definition (environment, class, - * method, key) to the most general (key only), unless use of default environments is turned off in which - * case the most general proeprty searched is (environment, key). - */ - public String[] getProperties(String key) - { - // Try to get the callers class name and method name by throwing an exception an searching the stack frames. - String className = null; - String methodName = null; - - /* Java 1.4 onwards only. - try { - throw new Exception(); - } catch (Exception e) { - StackTraceElement[] stack = e.getStackTrace(); - // Check that the stack trace contains at least two elements, one for this method and one for the caller. - if (stack.length >= 2) { - className = stack[1].getClassName(); - methodName = stack[1].getMethodName(); - } - }*/ - return getProperties(className, methodName, key); - } - - /** - * Looks up an array property value relative to the environment, base class and modifier. The default environment will - * be checked for a matching array property if defaults are being used. - * - * @param base An object of the class to retrieve properties relative to. - * @param modifier The modifier (which may stand for a method of the class). - * @param key The property key. - * - * @return The array value of this indexed property searching from the most specific definition (environment, class, - * modifier, key) to the most general (key only), unless use of default environments is turned off in which - * case the most general proeprty searched is (environment, key). - */ - public String[] getProperties(Object base, String modifier, String key) - { - return getProperties(base.getClass().getName(), modifier, key); - } - - /** - * Looks up an array property value relative to the environment, base class and modifier. The default environment will - * be checked for a matching array property if defaults are being used. - * - * @param base The name of the class to retrieve properties relative to. - * @param modifier The modifier (which may stand for a method of the class). - * @param key The property key. - * - * @return The array value of this indexed property searching from the most specific definition (environment, class, - * modifier, key) to the most general (key only), unless use of default environments is turned off in which - * case the most general property searched is (environment, key). - */ - public String[] getProperties(String base, String modifier, String key) - { - String[] result = null; - - // Loop over the key orderings, from the most specific to the most general, until a matching value is found. - for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();) - { - String nextKey = (String) i.next(); - ArrayList arrayList = (ArrayList) arrayProperties.get(nextKey); - - if (arrayList != null) - { - result = (String[]) arrayList.toArray(new String[] {}); - - break; - } - } - - return result; - } - - /** - * For a given environment, base, modifier and key and setting of the use of default environments feature this - * generates an iterator that walks over the order in which to try and access properties. - * - *

          See the {@link #ORDER} constant for an explanation of how the key ordering is generated. - * - * @param base The name of the class to retrieve properties relative to. - * @param modifier The modifier (which may stand for a method of the class). - * @param key The property key. - * - * @return An Iterator over String keys defining the order in which properties should be accessed. - */ - protected Iterator getKeyIterator(final String base, final String modifier, final String key) - { - return new Iterator() - { - // The key ordering count always begins at the start of the ORDER array. - private int i = 0; - - public boolean hasNext() - { - return (useDefaults ? ((i < ORDER.length) && (ORDER[i] > ENVIRONMENT_DEFAULTS_CUTOFF)) - : (i < ORDER.length)); - } - - public Object next() - { - // Check that there is a next element and return null if not. - if (!hasNext()) - { - return null; - } - - // Get the next ordering count. - int o = ORDER[i]; - - // Do bit matching on the count to choose which elements to include in the key. - String result = - (((o & E) != 0) ? (environment + ".") : "") + (((o & B) != 0) ? (base + ".") : "") - + (((o & M) != 0) ? (modifier + ".") : "") + key; - - // Increment the iterator to get the next key on the next call. - i++; - - return result; - } - - public void remove() - { - // This method is not supported. - throw new UnsupportedOperationException("remove() is not supported on this key order iterator as " - + "the ordering cannot be changed"); - } - }; - } - - /** - * Scans all the properties in the parent Properties object and creates arrays for any array property definitions. - * - *

          Array properties are defined with indexes. For example: - * - *

          - * property.1=one
          - * property.2=two
          - * property.3=three
          - *
          - * - *

          Note that these properties will be stored as the 'empty string' or "" property array. - * - *

          - * .1=one
          - * 2=two
          - *
          - */ - protected void createArrayProperties() - { - // Scan through all defined properties. - for (Object o : keySet()) - { - String key = (String) o; - String value = super.getProperty(key); - - // Split the property key into everything before the last '.' and after it. - int lastDotIndex = key.lastIndexOf('.'); - String keyEnding = key.substring(lastDotIndex + 1, key.length()); - String keyStart = key.substring(0, (lastDotIndex == -1) ? 0 : lastDotIndex); - - // Check if the property key ends in an integer, in which case it is an array property. - int index = 0; - - try - { - index = Integer.parseInt(keyEnding); - } - // The ending is not an integer so its not an array. - catch (NumberFormatException e) - { - // Scan the next property. - continue; - } - - // Check if an array property already exists for this base name and create one if not. - ArrayList propArray = (ArrayList) arrayProperties.get(keyStart); - - if (propArray == null) - { - propArray = new ArrayList(); - arrayProperties.put(keyStart, propArray); - } - - // Add the new property value to the array property for the index. - propArray.set(index, value); - } - } -} +/* + * + * 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.junit.extensions.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +/** + * ContextualProperties is an extension of {@link java.util.Properties} that automatically selects properties based on an + * environment parameter (defined by the system property {@link #ENV_SYS_PROPERTY}), the name of a class, plus a modifier + * (which can be used to name a method of a class) and a property key. It also supports the definition of arrays of + * property values using indexes. The properties are searched in the following order until a match is found: + * + *

            + *
          1. environment + class name with package name + modifier + key + *
          2. environment + class name with package name + key + *
          3. environment + key + *
          4. class name with package name + modifier + key + *
          5. class name with package name + key + *
          6. key + *
          + * + *

          To create arrays of property values add index numbers onto the end of the property keys. An array of string values + * will be created with the elements of the array set to the value of the property at the matching index. Ideally the + * index values will be contiguous, starting at 0. This does not need to be the case however. If an array definition + * begins at index n, and ends at index m, Then an array big enough to hold m + 1 elements will be created and populated + * with values from n to m. Values before n and any missing values between n and m will be null in the array. + * + *

          To give an example, suppose you have two different environments 'DEVELOPMENT' and 'PRODUCTION' and they each need + * the same properties but set to different values for each environment and some properties the same in both, you could + * create a properties file like: + * + *

          + * # Project configuration properties file.
          + *
          + * # These properties are environment specific.
          + * DEVELOPMENT.debug=true
          + * PRODUCTION.debug=false
          + *
          + * # Always debug MyClass in all environments but not the myMethod method.
          + * MyClass.debug=true
          + * MyClass.myMethod.debug=false
          + *
          + * # Set up an array of my ten favourite animals. Leave elements 3 to 8 as null as I haven't decided on them yet.
          + * animals.0=cat
          + * animals.1=dog
          + * animals.2=elephant
          + * animals.9=lion
          + *
          + * # This is a default value that will be used when the environment is not known.
          + * debug=false
          + *
          + * + *

          The most specific definition of a property is searched for first moving out to the most general. This allows + * general property defaults to be set and then overiden for specific uses by some classes and modifiers. + * + *

          A ContextualProperties object can be loaded in the same way as a java.utils.Properties. A recommended way to do + * this that does not assume that the properties file is a file (it could be in a jar) is to load the properties from the + * url for the resource lookup up on the classpath: + * + *

          + * Properties configProperties = new ContextualProperties();
          + * configProperties.load(this.getClass().getClassLoader().getResourceAsStream("config.properties"));
          + *
          + * + *

          EnvironmentProperties will load the 'DEVELOPMENT.debug' property or 'PROUCTION.debug' property based on the setting + * of the system environment property. If a matching property for the environment cannot be found then the simple property + * name without the environment pre-pended onto it will be used instead. This 'use of default environments' behaviour is + * turned on initially but it can be disabled by calling the {@link #useDefaultEnvironments} method. + * + *

          When a property matching a key cannot be found then the property accessor methods will always return null. If a + * default value for a property exists but the 'use of default environments' behavious prevents it being used then the + * accessor methods will return null. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Automatically select properties dependant on environment, class name and modifier as well as property key. + *
          Convert indexed properties into arrays. + *
          + * + * @author Rupert Smith + */ +public class ContextualProperties extends ParsedProperties +{ + /** The name of the system property that is used to define the environment. */ + public static final String ENV_SYS_PROPERTY = "environment"; + + /** + *

          Holds the iteration count down order. + * + *

          If e = 4, b = 2, m = 1 then the iteration order or i is 7,6,4 and then if using environment defaults 3,2,0 + * where the accessor key is: + * (i & e != 0 ? environment : "") + (i & b != 0 ? base : "") + (1 + m != 0 ? modifier : "") + key + * + *

          In other words the presence or otherwise of the three least significant bits when counting down from 7 + * specifies which of the environment, base and modifier are to be included in the key where the environment, base + * and modifier stand for the bits in positions 2, 1 and 0. The numbers 5 and 1 are missed out of the count because + * they stand for the case where the modifier is used without the base which is not done. + */ + private static final int[] ORDER = new int[] { 7, 6, 4, 3, 2, 0 }; + + /** + * Defines the point in the iteration count order below which the 'use environment defaults' feature is being used. + */ + private static final int ENVIRONMENT_DEFAULTS_CUTOFF = 4; + + /** Defines the bit representation for the environment in the key ordering. See {@link #ORDER}. */ + private static final int E = 4; + + /** Defines the bit representation for the base in the key ordering. See {@link #ORDER}. */ + private static final int B = 2; + + /** Defines the bit representation for the modifier in the key ordering. See {@link #ORDER}. */ + private static final int M = 1; + + /** Used to hold the value of the environment system property. */ + private String environment; + + /** Used to indicate that the 'use of defaults' behaviour should be used. */ + private boolean useDefaults = true; + + /** Used to hold all the array properties. This is a mapping from property names to ArrayLists of Strings. */ + protected Map arrayProperties = new HashMap(); + + /** + * Default constructor that builds a ContextualProperties that uses environment defaults. + */ + public ContextualProperties() + { + super(); + + // Keep the value of the system environment property. + environment = System.getProperty(ENV_SYS_PROPERTY); + } + + /** + * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties. + * + * @param props The properties to initialize this with. + */ + public ContextualProperties(Properties props) + { + super(props); + + // Keep the value of the system environment property. + environment = System.getProperty(ENV_SYS_PROPERTY); + + // Extract any array properties as arrays. + createArrayProperties(); + } + + /** + * Parses an input stream as properties. + * + * @param inStream The input stream to read the properties from. + * + * @exception IOException If there is an IO error during reading from the input stream. + */ + public void load(InputStream inStream) throws IOException + { + super.load(inStream); + + // Extract any array properties as arrays. + createArrayProperties(); + } + + /** + * Tells this environment aware properties object whether it should use default environment properties without a + * pre-pended environment when a property for the current environment cannot be found. + * + * @param flag True to use defaults, false to not use defaults. + */ + public void useDefaultEnvironments(boolean flag) + { + useDefaults = flag; + } + + /** + * Looks up a property value relative to the environment, callers class and method. The default environment will be + * checked for a matching property if defaults are being used. In order to work out the callers class and method this + * method throws an exception and then searches one level up its stack frames. + * + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, method, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * proeprty searched is (environment, key). + */ + public String getProperty(String key) + { + // Try to get the callers class name and method name by examing the stack. + String className = null; + String methodName = null; + + // Java 1.4 onwards only. + /*try + { + throw new Exception(); + } + catch (Exception e) + { + StackTraceElement[] stack = e.getStackTrace(); + + // Check that the stack trace contains at least two elements, one for this method and one for the caller. + if (stack.length >= 2) + { + className = stack[1].getClassName(); + methodName = stack[1].getMethodName(); + } + }*/ + + // Java 1.5 onwards only. + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + + // Check that the stack trace contains at least two elements, one for this method and one for the caller. + if (stack.length >= 2) + { + className = stack[1].getClassName(); + methodName = stack[1].getMethodName(); + } + + // Java 1.3 and before? Not sure, some horrible thing that parses the text spat out by printStackTrace? + + return getProperty(className, methodName, key); + } + + /** + * Looks up a property value relative to the environment, base class and modifier. The default environment will be + * checked for a matching property if defaults are being used. + * + * @param base An object of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, modifier, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * property searched is (environment, key). + */ + public String getProperty(Object base, String modifier, String key) + { + return getProperty(base.getClass().getName(), modifier, key); + } + + /** + * Looks up a property value relative to the environment, base class and modifier. The default environment will be + * checked for a matching property if defaults are being used. + * + * @param base The name of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, modifier, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * property searched is (environment, key). + */ + public String getProperty(String base, String modifier, String key) + { + String result = null; + + // Loop over the key orderings, from the most specific to the most general, until a matching value is found. + for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();) + { + String nextKey = (String) i.next(); + + result = super.getProperty(nextKey); + + if (result != null) + { + break; + } + } + + return result; + } + + /** + * Looks up an array property value relative to the environment, callers class and method. The default environment + * will be checked for a matching array property if defaults are being used. In order to work out the callers class + * and method this method throws an exception and then searches one level up its stack frames. + * + * @param key The property key. + * + * @return The array value of this indexed property searching from the most specific definition (environment, class, + * method, key) to the most general (key only), unless use of default environments is turned off in which + * case the most general proeprty searched is (environment, key). + */ + public String[] getProperties(String key) + { + // Try to get the callers class name and method name by throwing an exception an searching the stack frames. + String className = null; + String methodName = null; + + /* Java 1.4 onwards only. + try { + throw new Exception(); + } catch (Exception e) { + StackTraceElement[] stack = e.getStackTrace(); + // Check that the stack trace contains at least two elements, one for this method and one for the caller. + if (stack.length >= 2) { + className = stack[1].getClassName(); + methodName = stack[1].getMethodName(); + } + }*/ + return getProperties(className, methodName, key); + } + + /** + * Looks up an array property value relative to the environment, base class and modifier. The default environment will + * be checked for a matching array property if defaults are being used. + * + * @param base An object of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The array value of this indexed property searching from the most specific definition (environment, class, + * modifier, key) to the most general (key only), unless use of default environments is turned off in which + * case the most general proeprty searched is (environment, key). + */ + public String[] getProperties(Object base, String modifier, String key) + { + return getProperties(base.getClass().getName(), modifier, key); + } + + /** + * Looks up an array property value relative to the environment, base class and modifier. The default environment will + * be checked for a matching array property if defaults are being used. + * + * @param base The name of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return The array value of this indexed property searching from the most specific definition (environment, class, + * modifier, key) to the most general (key only), unless use of default environments is turned off in which + * case the most general property searched is (environment, key). + */ + public String[] getProperties(String base, String modifier, String key) + { + String[] result = null; + + // Loop over the key orderings, from the most specific to the most general, until a matching value is found. + for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();) + { + String nextKey = (String) i.next(); + ArrayList arrayList = (ArrayList) arrayProperties.get(nextKey); + + if (arrayList != null) + { + result = (String[]) arrayList.toArray(new String[] {}); + + break; + } + } + + return result; + } + + /** + * For a given environment, base, modifier and key and setting of the use of default environments feature this + * generates an iterator that walks over the order in which to try and access properties. + * + *

          See the {@link #ORDER} constant for an explanation of how the key ordering is generated. + * + * @param base The name of the class to retrieve properties relative to. + * @param modifier The modifier (which may stand for a method of the class). + * @param key The property key. + * + * @return An Iterator over String keys defining the order in which properties should be accessed. + */ + protected Iterator getKeyIterator(final String base, final String modifier, final String key) + { + return new Iterator() + { + // The key ordering count always begins at the start of the ORDER array. + private int i = 0; + + public boolean hasNext() + { + return (useDefaults ? ((i < ORDER.length) && (ORDER[i] > ENVIRONMENT_DEFAULTS_CUTOFF)) + : (i < ORDER.length)); + } + + public Object next() + { + // Check that there is a next element and return null if not. + if (!hasNext()) + { + return null; + } + + // Get the next ordering count. + int o = ORDER[i]; + + // Do bit matching on the count to choose which elements to include in the key. + String result = + (((o & E) != 0) ? (environment + ".") : "") + (((o & B) != 0) ? (base + ".") : "") + + (((o & M) != 0) ? (modifier + ".") : "") + key; + + // Increment the iterator to get the next key on the next call. + i++; + + return result; + } + + public void remove() + { + // This method is not supported. + throw new UnsupportedOperationException("remove() is not supported on this key order iterator as " + + "the ordering cannot be changed"); + } + }; + } + + /** + * Scans all the properties in the parent Properties object and creates arrays for any array property definitions. + * + *

          Array properties are defined with indexes. For example: + * + *

          + * property.1=one
          + * property.2=two
          + * property.3=three
          + *
          + * + *

          Note that these properties will be stored as the 'empty string' or "" property array. + * + *

          + * .1=one
          + * 2=two
          + *
          + */ + protected void createArrayProperties() + { + // Scan through all defined properties. + for (Object o : keySet()) + { + String key = (String) o; + String value = super.getProperty(key); + + // Split the property key into everything before the last '.' and after it. + int lastDotIndex = key.lastIndexOf('.'); + String keyEnding = key.substring(lastDotIndex + 1, key.length()); + String keyStart = key.substring(0, (lastDotIndex == -1) ? 0 : lastDotIndex); + + // Check if the property key ends in an integer, in which case it is an array property. + int index = 0; + + try + { + index = Integer.parseInt(keyEnding); + } + // The ending is not an integer so its not an array. + catch (NumberFormatException e) + { + // Scan the next property. + continue; + } + + // Check if an array property already exists for this base name and create one if not. + ArrayList propArray = (ArrayList) arrayProperties.get(keyStart); + + if (propArray == null) + { + propArray = new ArrayList(); + arrayProperties.put(keyStart, propArray); + } + + // Add the new property value to the array property for the index. + propArray.set(index, value); + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java index 7a45632643..7c803294f4 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java @@ -1,428 +1,428 @@ -/* - * - * 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.junit.extensions.util; - -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Mathematical support methods for the toolkit. Caculating averages, variances, min/max for test latencies and - * generating linear/exponential sequences for test size/concurrency ramping up. - * - *

          The sequence specifications are of the form [lowest(, ...)(, highest)](,sample=s)(,exp), where round brackets - * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided - * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples. - * - *

          The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least - * one of the optional values must be present. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Generate a sequene of integers from a sequence specification. - *
          Parse an encoded duration into milliseconds. - *
          - * - * @author Rupert Smith - */ -public class MathUtils -{ - /** Used for debugging. */ - // private static final Logger log = Logger.getLogger(MathUtils.class); - - /** The sequence defintion matching regular expression. */ - public static final String SEQUENCE_REGEXP = "^(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$"; - - /** The regular expression that matches sequence definitions. */ - private static final Pattern SEQUENCE_PATTERN = Pattern.compile(SEQUENCE_REGEXP); - - /** The duration definition matching regular expression. */ - public static final String DURATION_REGEXP = "^(\\d+D)?(\\d+H)?(\\d+M)?(\\d+S)?$"; - - /** The regular expression that matches the duration expression. */ - public static final Pattern DURATION_PATTERN = Pattern.compile(DURATION_REGEXP); - - /** For matching name=value pairs. */ - public static final String NAME_VALUE_REGEXP = "^\\w+=\\w+$"; - - /** For matching name=[value1: value2: ...] variations. */ - public static final String NAME_VALUE_VARIATION_REGEXP = "^\\w+=\\[[\\w:]+\\]$"; - - /** For matching name=[n: ... :m](:sample=s)(:exp) sequences. */ - public static final String NAME_VALUE_SEQUENCE_REGEXP = "^\\w+=(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$"; - - /** The regular expression that matches name=value pairs and variations. */ - public static final Pattern NAME_VALUE_PATTERN = - Pattern.compile("(" + NAME_VALUE_REGEXP + ")|(" + NAME_VALUE_VARIATION_REGEXP + ")|(" + NAME_VALUE_SEQUENCE_REGEXP - + ")"); - - /** - * Runs a quick test of the sequence generation methods to confirm that they work as expected. - * - * @param args The command line parameters. - */ - public static void main(String[] args) - { - // Use the command line parser to evaluate the command line. - CommandLineParser commandLine = - new CommandLineParser( - new String[][] - { - { "s", "The sequence definition.", "[m:...:n](:sample=s)(:exp)", "true", MathUtils.SEQUENCE_REGEXP }, - { "d", "The duration definition.", "dDhHmMsS", "false", MathUtils.DURATION_REGEXP } - }); - - // Capture the command line arguments or display errors and correct usage and then exit. - ParsedProperties options = null; - - try - { - options = new ParsedProperties(commandLine.parseCommandLine(args)); - } - catch (IllegalArgumentException e) - { - System.out.println(commandLine.getErrors()); - System.out.println(commandLine.getUsage()); - System.exit(-1); - } - - // Extract the command line options. - String sequence = options.getProperty("s"); - String durationString = options.getProperty("d"); - - System.out.println("Sequence is: " + printArray(parseSequence(sequence))); - - if (durationString != null) - { - System.out.println("Duration is: " + parseDuration(durationString)); - } - } - - /** - * Given a start and end and a number of steps this method generates a sequence of evenly spaced integer - * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number - * of values in the sequence. The sequence returned may contain less than the specified number where the integer - * range between start and end is too small to contain that many. - * - *

          As the results are integers, they will not be perfectly evenly spaced but a best-fit. - * - * @param start The sequence start. - * @param end The sequence end. - * @param steps The number of steps. - * - * @return The sequence. - */ - public static int[] generateSequence(int start, int end, int steps) - { - // Check that there are at least two steps. - if (steps < 2) - { - throw new IllegalArgumentException("There must be at least 2 steps."); - } - - ArrayList result = new ArrayList(); - - // Calculate the sequence using floating point, then round into the results. - double fStart = start; - double fEnd = end; - double fCurrent = start; - - for (int i = 0; i < steps; i++) - { - fCurrent = (((fEnd - fStart) / (steps - 1)) * i) + fStart; - - roundAndAdd(result, fCurrent); - } - - // Return the results after converting to a primitive array. - return intListToPrimitiveArray(result); - } - - /** - * Given a start and end and a number of steps this method generates a sequence of expontentially spaced integer - * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number - * of values in the sequence. An exponentially spaced sequence is one where the ratio between any two consecutive - * numbers in the sequence remains constant. The sequence returned may contain less than the specified number where - * the difference between two consecutive values is too small (this is more likely at the start of the sequence, - * where the values are closer together). - * - *

          As the results are integers, they will not be perfectly exponentially spaced but a best-fit. - * - * @param start The sequence start. - * @param end The sequence end. - * @param steps The number of steps. - * - * @return The sequence. - */ - public static int[] generateExpSequence(int start, int end, int steps) - { - // Check that there are at least two steps. - if (steps < 2) - { - throw new IllegalArgumentException("There must be at least 2 steps."); - } - - ArrayList result = new ArrayList(); - - // Calculate the sequence using floating point, then round into the results. - double fStart = start; - double fEnd = end; - // float fCurrent = start; - double diff = fEnd - fStart; - double factor = java.lang.Math.pow(diff, (1.0f / (steps - 1))); - - for (int i = 0; i < steps; i++) - { - // This is a cheat to get the end exactly on and lose the accumulated rounding error. - if (i == (steps - 1)) - { - result.add(end); - } - else - { - roundAndAdd(result, fStart - 1.0f + java.lang.Math.pow(factor, i)); - } - } - - // Return the results after converting to a primitive array. - return intListToPrimitiveArray(result); - } - - /** - * Parses a string defintion of a sequence into an int array containing the sequence. The definition will conform - * to the regular expression: "^(\[[0-9,]+\])(,samples=[0-9]+)?(,exp)?$". This splits it into three parts, - * an array of integers, the optional sample count and the optional exponential flag. - * - * @param sequenceDef The sequence definition. - * - * @return The sequence as a fully expanded int array. - */ - public static int[] parseSequence(String sequenceDef) - { - // Match the sequence definition against the regular expression for sequences. - Matcher matcher = SEQUENCE_PATTERN.matcher(sequenceDef); - - // Check that the argument is of the right format accepted by this method. - if (!matcher.matches()) - { - throw new IllegalArgumentException("The sequence definition is not in the correct format."); - } - - // Get the total number of matching groups to see if either of the optional samples or exponential flag - // goups were set. - int numGroups = matcher.groupCount(); - - // Split the array of integers on commas. - String intArrayString = matcher.group(1); - - String[] intSplits = intArrayString.split("[:\\[\\]]"); - - int[] sequence = new int[intSplits.length - 1]; - - for (int i = 1; i < intSplits.length; i++) - { - sequence[i - 1] = Integer.parseInt(intSplits[i]); - } - - // Check for the optional samples count. - int samples = 0; - - if ((numGroups > 1) && (matcher.group(2) != null)) - { - String samplesGroup = matcher.group(2); - - String samplesString = samplesGroup.substring(",samples=".length()); - samples = Integer.parseInt(samplesString); - } - - // Check for the optional exponential flag. - boolean expFlag = false; - - if ((numGroups > 2) && (matcher.group(3) != null)) - { - expFlag = true; - } - - // If there is a sample count and 2 or more sequence values defined, then generate the sequence from the first - // and last sequence values. - if ((samples != 0) && (sequence.length >= 2)) - { - int start = sequence[0]; - int end = sequence[sequence.length - 1]; - - if (!expFlag) - { - sequence = generateSequence(start, end, samples); - } - else - { - sequence = generateExpSequence(start, end, samples); - } - } - - return sequence; - } - - /** - * Parses a duration defined as a string, giving a duration in days, hours, minutes and seconds into a number - * of milliseconds equal to that duration. - * - * @param duration The duration definition string. - * - * @return The duration in millliseconds. - */ - public static long parseDuration(String duration) - { - // Match the duration against the regular expression. - Matcher matcher = DURATION_PATTERN.matcher(duration); - - // Check that the argument is of the right format accepted by this method. - if (!matcher.matches()) - { - throw new IllegalArgumentException("The duration definition is not in the correct format."); - } - - // This accumulates the duration. - long result = 0; - - int numGroups = matcher.groupCount(); - - // Extract the days. - if (numGroups >= 1) - { - String daysString = matcher.group(1); - result += - (daysString == null) - ? 0 : (Long.parseLong(daysString.substring(0, daysString.length() - 1)) * 24 * 60 * 60 * 1000); - } - - // Extract the hours. - if (numGroups >= 2) - { - String hoursString = matcher.group(2); - result += - (hoursString == null) ? 0 - : (Long.parseLong(hoursString.substring(0, hoursString.length() - 1)) * 60 * 60 * 1000); - } - - // Extract the minutes. - if (numGroups >= 3) - { - String minutesString = matcher.group(3); - result += - (minutesString == null) - ? 0 : (Long.parseLong(minutesString.substring(0, minutesString.length() - 1)) * 60 * 1000); - } - - // Extract the seconds. - if (numGroups >= 4) - { - String secondsString = matcher.group(4); - result += - (secondsString == null) ? 0 : (Long.parseLong(secondsString.substring(0, secondsString.length() - 1)) * 1000); - } - - return result; - } - - /** - * Pretty prints an array of ints as a string. - * - * @param array The array to pretty print. - * - * @return The pretty printed string. - */ - public static String printArray(int[] array) - { - String result = "["; - for (int i = 0; i < array.length; i++) - { - result += array[i]; - result += (i < (array.length - 1)) ? ", " : ""; - } - - result += "]"; - - return result; - } - - /** - * Returns the maximum value in an array of integers. - * - * @param values The array to find the amx in. - * - * @return The max value. - */ - public static int maxInArray(int[] values) - { - if ((values == null) || (values.length == 0)) - { - throw new IllegalArgumentException("Cannot find the max of a null or empty array."); - } - - int max = values[0]; - - for (int value : values) - { - max = (max < value) ? value : max; - } - - return max; - } - - /** - * The #toArray methods of collections cannot be used with primitive arrays. This loops over and array list - * of Integers and outputs and array of int. - * - * @param result The array of Integers to convert. - * - * @return An array of int. - */ - private static int[] intListToPrimitiveArray(ArrayList result) - { - int[] resultArray = new int[result.size()]; - int index = 0; - for (int r : result) - { - resultArray[index] = result.get(index); - index++; - } - - return resultArray; - } - - /** - * Rounds the specified floating point value to the nearest integer and adds it to the specified list of - * integers, provided it is not already in the list. - * - * @param result The list of integers to add to. - * @param value The new candidate to round and add to the list. - */ - private static void roundAndAdd(ArrayList result, double value) - { - int roundedValue = (int) Math.round(value); - - if (!result.contains(roundedValue)) - { - result.add(roundedValue); - } - } -} +/* + * + * 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.junit.extensions.util; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Mathematical support methods for the toolkit. Caculating averages, variances, min/max for test latencies and + * generating linear/exponential sequences for test size/concurrency ramping up. + * + *

          The sequence specifications are of the form [lowest(, ...)(, highest)](,sample=s)(,exp), where round brackets + * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided + * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples. + * + *

          The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least + * one of the optional values must be present. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Generate a sequene of integers from a sequence specification. + *
          Parse an encoded duration into milliseconds. + *
          + * + * @author Rupert Smith + */ +public class MathUtils +{ + /** Used for debugging. */ + // private static final Logger log = Logger.getLogger(MathUtils.class); + + /** The sequence defintion matching regular expression. */ + public static final String SEQUENCE_REGEXP = "^(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$"; + + /** The regular expression that matches sequence definitions. */ + private static final Pattern SEQUENCE_PATTERN = Pattern.compile(SEQUENCE_REGEXP); + + /** The duration definition matching regular expression. */ + public static final String DURATION_REGEXP = "^(\\d+D)?(\\d+H)?(\\d+M)?(\\d+S)?$"; + + /** The regular expression that matches the duration expression. */ + public static final Pattern DURATION_PATTERN = Pattern.compile(DURATION_REGEXP); + + /** For matching name=value pairs. */ + public static final String NAME_VALUE_REGEXP = "^\\w+=\\w+$"; + + /** For matching name=[value1: value2: ...] variations. */ + public static final String NAME_VALUE_VARIATION_REGEXP = "^\\w+=\\[[\\w:]+\\]$"; + + /** For matching name=[n: ... :m](:sample=s)(:exp) sequences. */ + public static final String NAME_VALUE_SEQUENCE_REGEXP = "^\\w+=(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$"; + + /** The regular expression that matches name=value pairs and variations. */ + public static final Pattern NAME_VALUE_PATTERN = + Pattern.compile("(" + NAME_VALUE_REGEXP + ")|(" + NAME_VALUE_VARIATION_REGEXP + ")|(" + NAME_VALUE_SEQUENCE_REGEXP + + ")"); + + /** + * Runs a quick test of the sequence generation methods to confirm that they work as expected. + * + * @param args The command line parameters. + */ + public static void main(String[] args) + { + // Use the command line parser to evaluate the command line. + CommandLineParser commandLine = + new CommandLineParser( + new String[][] + { + { "s", "The sequence definition.", "[m:...:n](:sample=s)(:exp)", "true", MathUtils.SEQUENCE_REGEXP }, + { "d", "The duration definition.", "dDhHmMsS", "false", MathUtils.DURATION_REGEXP } + }); + + // Capture the command line arguments or display errors and correct usage and then exit. + ParsedProperties options = null; + + try + { + options = new ParsedProperties(commandLine.parseCommandLine(args)); + } + catch (IllegalArgumentException e) + { + System.out.println(commandLine.getErrors()); + System.out.println(commandLine.getUsage()); + System.exit(-1); + } + + // Extract the command line options. + String sequence = options.getProperty("s"); + String durationString = options.getProperty("d"); + + System.out.println("Sequence is: " + printArray(parseSequence(sequence))); + + if (durationString != null) + { + System.out.println("Duration is: " + parseDuration(durationString)); + } + } + + /** + * Given a start and end and a number of steps this method generates a sequence of evenly spaced integer + * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number + * of values in the sequence. The sequence returned may contain less than the specified number where the integer + * range between start and end is too small to contain that many. + * + *

          As the results are integers, they will not be perfectly evenly spaced but a best-fit. + * + * @param start The sequence start. + * @param end The sequence end. + * @param steps The number of steps. + * + * @return The sequence. + */ + public static int[] generateSequence(int start, int end, int steps) + { + // Check that there are at least two steps. + if (steps < 2) + { + throw new IllegalArgumentException("There must be at least 2 steps."); + } + + ArrayList result = new ArrayList(); + + // Calculate the sequence using floating point, then round into the results. + double fStart = start; + double fEnd = end; + double fCurrent = start; + + for (int i = 0; i < steps; i++) + { + fCurrent = (((fEnd - fStart) / (steps - 1)) * i) + fStart; + + roundAndAdd(result, fCurrent); + } + + // Return the results after converting to a primitive array. + return intListToPrimitiveArray(result); + } + + /** + * Given a start and end and a number of steps this method generates a sequence of expontentially spaced integer + * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number + * of values in the sequence. An exponentially spaced sequence is one where the ratio between any two consecutive + * numbers in the sequence remains constant. The sequence returned may contain less than the specified number where + * the difference between two consecutive values is too small (this is more likely at the start of the sequence, + * where the values are closer together). + * + *

          As the results are integers, they will not be perfectly exponentially spaced but a best-fit. + * + * @param start The sequence start. + * @param end The sequence end. + * @param steps The number of steps. + * + * @return The sequence. + */ + public static int[] generateExpSequence(int start, int end, int steps) + { + // Check that there are at least two steps. + if (steps < 2) + { + throw new IllegalArgumentException("There must be at least 2 steps."); + } + + ArrayList result = new ArrayList(); + + // Calculate the sequence using floating point, then round into the results. + double fStart = start; + double fEnd = end; + // float fCurrent = start; + double diff = fEnd - fStart; + double factor = java.lang.Math.pow(diff, (1.0f / (steps - 1))); + + for (int i = 0; i < steps; i++) + { + // This is a cheat to get the end exactly on and lose the accumulated rounding error. + if (i == (steps - 1)) + { + result.add(end); + } + else + { + roundAndAdd(result, fStart - 1.0f + java.lang.Math.pow(factor, i)); + } + } + + // Return the results after converting to a primitive array. + return intListToPrimitiveArray(result); + } + + /** + * Parses a string defintion of a sequence into an int array containing the sequence. The definition will conform + * to the regular expression: "^(\[[0-9,]+\])(,samples=[0-9]+)?(,exp)?$". This splits it into three parts, + * an array of integers, the optional sample count and the optional exponential flag. + * + * @param sequenceDef The sequence definition. + * + * @return The sequence as a fully expanded int array. + */ + public static int[] parseSequence(String sequenceDef) + { + // Match the sequence definition against the regular expression for sequences. + Matcher matcher = SEQUENCE_PATTERN.matcher(sequenceDef); + + // Check that the argument is of the right format accepted by this method. + if (!matcher.matches()) + { + throw new IllegalArgumentException("The sequence definition is not in the correct format."); + } + + // Get the total number of matching groups to see if either of the optional samples or exponential flag + // goups were set. + int numGroups = matcher.groupCount(); + + // Split the array of integers on commas. + String intArrayString = matcher.group(1); + + String[] intSplits = intArrayString.split("[:\\[\\]]"); + + int[] sequence = new int[intSplits.length - 1]; + + for (int i = 1; i < intSplits.length; i++) + { + sequence[i - 1] = Integer.parseInt(intSplits[i]); + } + + // Check for the optional samples count. + int samples = 0; + + if ((numGroups > 1) && (matcher.group(2) != null)) + { + String samplesGroup = matcher.group(2); + + String samplesString = samplesGroup.substring(",samples=".length()); + samples = Integer.parseInt(samplesString); + } + + // Check for the optional exponential flag. + boolean expFlag = false; + + if ((numGroups > 2) && (matcher.group(3) != null)) + { + expFlag = true; + } + + // If there is a sample count and 2 or more sequence values defined, then generate the sequence from the first + // and last sequence values. + if ((samples != 0) && (sequence.length >= 2)) + { + int start = sequence[0]; + int end = sequence[sequence.length - 1]; + + if (!expFlag) + { + sequence = generateSequence(start, end, samples); + } + else + { + sequence = generateExpSequence(start, end, samples); + } + } + + return sequence; + } + + /** + * Parses a duration defined as a string, giving a duration in days, hours, minutes and seconds into a number + * of milliseconds equal to that duration. + * + * @param duration The duration definition string. + * + * @return The duration in millliseconds. + */ + public static long parseDuration(String duration) + { + // Match the duration against the regular expression. + Matcher matcher = DURATION_PATTERN.matcher(duration); + + // Check that the argument is of the right format accepted by this method. + if (!matcher.matches()) + { + throw new IllegalArgumentException("The duration definition is not in the correct format."); + } + + // This accumulates the duration. + long result = 0; + + int numGroups = matcher.groupCount(); + + // Extract the days. + if (numGroups >= 1) + { + String daysString = matcher.group(1); + result += + (daysString == null) + ? 0 : (Long.parseLong(daysString.substring(0, daysString.length() - 1)) * 24 * 60 * 60 * 1000); + } + + // Extract the hours. + if (numGroups >= 2) + { + String hoursString = matcher.group(2); + result += + (hoursString == null) ? 0 + : (Long.parseLong(hoursString.substring(0, hoursString.length() - 1)) * 60 * 60 * 1000); + } + + // Extract the minutes. + if (numGroups >= 3) + { + String minutesString = matcher.group(3); + result += + (minutesString == null) + ? 0 : (Long.parseLong(minutesString.substring(0, minutesString.length() - 1)) * 60 * 1000); + } + + // Extract the seconds. + if (numGroups >= 4) + { + String secondsString = matcher.group(4); + result += + (secondsString == null) ? 0 : (Long.parseLong(secondsString.substring(0, secondsString.length() - 1)) * 1000); + } + + return result; + } + + /** + * Pretty prints an array of ints as a string. + * + * @param array The array to pretty print. + * + * @return The pretty printed string. + */ + public static String printArray(int[] array) + { + String result = "["; + for (int i = 0; i < array.length; i++) + { + result += array[i]; + result += (i < (array.length - 1)) ? ", " : ""; + } + + result += "]"; + + return result; + } + + /** + * Returns the maximum value in an array of integers. + * + * @param values The array to find the amx in. + * + * @return The max value. + */ + public static int maxInArray(int[] values) + { + if ((values == null) || (values.length == 0)) + { + throw new IllegalArgumentException("Cannot find the max of a null or empty array."); + } + + int max = values[0]; + + for (int value : values) + { + max = (max < value) ? value : max; + } + + return max; + } + + /** + * The #toArray methods of collections cannot be used with primitive arrays. This loops over and array list + * of Integers and outputs and array of int. + * + * @param result The array of Integers to convert. + * + * @return An array of int. + */ + private static int[] intListToPrimitiveArray(ArrayList result) + { + int[] resultArray = new int[result.size()]; + int index = 0; + for (int r : result) + { + resultArray[index] = result.get(index); + index++; + } + + return resultArray; + } + + /** + * Rounds the specified floating point value to the nearest integer and adds it to the specified list of + * integers, provided it is not already in the list. + * + * @param result The list of integers to add to. + * @param value The new candidate to round and add to the list. + */ + private static void roundAndAdd(ArrayList result, double value) + { + int roundedValue = (int) Math.round(value); + + if (!result.contains(roundedValue)) + { + result.add(roundedValue); + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java index 59c8cfbd3a..1cc6757675 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java @@ -1,390 +1,390 @@ -/* - * - * 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.junit.extensions.util; - -import java.util.Properties; - -/** - * ParsedProperties extends the basic Properties class with methods to extract properties, not as strings but as strings - * parsed into basic types. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - * - * @author Rupert Smith - */ -public class ParsedProperties extends Properties -{ - /** - * Creates an empty ParsedProperties. - */ - public ParsedProperties() - { - super(); - } - - /** - * Creates a ParsedProperties initialized with the specified properties. - * - * @param props The properties to initialize this with. - */ - public ParsedProperties(Properties props) - { - super(props); - } - - /** - * Helper method for setting system properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public static boolean setSysPropertyIfNull(String propname, boolean value) - { - return Boolean.parseBoolean(setSysPropertyIfNull(propname, Boolean.toString(value))); - } - - /** - * Helper method for setting system properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public static short setSysPropertyIfNull(String propname, short value) - { - return Short.parseShort(setSysPropertyIfNull(propname, Short.toString(value))); - } - - /** - * Helper method for setting system properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public static int setSysPropertyIfNull(String propname, int value) - { - return Integer.parseInt(setSysPropertyIfNull(propname, Integer.toString(value))); - } - - /** - * Helper method for setting system properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public static long setSysPropertyIfNull(String propname, long value) - { - return Long.parseLong(setSysPropertyIfNull(propname, Long.toString(value))); - } - - /** - * Helper method for setting system properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public static float setSysPropertyIfNull(String propname, float value) - { - return Float.parseFloat(setSysPropertyIfNull(propname, Float.toString(value))); - } - - /** - * Helper method for setting system properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public static double setSysPropertyIfNull(String propname, double value) - { - return Double.parseDouble(setSysPropertyIfNull(propname, Double.toString(value))); - } - - /** - * Helper method for setting system properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the system property after this method call. - */ - public static String setSysPropertyIfNull(String propname, String value) - { - String property = System.getProperty(propname); - - if (property == null) - { - System.setProperty(propname, value); - - return value; - } - else - { - return property; - } - } - - /** - * Helper method for setting properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public boolean setPropertyIfNull(String propname, boolean value) - { - return Boolean.parseBoolean(setPropertyIfNull(propname, Boolean.toString(value))); - } - - /** - * Helper method for setting properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public short setPropertyIfNull(String propname, short value) - { - return Short.parseShort(setPropertyIfNull(propname, Short.toString(value))); - } - - /** - * Helper method for setting properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public int setPropertyIfNull(String propname, int value) - { - return Integer.parseInt(setPropertyIfNull(propname, Integer.toString(value))); - } - - /** - * Helper method for setting properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public long setPropertyIfNull(String propname, long value) - { - return Long.parseLong(setPropertyIfNull(propname, Long.toString(value))); - } - - /** - * Helper method for setting properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public float setPropertyIfNull(String propname, float value) - { - return Float.parseFloat(setPropertyIfNull(propname, Float.toString(value))); - } - - /** - * Helper method for setting properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public double setPropertyIfNull(String propname, double value) - { - return Double.parseDouble(setPropertyIfNull(propname, Double.toString(value))); - } - - /** - * Helper method for setting properties to defaults when they are not already set. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public String setPropertyIfNull(String propname, String value) - { - String property = super.getProperty(propname); - - if (property == null) - { - super.setProperty(propname, value); - - return value; - } - else - { - return property; - } - } - - /** - * Helper method for setting properties. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public boolean setProperty(String propname, boolean value) - { - setProperty(propname, Boolean.toString(value)); - - return value; - } - - /** - * Helper method for setting properties. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public short setProperty(String propname, short value) - { - setProperty(propname, Short.toString(value)); - - return value; - } - - /** - * Helper method for setting properties. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public int setProperty(String propname, int value) - { - setProperty(propname, Integer.toString(value)); - - return value; - } - - /** - * Helper method for setting properties. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public long setProperty(String propname, long value) - { - setProperty(propname, Long.toString(value)); - - return value; - } - - /** - * Helper method for setting properties. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public float setProperty(String propname, float value) - { - setProperty(propname, Float.toString(value)); - - return value; - } - - /** - * Helper method for setting properties. - * - * @param propname The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property after this method call. - */ - public double setProperty(String propname, double value) - { - setProperty(propname, Double.toString(value)); - - return value; - } - - /** - * Parses a property as a boolean. - * - * @param propName The property. - * - * @return The property as a boolean, or false if it does not exist. - */ - public boolean getPropertyAsBoolean(String propName) - { - String prop = getProperty(propName); - - return (prop != null) && Boolean.parseBoolean(prop); - } - - /** - * Parses a property as an integer. - * - * @param propName The property. - * - * @return The property as a integer, or null if it does not exist. - */ - public Integer getPropertyAsInteger(String propName) - { - String prop = getProperty(propName); - - return (prop != null) ? new Integer(prop) : null; - } - - /** - * Parses a property as a long. - * - * @param propName The property. - * - * @return The property as a long, or null if it does not exist. - */ - public Long getPropertyAsLong(String propName) - { - String prop = getProperty(propName); - - return (prop != null) ? new Long(prop) : null; - } -} +/* + * + * 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.junit.extensions.util; + +import java.util.Properties; + +/** + * ParsedProperties extends the basic Properties class with methods to extract properties, not as strings but as strings + * parsed into basic types. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + * + * @author Rupert Smith + */ +public class ParsedProperties extends Properties +{ + /** + * Creates an empty ParsedProperties. + */ + public ParsedProperties() + { + super(); + } + + /** + * Creates a ParsedProperties initialized with the specified properties. + * + * @param props The properties to initialize this with. + */ + public ParsedProperties(Properties props) + { + super(props); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static boolean setSysPropertyIfNull(String propname, boolean value) + { + return Boolean.parseBoolean(setSysPropertyIfNull(propname, Boolean.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static short setSysPropertyIfNull(String propname, short value) + { + return Short.parseShort(setSysPropertyIfNull(propname, Short.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static int setSysPropertyIfNull(String propname, int value) + { + return Integer.parseInt(setSysPropertyIfNull(propname, Integer.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static long setSysPropertyIfNull(String propname, long value) + { + return Long.parseLong(setSysPropertyIfNull(propname, Long.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static float setSysPropertyIfNull(String propname, float value) + { + return Float.parseFloat(setSysPropertyIfNull(propname, Float.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public static double setSysPropertyIfNull(String propname, double value) + { + return Double.parseDouble(setSysPropertyIfNull(propname, Double.toString(value))); + } + + /** + * Helper method for setting system properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the system property after this method call. + */ + public static String setSysPropertyIfNull(String propname, String value) + { + String property = System.getProperty(propname); + + if (property == null) + { + System.setProperty(propname, value); + + return value; + } + else + { + return property; + } + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public boolean setPropertyIfNull(String propname, boolean value) + { + return Boolean.parseBoolean(setPropertyIfNull(propname, Boolean.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public short setPropertyIfNull(String propname, short value) + { + return Short.parseShort(setPropertyIfNull(propname, Short.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public int setPropertyIfNull(String propname, int value) + { + return Integer.parseInt(setPropertyIfNull(propname, Integer.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public long setPropertyIfNull(String propname, long value) + { + return Long.parseLong(setPropertyIfNull(propname, Long.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public float setPropertyIfNull(String propname, float value) + { + return Float.parseFloat(setPropertyIfNull(propname, Float.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public double setPropertyIfNull(String propname, double value) + { + return Double.parseDouble(setPropertyIfNull(propname, Double.toString(value))); + } + + /** + * Helper method for setting properties to defaults when they are not already set. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public String setPropertyIfNull(String propname, String value) + { + String property = super.getProperty(propname); + + if (property == null) + { + super.setProperty(propname, value); + + return value; + } + else + { + return property; + } + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public boolean setProperty(String propname, boolean value) + { + setProperty(propname, Boolean.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public short setProperty(String propname, short value) + { + setProperty(propname, Short.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public int setProperty(String propname, int value) + { + setProperty(propname, Integer.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public long setProperty(String propname, long value) + { + setProperty(propname, Long.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public float setProperty(String propname, float value) + { + setProperty(propname, Float.toString(value)); + + return value; + } + + /** + * Helper method for setting properties. + * + * @param propname The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property after this method call. + */ + public double setProperty(String propname, double value) + { + setProperty(propname, Double.toString(value)); + + return value; + } + + /** + * Parses a property as a boolean. + * + * @param propName The property. + * + * @return The property as a boolean, or false if it does not exist. + */ + public boolean getPropertyAsBoolean(String propName) + { + String prop = getProperty(propName); + + return (prop != null) && Boolean.parseBoolean(prop); + } + + /** + * Parses a property as an integer. + * + * @param propName The property. + * + * @return The property as a integer, or null if it does not exist. + */ + public Integer getPropertyAsInteger(String propName) + { + String prop = getProperty(propName); + + return (prop != null) ? new Integer(prop) : null; + } + + /** + * Parses a property as a long. + * + * @param propName The property. + * + * @return The property as a long, or null if it does not exist. + */ + public Long getPropertyAsLong(String propName) + { + String prop = getProperty(propName); + + return (prop != null) ? new Long(prop) : null; + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java index 5f3ebb4545..ecc08770a9 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java @@ -1,94 +1,94 @@ -/* - * - * 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.junit.extensions.util; - -/** - * SizeOf provides a static method that does its best to return an accurate measure of the total amount of memory used by - * the virtual machine. This is calculated as the total memory available to the VM minus the actual amount used by it. - * Before this measurement is taken the garbage collector is run many times until the used memory calculation stabilizes. - * Generally, this trick works quite well to provide an accurate reading, however, it cannot be relied upon to be totally - * accurate. It is also quite slow. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Calculate total memory used. - *
          - * - * @author Rupert Smith - */ -public class SizeOf -{ - /** Holds a reference to the runtime object. */ - private static final Runtime RUNTIME = Runtime.getRuntime(); - - /** - * Makes 4 calls the {@link #runGCTillStable} method. - */ - public static void runGCTillStableSeveralTimes() - { - // It helps to call Runtime.gc() using several method calls. - for (int r = 0; r < 4; ++r) - { - runGCTillStable(); - } - } - - /** - * Runs the garbage collector until the used memory reading stabilizes. It may run the garbage collector up - * to 500 times. - */ - public static void runGCTillStable() - { - long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE; - - for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++i) - { - RUNTIME.runFinalization(); - RUNTIME.gc(); - Thread.currentThread().yield(); - - usedMem2 = usedMem1; - usedMem1 = usedMemory(); - } - } - - /** - * Runs the garbage collector until the used memory stabilizes and then measures it. - * - * @return The amount of memory used by the virtual machine. - */ - public static long getUsedMemory() - { - runGCTillStableSeveralTimes(); - - return usedMemory(); - } - - /** - * Returns the amount of memory used by subtracting the free memory from the total available memory. - * - * @return The amount of memory used. - */ - private static long usedMemory() - { - return RUNTIME.totalMemory() - RUNTIME.freeMemory(); - } -} +/* + * + * 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.junit.extensions.util; + +/** + * SizeOf provides a static method that does its best to return an accurate measure of the total amount of memory used by + * the virtual machine. This is calculated as the total memory available to the VM minus the actual amount used by it. + * Before this measurement is taken the garbage collector is run many times until the used memory calculation stabilizes. + * Generally, this trick works quite well to provide an accurate reading, however, it cannot be relied upon to be totally + * accurate. It is also quite slow. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Calculate total memory used. + *
          + * + * @author Rupert Smith + */ +public class SizeOf +{ + /** Holds a reference to the runtime object. */ + private static final Runtime RUNTIME = Runtime.getRuntime(); + + /** + * Makes 4 calls the {@link #runGCTillStable} method. + */ + public static void runGCTillStableSeveralTimes() + { + // It helps to call Runtime.gc() using several method calls. + for (int r = 0; r < 4; ++r) + { + runGCTillStable(); + } + } + + /** + * Runs the garbage collector until the used memory reading stabilizes. It may run the garbage collector up + * to 500 times. + */ + public static void runGCTillStable() + { + long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE; + + for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++i) + { + RUNTIME.runFinalization(); + RUNTIME.gc(); + Thread.currentThread().yield(); + + usedMem2 = usedMem1; + usedMem1 = usedMemory(); + } + } + + /** + * Runs the garbage collector until the used memory stabilizes and then measures it. + * + * @return The amount of memory used by the virtual machine. + */ + public static long getUsedMemory() + { + runGCTillStableSeveralTimes(); + + return usedMemory(); + } + + /** + * Returns the amount of memory used by subtracting the free memory from the total available memory. + * + * @return The amount of memory used. + */ + private static long usedMemory() + { + return RUNTIME.totalMemory() - RUNTIME.freeMemory(); + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java index 9078c0e247..acc1e2c218 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java @@ -1,131 +1,131 @@ -/* - * - * 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.junit.extensions.util; - -import java.util.EmptyStackException; -import java.util.NoSuchElementException; -import java.util.Queue; -import java.util.Stack; - -/** - * The Stack class in java.util (most unhelpfully) does not implement the Queue interface. This is an adaption of that - * class as a queue. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Turn a stack into a queue. - *
          - * - * @todo Need to override the add method, and iterator and consider other methods too. They work like LIFO but - * really wany FIFO behaviour accross the whole data structure. - * - * @author Rupert Smith - */ -public class StackQueue extends Stack implements Queue -{ - /** - * Retrieves, but does not remove, the head of this queue. - * - * @return The element at the top of the stack. - */ - public E element() - { - try - { - return super.peek(); - } - catch (EmptyStackException e) - { - NoSuchElementException t = new NoSuchElementException(); - t.initCause(e); - throw t; - } - } - - /** - * Inserts the specified element into this queue, if possible. - * - * @param o The data element to push onto the stack. - * - * @return True if it was added to the stack, false otherwise (this implementation always returns true). - */ - public boolean offer(E o) - { - push(o); - - return true; - } - - /** - * Retrieves, but does not remove, the head of this queue, returning null if this queue is empty. - * - * @return The top element from the stack, or null if the stack is empty. - */ - public E peek() - { - try - { - return super.peek(); - } - catch (EmptyStackException e) - { - return null; - } - } - - /** - * Retrieves and removes the head of this queue, or null if this queue is empty. - * - * @return The top element from the stack, or null if the stack is empty. - */ - public E poll() - { - try - { - return super.pop(); - } - catch (EmptyStackException e) - { - return null; - } - } - - /** - * Retrieves and removes the head of this queue. - * - * @return The top element from the stack, or null if the stack is empty. - * - * @throws NoSuchElementException If the stack is empty so no element can be removed from it. - */ - public E remove() - { - try - { - return super.pop(); - } - catch (EmptyStackException e) - { - NoSuchElementException t = new NoSuchElementException(); - t.initCause(e); - throw t; - } - } -} +/* + * + * 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.junit.extensions.util; + +import java.util.EmptyStackException; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Stack; + +/** + * The Stack class in java.util (most unhelpfully) does not implement the Queue interface. This is an adaption of that + * class as a queue. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Turn a stack into a queue. + *
          + * + * @todo Need to override the add method, and iterator and consider other methods too. They work like LIFO but + * really wany FIFO behaviour accross the whole data structure. + * + * @author Rupert Smith + */ +public class StackQueue extends Stack implements Queue +{ + /** + * Retrieves, but does not remove, the head of this queue. + * + * @return The element at the top of the stack. + */ + public E element() + { + try + { + return super.peek(); + } + catch (EmptyStackException e) + { + NoSuchElementException t = new NoSuchElementException(); + t.initCause(e); + throw t; + } + } + + /** + * Inserts the specified element into this queue, if possible. + * + * @param o The data element to push onto the stack. + * + * @return True if it was added to the stack, false otherwise (this implementation always returns true). + */ + public boolean offer(E o) + { + push(o); + + return true; + } + + /** + * Retrieves, but does not remove, the head of this queue, returning null if this queue is empty. + * + * @return The top element from the stack, or null if the stack is empty. + */ + public E peek() + { + try + { + return super.peek(); + } + catch (EmptyStackException e) + { + return null; + } + } + + /** + * Retrieves and removes the head of this queue, or null if this queue is empty. + * + * @return The top element from the stack, or null if the stack is empty. + */ + public E poll() + { + try + { + return super.pop(); + } + catch (EmptyStackException e) + { + return null; + } + } + + /** + * Retrieves and removes the head of this queue. + * + * @return The top element from the stack, or null if the stack is empty. + * + * @throws NoSuchElementException If the stack is empty so no element can be removed from it. + */ + public E remove() + { + try + { + return super.pop(); + } + catch (EmptyStackException e) + { + NoSuchElementException t = new NoSuchElementException(); + t.initCause(e); + throw t; + } + } +} diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java index edb7b6d73a..d402077963 100644 --- a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java +++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java @@ -1,202 +1,202 @@ -/* - * - * 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.junit.extensions.util; - -import java.util.Properties; - -/** - * TestContextProperties is an extension of {@link ParsedProperties} that keeps track of property key/value pairs - * that are used by tests being run under the {@link org.apache.qpid.junit.extensions.TKTestRunner}. To keep the - * test runner notified of configurable test parameters, tests should establish their required property values by - * initiliazing fields or statics or in the constructor, through this class. The tk test runner automatically places - * any additional properties specified on the command line into the this class, and these are held statically. - * - *

          Here is an example: - * - *

          - * public class MyTestClass extends TestCase {
          - *     ParsedProperties testProps = TestContextProperties.getInstance();
          - *     private int testParam = testProps.setPropertyIfNull("testParam", 1);
          - * ...
          - * 
          - * - *

          This has the effect of setting up the field testParam with the default value of 1, unless it is overridden - * by values passed to the tk test runner. It also notifies the tk test runner of the name and value of the test - * parameter actually used for the test, so that this can be logged in the test output file. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Log all name/value pairs read or written. - *
          - * - * @author Rupert Smith - */ -public class TestContextProperties extends ParsedProperties -{ - /** Used for debugging. */ - // Logger log = Logger.getLogger(TestContextProperties.class); - - /** Holds all properties set or read through this property extension class. */ - private Properties accessedProps = new Properties(); - - /** The singleton instance of the test context properties. */ - private static TestContextProperties singleton = null; - - /** - * Default constructor that builds a ContextualProperties that uses environment defaults. - */ - private TestContextProperties() - { - super(); - } - - /** - * Gets the singleton instance of the test context properties. - * - * @return The singleton instance of the test context properties. - */ - public static synchronized ParsedProperties getInstance() - { - if (singleton == null) - { - singleton = new TestContextProperties(); - } - - return singleton; - } - - /** - * Gets the singleton instance of the test context properties, applying a specified set of default properties to - * it, if they are not already set. - * - * @param defaults The defaults to apply for properties not already set. - * - * @return The singleton instance of the test context properties. - */ - public static synchronized ParsedProperties getInstance(Properties defaults) - { - ParsedProperties props = getInstance(); - - for (Object key : defaults.keySet()) - { - String stringKey = (String) key; - String value = defaults.getProperty(stringKey); - - props.setPropertyIfNull(stringKey, value); - } - - return props; - } - - /* - * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties. - * - * @param props The properties to initialize this with. - */ - /*public TestContextProperties(Properties props) - { - super(); - }*/ - - /** - * Gets all of the properties (with their most recent values) that have been set or read through this class. - * - * @return All of the properties accessed through this class. - */ - public static Properties getAccessedProps() - { - return (singleton == null) ? new Properties() : singleton; - // return accessedProps; - } - - /** - * Looks up a property value relative to the environment, callers class and method. The default environment will be - * checked for a matching property if defaults are being used. The property key/value pair is remembered and made - * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. - * - * @param key The property key. - * - * @return The value of this property searching from the most specific definition (environment, class, method, key) - * to the most general (key only), unless use of default environments is turned off in which case the most general - * proeprty searched is (environment, key). - */ - public String getProperty(String key) - { - // log.debug("public String getProperty(String key = " + key + "): called"); - - String value = super.getProperty(key); - - if (value != null) - { - accessedProps.setProperty(key, value); - } - - // log.debug("value = " + value); - - return value; - } - - /** - * Calls the Hashtable method put. Provided for parallelism with the getProperty - * method. Enforces use of strings for property keys and values. The value returned is the result of the - * Hashtable call to put. The property key/value pair is remembered and made - * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. - * - * @param key The key to be placed into this property list. - * @param value The value corresponding to key. - * - * @return The previous value of the specified key in this property list, or null if it did not have one. - */ - public synchronized Object setProperty(String key, String value) - { - // log.debug("public synchronized Object setProperty(String key = " + key + ", String value = " + value + "): called"); - - Object result = super.setProperty(key, value); - accessedProps.setProperty(key, value); - - return result; - } - - /** - * Helper method for setting properties to defaults when they are not already set. The property key/value pair is - * remembered and made available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. - * - * @param key The name of the system property to set. - * @param value The value to set it to. - * - * @return The value of the property, which will be the value passed in if it was null, or the existing value otherwise. - */ - public String setPropertyIfNull(String key, String value) - { - // log.debug("public String setPropertyIfNull(String key = " + key + ", String value = " + value + "): called"); - - String result = super.setPropertyIfNull(key, value); - - if (value != null) - { - accessedProps.setProperty(key, result); - } - - // log.debug("result = " + result); - - return result; - } -} +/* + * + * 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.junit.extensions.util; + +import java.util.Properties; + +/** + * TestContextProperties is an extension of {@link ParsedProperties} that keeps track of property key/value pairs + * that are used by tests being run under the {@link org.apache.qpid.junit.extensions.TKTestRunner}. To keep the + * test runner notified of configurable test parameters, tests should establish their required property values by + * initiliazing fields or statics or in the constructor, through this class. The tk test runner automatically places + * any additional properties specified on the command line into the this class, and these are held statically. + * + *

          Here is an example: + * + *

          + * public class MyTestClass extends TestCase {
          + *     ParsedProperties testProps = TestContextProperties.getInstance();
          + *     private int testParam = testProps.setPropertyIfNull("testParam", 1);
          + * ...
          + * 
          + * + *

          This has the effect of setting up the field testParam with the default value of 1, unless it is overridden + * by values passed to the tk test runner. It also notifies the tk test runner of the name and value of the test + * parameter actually used for the test, so that this can be logged in the test output file. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Log all name/value pairs read or written. + *
          + * + * @author Rupert Smith + */ +public class TestContextProperties extends ParsedProperties +{ + /** Used for debugging. */ + // Logger log = Logger.getLogger(TestContextProperties.class); + + /** Holds all properties set or read through this property extension class. */ + private Properties accessedProps = new Properties(); + + /** The singleton instance of the test context properties. */ + private static TestContextProperties singleton = null; + + /** + * Default constructor that builds a ContextualProperties that uses environment defaults. + */ + private TestContextProperties() + { + super(); + } + + /** + * Gets the singleton instance of the test context properties. + * + * @return The singleton instance of the test context properties. + */ + public static synchronized ParsedProperties getInstance() + { + if (singleton == null) + { + singleton = new TestContextProperties(); + } + + return singleton; + } + + /** + * Gets the singleton instance of the test context properties, applying a specified set of default properties to + * it, if they are not already set. + * + * @param defaults The defaults to apply for properties not already set. + * + * @return The singleton instance of the test context properties. + */ + public static synchronized ParsedProperties getInstance(Properties defaults) + { + ParsedProperties props = getInstance(); + + for (Object key : defaults.keySet()) + { + String stringKey = (String) key; + String value = defaults.getProperty(stringKey); + + props.setPropertyIfNull(stringKey, value); + } + + return props; + } + + /* + * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties. + * + * @param props The properties to initialize this with. + */ + /*public TestContextProperties(Properties props) + { + super(); + }*/ + + /** + * Gets all of the properties (with their most recent values) that have been set or read through this class. + * + * @return All of the properties accessed through this class. + */ + public static Properties getAccessedProps() + { + return (singleton == null) ? new Properties() : singleton; + // return accessedProps; + } + + /** + * Looks up a property value relative to the environment, callers class and method. The default environment will be + * checked for a matching property if defaults are being used. The property key/value pair is remembered and made + * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. + * + * @param key The property key. + * + * @return The value of this property searching from the most specific definition (environment, class, method, key) + * to the most general (key only), unless use of default environments is turned off in which case the most general + * proeprty searched is (environment, key). + */ + public String getProperty(String key) + { + // log.debug("public String getProperty(String key = " + key + "): called"); + + String value = super.getProperty(key); + + if (value != null) + { + accessedProps.setProperty(key, value); + } + + // log.debug("value = " + value); + + return value; + } + + /** + * Calls the Hashtable method put. Provided for parallelism with the getProperty + * method. Enforces use of strings for property keys and values. The value returned is the result of the + * Hashtable call to put. The property key/value pair is remembered and made + * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. + * + * @param key The key to be placed into this property list. + * @param value The value corresponding to key. + * + * @return The previous value of the specified key in this property list, or null if it did not have one. + */ + public synchronized Object setProperty(String key, String value) + { + // log.debug("public synchronized Object setProperty(String key = " + key + ", String value = " + value + "): called"); + + Object result = super.setProperty(key, value); + accessedProps.setProperty(key, value); + + return result; + } + + /** + * Helper method for setting properties to defaults when they are not already set. The property key/value pair is + * remembered and made available to {@link org.apache.qpid.junit.extensions.TKTestRunner}. + * + * @param key The name of the system property to set. + * @param value The value to set it to. + * + * @return The value of the property, which will be the value passed in if it was null, or the existing value otherwise. + */ + public String setPropertyIfNull(String key, String value) + { + // log.debug("public String setPropertyIfNull(String key = " + key + ", String value = " + value + "): called"); + + String result = super.setPropertyIfNull(key, value); + + if (value != null) + { + accessedProps.setProperty(key, result); + } + + // log.debug("result = " + result); + + return result; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java index b9632eee4c..0afec83b19 100644 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java +++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java @@ -1,107 +1,107 @@ -/* - * - * 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.ping; - -import org.apache.log4j.Logger; - -import org.apache.qpid.requestreply.PingPongProducer; - -import javax.jms.Destination; - -import java.util.List; -import java.util.Properties; - -/** - * PingClient is a {@link PingPongProducer} that does not need a {@link org.apache.qpid.requestreply.PingPongBouncer} - * to send replies to its pings. It simply listens to its own ping destinations, rather than seperate reply queues. - * It is an all in one ping client, that produces and consumes its own pings. - * - *

          The constructor increments a count of the number of ping clients created. It is assumed that where many - * are created they will all be run in parallel and be active in sending and consuming pings at the same time. - * If the unique destinations flag is not set and a pub/sub ping cycle is being run, this means that they will all hear - * pings sent by each other. The expected number of pings received will therefore be multiplied up by the number of - * active ping clients. The {@link #getConsumersPerDestination()} method is used to supply this multiplier under these - * conditions. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Create a ping producer that listens to its own pings {@link PingPongProducer} - *
          Count the number of ping producers and produce multiplier for scaling up messages expected over topic pings. - *
          - */ -public class PingClient extends PingPongProducer -{ - /** Used for debugging. */ - private final Logger log = Logger.getLogger(PingClient.class); - - /** Used to count the number of ping clients created. */ - private static int _pingClientCount; - - /** - * Creates a ping producer with the specified parameters, of which there are many. See the class level comments - * for {@link PingPongProducer} for details. This constructor creates a connection to the broker and creates - * producer and consumer sessions on it, to send and recieve its pings and replies on. - * - * @param overrides Properties containing any desired overrides to the defaults. - * - * @throws Exception Any exceptions are allowed to fall through. - */ - public PingClient(Properties overrides) throws Exception - { - super(overrides); - - _pingClientCount++; - } - - /** - * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the - * effect of making this pinger listen to its own pings. - * - * @return The ping destinations. - */ - public List getReplyDestinations() - { - return _pingDestinations; - } - - /** - * Supplies the multiplier for the number of ping clients that will hear each ping when doing pub/sub pinging. - * - * @return The scaling up of the number of expected pub/sub pings. - */ - public int getConsumersPerDestination() - { - log.debug("public int getConsumersPerDestination(): called"); - - if (_isUnique) - { - log.debug(_noOfConsumers + " consumer per destination."); - - return _noOfConsumers; - } - else - { - log.debug((_pingClientCount * _noOfConsumers) + " consumers per destination."); - - return _pingClientCount * _noOfConsumers; - } - } -} +/* + * + * 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.ping; + +import org.apache.log4j.Logger; + +import org.apache.qpid.requestreply.PingPongProducer; + +import javax.jms.Destination; + +import java.util.List; +import java.util.Properties; + +/** + * PingClient is a {@link PingPongProducer} that does not need a {@link org.apache.qpid.requestreply.PingPongBouncer} + * to send replies to its pings. It simply listens to its own ping destinations, rather than seperate reply queues. + * It is an all in one ping client, that produces and consumes its own pings. + * + *

          The constructor increments a count of the number of ping clients created. It is assumed that where many + * are created they will all be run in parallel and be active in sending and consuming pings at the same time. + * If the unique destinations flag is not set and a pub/sub ping cycle is being run, this means that they will all hear + * pings sent by each other. The expected number of pings received will therefore be multiplied up by the number of + * active ping clients. The {@link #getConsumersPerDestination()} method is used to supply this multiplier under these + * conditions. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Create a ping producer that listens to its own pings {@link PingPongProducer} + *
          Count the number of ping producers and produce multiplier for scaling up messages expected over topic pings. + *
          + */ +public class PingClient extends PingPongProducer +{ + /** Used for debugging. */ + private final Logger log = Logger.getLogger(PingClient.class); + + /** Used to count the number of ping clients created. */ + private static int _pingClientCount; + + /** + * Creates a ping producer with the specified parameters, of which there are many. See the class level comments + * for {@link PingPongProducer} for details. This constructor creates a connection to the broker and creates + * producer and consumer sessions on it, to send and recieve its pings and replies on. + * + * @param overrides Properties containing any desired overrides to the defaults. + * + * @throws Exception Any exceptions are allowed to fall through. + */ + public PingClient(Properties overrides) throws Exception + { + super(overrides); + + _pingClientCount++; + } + + /** + * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the + * effect of making this pinger listen to its own pings. + * + * @return The ping destinations. + */ + public List getReplyDestinations() + { + return _pingDestinations; + } + + /** + * Supplies the multiplier for the number of ping clients that will hear each ping when doing pub/sub pinging. + * + * @return The scaling up of the number of expected pub/sub pings. + */ + public int getConsumersPerDestination() + { + log.debug("public int getConsumersPerDestination(): called"); + + if (_isUnique) + { + log.debug(_noOfConsumers + " consumer per destination."); + + return _noOfConsumers; + } + else + { + log.debug((_pingClientCount * _noOfConsumers) + " consumers per destination."); + + return _pingClientCount * _noOfConsumers; + } + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java index 0c8c19243a..a15897c82b 100644 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java +++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java @@ -1,452 +1,452 @@ -/* - * - * 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.ping; - -import org.apache.log4j.Logger; - -import org.apache.qpid.requestreply.PingPongProducer; -import org.apache.qpid.util.CommandLineParser; - -import org.apache.qpid.junit.extensions.util.MathUtils; -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.*; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * PingDurableClient is a variation of the {@link PingPongProducer} ping tool. Instead of sending its pings and - * receiving replies to them at the same time, this tool sends pings until it is signalled by some 'event' to stop - * sending. It then waits for another signal before it re-opens a fresh connection and attempts to receive all of the - * pings that it has succesfully sent. It is intended to be an interactive test that lets a user experiment with - * failure conditions when using durable messaging. - * - *

          The events that can stop it from sending are input from the user on the console, failure of its connection to - * the broker, completion of sending a specified number of messages, or expiry of a specified duration. In all cases - * it will do its best to clean up and close the connection before opening a fresh connection to receive the pings - * with. - * - *

          The event to re-connect and attempt to recieve the pings is input from the user on the console. - * - *

          This ping client inherits the configuration properties of its parent class ({@link PingPongProducer}) and - * additionally accepts the following parameters: - * - *

          - *
          Parameters
          Parameter Default Comments - *
          numMessages 100 The total number of messages to send. - *
          numMessagesToAction -1 The number of messages to send before taking a custom 'action'. - *
          duration 30S The length of time to ping for. (Format dDhHmMsS, for d days, h hours, - * m minutes and s seconds). - *
          - * - *

          This ping client also overrides some of the defaults of its parent class, to provide a reasonable set up - * when no parameters are specified. - * - *

          - *
          Parameters
          Parameter Default Comments - *
          uniqueDests false Prevents destination names being timestamped. - *
          transacted true Only makes sense to test with transactions. - *
          persistent true Only makes sense to test persistent. - *
          durableDests true Should use durable queues with persistent messages. - *
          commitBatchSize 10 - *
          rate 20 Total default test time is 5 seconds. - *
          - * - *

          When a number of messages or duration is specified, this ping client will ping until the first of those limits - * is reached. Reaching the limit will be interpreted as the first signal to stop sending, and the ping client will - * wait for the second signal before receiving its pings. - * - *

          This class provides a mechanism for extensions to add arbitrary actions, after a particular number of messages - * have been sent. When the number of messages equal the value set in the 'numMessagesToAction' property is method, - * the {@link #takeAction} method is called. By default this does nothing, but extensions of this class can provide - * custom behaviour with alternative implementations of this method (for example taking a backup). - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Send and receive pings. - *
          Accept user input to signal stop sending. - *
          Accept user input to signal start receiving. - *
          Provide feedback on pings sent versus pings received. - *
          Provide extension point for arbitrary action on a particular message count. - *
          - */ -public class PingDurableClient extends PingPongProducer implements ExceptionListener -{ - private static final Logger log = Logger.getLogger(PingDurableClient.class); - - public static final String NUM_MESSAGES_PROPNAME = "numMessages"; - public static final String NUM_MESSAGES_DEFAULT = "100"; - public static final String DURATION_PROPNAME = "duration"; - public static final String DURATION_DEFAULT = "30S"; - public static final String NUM_MESSAGES_TO_ACTION_PROPNAME = "numMessagesToAction"; - public static final String NUM_MESSAGES_TO_ACTION_DEFAULT = "-1"; - - /** The maximum length of time to wait whilst receiving pings before assuming that no more are coming. */ - private static final long TIME_OUT = 3000; - - static - { - defaults.setProperty(NUM_MESSAGES_PROPNAME, NUM_MESSAGES_DEFAULT); - defaults.setProperty(DURATION_PROPNAME, DURATION_DEFAULT); - defaults.setProperty(UNIQUE_DESTS_PROPNAME, "false"); - defaults.setProperty(TRANSACTED_PROPNAME, "true"); - defaults.setProperty(PERSISTENT_MODE_PROPNAME, "true"); - defaults.setProperty(TX_BATCH_SIZE_PROPNAME, "10"); - defaults.setProperty(RATE_PROPNAME, "20"); - defaults.setProperty(DURABLE_DESTS_PROPNAME, "true"); - defaults.setProperty(NUM_MESSAGES_TO_ACTION_PROPNAME, NUM_MESSAGES_TO_ACTION_DEFAULT); - } - - /** Specifies the number of pings to send, if larger than 0. 0 means send until told to stop. */ - private int numMessages; - - /** Holds the number of messages to send before taking triggering the action. */ - private int numMessagesToAction; - - /** Sepcifies how long to ping for, if larger than 0. 0 means send until told to stop. */ - private long duration; - - /** Used to indciate that this application should terminate. Set by the shutdown hook. */ - private boolean terminate = false; - - /** - * @throws Exception Any exceptions are allowed to fall through. - */ - public PingDurableClient(Properties overrides) throws Exception - { - super(overrides); - log.debug("public PingDurableClient(Properties overrides = " + overrides + "): called"); - - // Extract the additional configuration parameters. - ParsedProperties properties = new ParsedProperties(defaults); - properties.putAll(overrides); - - numMessages = properties.getPropertyAsInteger(NUM_MESSAGES_PROPNAME); - String durationSpec = properties.getProperty(DURATION_PROPNAME); - numMessagesToAction = properties.getPropertyAsInteger(NUM_MESSAGES_TO_ACTION_PROPNAME); - - if (durationSpec != null) - { - duration = MathUtils.parseDuration(durationSpec) * 1000000; - } - } - - /** - * Starts the ping/wait/receive process. - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Create a ping producer overriding its defaults with all options passed on the command line. - Properties options = - CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); - PingDurableClient pingProducer = new PingDurableClient(options); - - // Create a shutdown hook to terminate the ping-pong producer. - Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); - - // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. - // pingProducer.getConnection().setExceptionListener(pingProducer); - - // Run the test procedure. - int sent = pingProducer.send(); - pingProducer.closeConnection(); - pingProducer.waitForUser("Press return to begin receiving the pings."); - pingProducer.receive(sent); - - System.exit(0); - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(1); - } - } - - /** - * Performs the main test procedure implemented by this ping client. See the class level comment for details. - */ - protected int send() throws Exception - { - log.debug("public void sendWaitReceive(): called"); - - log.debug("duration = " + duration); - log.debug("numMessages = " + numMessages); - - if (duration > 0) - { - System.out.println("Sending for up to " + (duration / 1000000000f) + " seconds."); - } - - if (_rate > 0) - { - System.out.println("Sending at " + _rate + " messages per second."); - } - - if (numMessages > 0) - { - System.out.println("Sending up to " + numMessages + " messages."); - } - - // Establish the connection and the message producer. - establishConnection(true, false); - _connection.start(); - - Message message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); - - // Send pings until a terminating condition is received. - boolean endCondition = false; - int messagesSent = 0; - int messagesCommitted = 0; - int messagesNotCommitted = 0; - long start = System.nanoTime(); - - // Clear console in. - clearConsole(); - - while (!endCondition) - { - boolean committed = false; - - try - { - committed = sendMessage(messagesSent, message) && _transacted; - - messagesSent++; - messagesNotCommitted++; - - // Keep count of the number of messsages currently committed and pending commit. - if (committed) - { - log.debug("Adding " + messagesNotCommitted + " messages to the committed count."); - messagesCommitted += messagesNotCommitted; - messagesNotCommitted = 0; - - System.out.println("Commited: " + messagesCommitted); - } - } - catch (JMSException e) - { - log.debug("Got JMSException whilst sending."); - _publish = false; - } - - // Perform the arbitrary action if the number of messages sent has reached the right number. - if (messagesSent == numMessagesToAction) - { - System.out.println("At action point, Messages sent = " + messagesSent + ", Messages Committed = " - + messagesCommitted + ", Messages not Committed = " + messagesNotCommitted); - takeAction(); - } - - // Determine if the end condition has been met, based on the number of messages, time passed, errors on - // the connection or user input. - long now = System.nanoTime(); - - if ((duration != 0) && ((now - start) > duration)) - { - System.out.println("Send halted because duration expired."); - endCondition = true; - } - else if ((numMessages != 0) && (messagesSent >= numMessages)) - { - System.out.println("Send halted because # messages completed."); - endCondition = true; - } - else if (System.in.available() > 0) - { - System.out.println("Send halted by user input."); - endCondition = true; - - clearConsole(); - } - else if (!_publish) - { - System.out.println("Send halted by error on the connection."); - endCondition = true; - } - } - - log.debug("messagesSent = " + messagesSent); - log.debug("messagesCommitted = " + messagesCommitted); - log.debug("messagesNotCommitted = " + messagesNotCommitted); - - System.out.println("Messages sent: " + messagesSent + ", Messages Committed = " + messagesCommitted - + ", Messages not Committed = " + messagesNotCommitted); - - return messagesSent; - } - - protected void closeConnection() - { - // Clean up the connection. - try - { - close(); - } - catch (JMSException e) - { - log.debug("There was an error whilst closing the connection: " + e, e); - System.out.println("There was an error whilst closing the connection."); - - // Ignore as did best could manage to clean up. - } - } - - protected void receive(int messagesSent) throws Exception - { - // Re-establish the connection and the message consumer. - _queueJVMSequenceID = new AtomicInteger(); - _queueSharedID = new AtomicInteger(); - - establishConnection(false, true); - _consumer[0].setMessageListener(null); - _consumerConnection[0].start(); - - // Try to receive all of the pings that were successfully sent. - int messagesReceived = 0; - boolean endCondition = false; - - while (!endCondition) - { - // Message received = _consumer.receiveNoWait(); - Message received = _consumer[0].receive(TIME_OUT); - log.debug("received = " + received); - - if (received != null) - { - messagesReceived++; - } - - // Determine if the end condition has been met, based on the number of messages and time passed since last - // receiving a message. - if (received == null) - { - System.out.println("Timed out."); - endCondition = true; - } - else if (messagesReceived >= messagesSent) - { - System.out.println("Got all messages."); - endCondition = true; - } - } - - // Ensure messages received are committed. - if (_consTransacted) - { - try - { - _consumerSession[0].commit(); - System.out.println("Committed for all messages received."); - } - catch (JMSException e) - { - log.debug("Error during commit: " + e, e); - System.out.println("Error during commit."); - try - { - _consumerSession[0].rollback(); - System.out.println("Rolled back on all messages received."); - } - catch (JMSException e2) - { - log.debug("Error during rollback: " + e, e); - System.out.println("Error on roll back of all messages received."); - } - - } - } - - log.debug("messagesReceived = " + messagesReceived); - - System.out.println("Messages received: " + messagesReceived); - - // Clean up the connection. - close(); - } - - /** - * Clears any pending input from the console. - */ - private void clearConsole() - { - try - { - BufferedReader bis = new BufferedReader(new InputStreamReader(System.in)); - - // System.in.skip(System.in.available()); - while (bis.ready()) - { - bis.readLine(); - } - } - catch (IOException e) - { } - } - - /** - * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the - * effect of making this pinger listen to its own pings. - * - * @return The ping destinations. - */ - public List getReplyDestinations() - { - return _pingDestinations; - } - - /** - * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered with - * the runtime system as a shutdown hook. This shutdown hook sets an additional terminate flag, compared with the - * shutdown hook in {@link PingPongProducer}, because the publish flag is used to indicate that sending or receiving - * message should stop, not that the application should termiante. - * - * @return A shutdown hook for the ping loop. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - stop(); - terminate = true; - } - }); - } - - /** - * Performs an aribtrary action once the 'numMesagesToAction' count is reached on sending messages. This default - * implementation does nothing. - */ - public void takeAction() - { } -} +/* + * + * 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.ping; + +import org.apache.log4j.Logger; + +import org.apache.qpid.requestreply.PingPongProducer; +import org.apache.qpid.util.CommandLineParser; + +import org.apache.qpid.junit.extensions.util.MathUtils; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * PingDurableClient is a variation of the {@link PingPongProducer} ping tool. Instead of sending its pings and + * receiving replies to them at the same time, this tool sends pings until it is signalled by some 'event' to stop + * sending. It then waits for another signal before it re-opens a fresh connection and attempts to receive all of the + * pings that it has succesfully sent. It is intended to be an interactive test that lets a user experiment with + * failure conditions when using durable messaging. + * + *

          The events that can stop it from sending are input from the user on the console, failure of its connection to + * the broker, completion of sending a specified number of messages, or expiry of a specified duration. In all cases + * it will do its best to clean up and close the connection before opening a fresh connection to receive the pings + * with. + * + *

          The event to re-connect and attempt to recieve the pings is input from the user on the console. + * + *

          This ping client inherits the configuration properties of its parent class ({@link PingPongProducer}) and + * additionally accepts the following parameters: + * + *

          + *
          Parameters
          Parameter Default Comments + *
          numMessages 100 The total number of messages to send. + *
          numMessagesToAction -1 The number of messages to send before taking a custom 'action'. + *
          duration 30S The length of time to ping for. (Format dDhHmMsS, for d days, h hours, + * m minutes and s seconds). + *
          + * + *

          This ping client also overrides some of the defaults of its parent class, to provide a reasonable set up + * when no parameters are specified. + * + *

          + *
          Parameters
          Parameter Default Comments + *
          uniqueDests false Prevents destination names being timestamped. + *
          transacted true Only makes sense to test with transactions. + *
          persistent true Only makes sense to test persistent. + *
          durableDests true Should use durable queues with persistent messages. + *
          commitBatchSize 10 + *
          rate 20 Total default test time is 5 seconds. + *
          + * + *

          When a number of messages or duration is specified, this ping client will ping until the first of those limits + * is reached. Reaching the limit will be interpreted as the first signal to stop sending, and the ping client will + * wait for the second signal before receiving its pings. + * + *

          This class provides a mechanism for extensions to add arbitrary actions, after a particular number of messages + * have been sent. When the number of messages equal the value set in the 'numMessagesToAction' property is method, + * the {@link #takeAction} method is called. By default this does nothing, but extensions of this class can provide + * custom behaviour with alternative implementations of this method (for example taking a backup). + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Send and receive pings. + *
          Accept user input to signal stop sending. + *
          Accept user input to signal start receiving. + *
          Provide feedback on pings sent versus pings received. + *
          Provide extension point for arbitrary action on a particular message count. + *
          + */ +public class PingDurableClient extends PingPongProducer implements ExceptionListener +{ + private static final Logger log = Logger.getLogger(PingDurableClient.class); + + public static final String NUM_MESSAGES_PROPNAME = "numMessages"; + public static final String NUM_MESSAGES_DEFAULT = "100"; + public static final String DURATION_PROPNAME = "duration"; + public static final String DURATION_DEFAULT = "30S"; + public static final String NUM_MESSAGES_TO_ACTION_PROPNAME = "numMessagesToAction"; + public static final String NUM_MESSAGES_TO_ACTION_DEFAULT = "-1"; + + /** The maximum length of time to wait whilst receiving pings before assuming that no more are coming. */ + private static final long TIME_OUT = 3000; + + static + { + defaults.setProperty(NUM_MESSAGES_PROPNAME, NUM_MESSAGES_DEFAULT); + defaults.setProperty(DURATION_PROPNAME, DURATION_DEFAULT); + defaults.setProperty(UNIQUE_DESTS_PROPNAME, "false"); + defaults.setProperty(TRANSACTED_PROPNAME, "true"); + defaults.setProperty(PERSISTENT_MODE_PROPNAME, "true"); + defaults.setProperty(TX_BATCH_SIZE_PROPNAME, "10"); + defaults.setProperty(RATE_PROPNAME, "20"); + defaults.setProperty(DURABLE_DESTS_PROPNAME, "true"); + defaults.setProperty(NUM_MESSAGES_TO_ACTION_PROPNAME, NUM_MESSAGES_TO_ACTION_DEFAULT); + } + + /** Specifies the number of pings to send, if larger than 0. 0 means send until told to stop. */ + private int numMessages; + + /** Holds the number of messages to send before taking triggering the action. */ + private int numMessagesToAction; + + /** Sepcifies how long to ping for, if larger than 0. 0 means send until told to stop. */ + private long duration; + + /** Used to indciate that this application should terminate. Set by the shutdown hook. */ + private boolean terminate = false; + + /** + * @throws Exception Any exceptions are allowed to fall through. + */ + public PingDurableClient(Properties overrides) throws Exception + { + super(overrides); + log.debug("public PingDurableClient(Properties overrides = " + overrides + "): called"); + + // Extract the additional configuration parameters. + ParsedProperties properties = new ParsedProperties(defaults); + properties.putAll(overrides); + + numMessages = properties.getPropertyAsInteger(NUM_MESSAGES_PROPNAME); + String durationSpec = properties.getProperty(DURATION_PROPNAME); + numMessagesToAction = properties.getPropertyAsInteger(NUM_MESSAGES_TO_ACTION_PROPNAME); + + if (durationSpec != null) + { + duration = MathUtils.parseDuration(durationSpec) * 1000000; + } + } + + /** + * Starts the ping/wait/receive process. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + // Create a ping producer overriding its defaults with all options passed on the command line. + Properties options = + CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); + PingDurableClient pingProducer = new PingDurableClient(options); + + // Create a shutdown hook to terminate the ping-pong producer. + Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); + + // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. + // pingProducer.getConnection().setExceptionListener(pingProducer); + + // Run the test procedure. + int sent = pingProducer.send(); + pingProducer.closeConnection(); + pingProducer.waitForUser("Press return to begin receiving the pings."); + pingProducer.receive(sent); + + System.exit(0); + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(1); + } + } + + /** + * Performs the main test procedure implemented by this ping client. See the class level comment for details. + */ + protected int send() throws Exception + { + log.debug("public void sendWaitReceive(): called"); + + log.debug("duration = " + duration); + log.debug("numMessages = " + numMessages); + + if (duration > 0) + { + System.out.println("Sending for up to " + (duration / 1000000000f) + " seconds."); + } + + if (_rate > 0) + { + System.out.println("Sending at " + _rate + " messages per second."); + } + + if (numMessages > 0) + { + System.out.println("Sending up to " + numMessages + " messages."); + } + + // Establish the connection and the message producer. + establishConnection(true, false); + _connection.start(); + + Message message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); + + // Send pings until a terminating condition is received. + boolean endCondition = false; + int messagesSent = 0; + int messagesCommitted = 0; + int messagesNotCommitted = 0; + long start = System.nanoTime(); + + // Clear console in. + clearConsole(); + + while (!endCondition) + { + boolean committed = false; + + try + { + committed = sendMessage(messagesSent, message) && _transacted; + + messagesSent++; + messagesNotCommitted++; + + // Keep count of the number of messsages currently committed and pending commit. + if (committed) + { + log.debug("Adding " + messagesNotCommitted + " messages to the committed count."); + messagesCommitted += messagesNotCommitted; + messagesNotCommitted = 0; + + System.out.println("Commited: " + messagesCommitted); + } + } + catch (JMSException e) + { + log.debug("Got JMSException whilst sending."); + _publish = false; + } + + // Perform the arbitrary action if the number of messages sent has reached the right number. + if (messagesSent == numMessagesToAction) + { + System.out.println("At action point, Messages sent = " + messagesSent + ", Messages Committed = " + + messagesCommitted + ", Messages not Committed = " + messagesNotCommitted); + takeAction(); + } + + // Determine if the end condition has been met, based on the number of messages, time passed, errors on + // the connection or user input. + long now = System.nanoTime(); + + if ((duration != 0) && ((now - start) > duration)) + { + System.out.println("Send halted because duration expired."); + endCondition = true; + } + else if ((numMessages != 0) && (messagesSent >= numMessages)) + { + System.out.println("Send halted because # messages completed."); + endCondition = true; + } + else if (System.in.available() > 0) + { + System.out.println("Send halted by user input."); + endCondition = true; + + clearConsole(); + } + else if (!_publish) + { + System.out.println("Send halted by error on the connection."); + endCondition = true; + } + } + + log.debug("messagesSent = " + messagesSent); + log.debug("messagesCommitted = " + messagesCommitted); + log.debug("messagesNotCommitted = " + messagesNotCommitted); + + System.out.println("Messages sent: " + messagesSent + ", Messages Committed = " + messagesCommitted + + ", Messages not Committed = " + messagesNotCommitted); + + return messagesSent; + } + + protected void closeConnection() + { + // Clean up the connection. + try + { + close(); + } + catch (JMSException e) + { + log.debug("There was an error whilst closing the connection: " + e, e); + System.out.println("There was an error whilst closing the connection."); + + // Ignore as did best could manage to clean up. + } + } + + protected void receive(int messagesSent) throws Exception + { + // Re-establish the connection and the message consumer. + _queueJVMSequenceID = new AtomicInteger(); + _queueSharedID = new AtomicInteger(); + + establishConnection(false, true); + _consumer[0].setMessageListener(null); + _consumerConnection[0].start(); + + // Try to receive all of the pings that were successfully sent. + int messagesReceived = 0; + boolean endCondition = false; + + while (!endCondition) + { + // Message received = _consumer.receiveNoWait(); + Message received = _consumer[0].receive(TIME_OUT); + log.debug("received = " + received); + + if (received != null) + { + messagesReceived++; + } + + // Determine if the end condition has been met, based on the number of messages and time passed since last + // receiving a message. + if (received == null) + { + System.out.println("Timed out."); + endCondition = true; + } + else if (messagesReceived >= messagesSent) + { + System.out.println("Got all messages."); + endCondition = true; + } + } + + // Ensure messages received are committed. + if (_consTransacted) + { + try + { + _consumerSession[0].commit(); + System.out.println("Committed for all messages received."); + } + catch (JMSException e) + { + log.debug("Error during commit: " + e, e); + System.out.println("Error during commit."); + try + { + _consumerSession[0].rollback(); + System.out.println("Rolled back on all messages received."); + } + catch (JMSException e2) + { + log.debug("Error during rollback: " + e, e); + System.out.println("Error on roll back of all messages received."); + } + + } + } + + log.debug("messagesReceived = " + messagesReceived); + + System.out.println("Messages received: " + messagesReceived); + + // Clean up the connection. + close(); + } + + /** + * Clears any pending input from the console. + */ + private void clearConsole() + { + try + { + BufferedReader bis = new BufferedReader(new InputStreamReader(System.in)); + + // System.in.skip(System.in.available()); + while (bis.ready()) + { + bis.readLine(); + } + } + catch (IOException e) + { } + } + + /** + * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the + * effect of making this pinger listen to its own pings. + * + * @return The ping destinations. + */ + public List getReplyDestinations() + { + return _pingDestinations; + } + + /** + * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered with + * the runtime system as a shutdown hook. This shutdown hook sets an additional terminate flag, compared with the + * shutdown hook in {@link PingPongProducer}, because the publish flag is used to indicate that sending or receiving + * message should stop, not that the application should termiante. + * + * @return A shutdown hook for the ping loop. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + stop(); + terminate = true; + } + }); + } + + /** + * Performs an aribtrary action once the 'numMesagesToAction' count is reached on sending messages. This default + * implementation does nothing. + */ + public void takeAction() + { } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java index 215dbcefa3..5ba4004c56 100644 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java +++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java @@ -1,311 +1,311 @@ -/* - * - * 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.ping; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.requestreply.PingPongProducer; - -import org.apache.qpid.junit.extensions.TimingController; -import org.apache.qpid.junit.extensions.TimingControllerAware; -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.JMSException; -import javax.jms.Message; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; - -/** - * PingLatencyTestPerf is a performance test that outputs multiple timings from its test method, using the timing - * controller interface supplied by the test runner from a seperate listener thread. It outputs round trip timings for - * individual ping messages rather than for how long a complete batch of messages took to process. It also differs from - * the {@link PingTestPerf} test that it extends because it can output timings as replies are received, rather than - * waiting until all expected replies are received. - * - *

          This test does not output timings for every single ping message, as when running at high volume, writing the test - * log for a vast number of messages would slow the testing down. Instead samples ping latency occasionally. The - * frequency of ping sampling is set using the {@link #TEST_RESULTS_BATCH_SIZE_PROPNAME} property, to override the - * default of every {@link #DEFAULT_TEST_RESULTS_BATCH_SIZE}. - * - *

          The size parameter logged for each individual ping is set to the size of the batch of messages that the - * individual timed ping was taken from, rather than 1 for a single message. This is so that the total throughput - * (messages / time) can be calculated in order to examine the relationship between throughput and latency. - * - *

          CRC Card
          Responsibilities Collaborations
          Send many ping - * messages and output timings for sampled individual pings.
          - */ -public class PingLatencyTestPerf extends PingTestPerf implements TimingControllerAware -{ - private static Logger _logger = Logger.getLogger(PingLatencyTestPerf.class); - - /** Holds the name of the property to get the test results logging batch size. */ - public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize"; - - /** Holds the default test results logging batch size. */ - public static final int DEFAULT_TEST_RESULTS_BATCH_SIZE = 1000; - - /** Used to hold the timing controller passed from the test runner. */ - private TimingController _timingController; - - /** Used to generate unique correlation ids for each test run. */ - private AtomicLong corellationIdGenerator = new AtomicLong(); - - /** - * Holds test specifics by correlation id. This consists of the expected number of messages and the timing - * controler. - */ - private Map perCorrelationIds = - Collections.synchronizedMap(new HashMap()); - - /** Holds the batched results listener, that does logging on batch boundaries. */ - private BatchedResultsListener batchedResultsListener = null; - - /** - * Creates a new asynchronous ping performance test with the specified name. - * - * @param name The test name. - */ - public PingLatencyTestPerf(String name) - { - super(name); - - // Sets up the test parameters with defaults. - ParsedProperties.setSysPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME, - Integer.toString(DEFAULT_TEST_RESULTS_BATCH_SIZE)); - } - - /** Compile all the tests into a test suite. */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Ping Latency Tests"); - - // Run performance tests in read committed mode. - suite.addTest(new PingLatencyTestPerf("testPingLatency")); - - return suite; - } - - /** - * Accepts a timing controller from the test runner. - * - * @param timingController The timing controller to register mutliple timings with. - */ - public void setTimingController(TimingController timingController) - { - _timingController = timingController; - } - - /** - * Gets the timing controller passed in by the test runner. - * - * @return The timing controller passed in by the test runner. - */ - public TimingController getTimingController() - { - return _timingController; - } - - /** - * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until all - * replies have been received or a time out occurs before exiting this method. - * - * @param numPings The number of pings to send. - */ - public void testPingLatency(int numPings) throws Exception - { - _logger.debug("public void testPingLatency(int numPings): called"); - - // Ensure that at least one ping was requeusted. - if (numPings == 0) - { - _logger.error("Number of pings requested was zero."); - } - - // Get the per thread test setup to run the test through. - PerThreadSetup perThreadSetup = threadSetup.get(); - PingClient pingClient = perThreadSetup._pingClient; - - // Advance the correlation id of messages to send, to make it unique for this run. - String messageCorrelationId = Long.toString(corellationIdGenerator.incrementAndGet()); - _logger.debug("messageCorrelationId = " + messageCorrelationId); - - // Initialize the count and timing controller for the new correlation id. - PerCorrelationId perCorrelationId = new PerCorrelationId(); - TimingController tc = getTimingController().getControllerForCurrentThread(); - perCorrelationId._tc = tc; - perCorrelationId._expectedCount = numPings; - perCorrelationIds.put(messageCorrelationId, perCorrelationId); - - // Attach the chained message listener to the ping producer to listen asynchronously for the replies to these - // messages. - pingClient.setChainedMessageListener(batchedResultsListener); - - // Generate a sample message of the specified size. - Message msg = - pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), - testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), - testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); - - // Send the requested number of messages, and wait until they have all been received. - long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); - int numReplies = pingClient.pingAndWaitForReply(msg, numPings, timeout, null); - - // Check that all the replies were received and log a fail if they were not. - if (numReplies < numPings) - { - tc.completeTest(false, 0); - } - - // Remove the chained message listener from the ping producer. - pingClient.removeChainedMessageListener(); - - // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up. - perCorrelationIds.remove(messageCorrelationId); - } - - /** Performs test fixture creation on a per thread basis. This will only be called once for each test thread. */ - public void threadSetUp() - { - _logger.debug("public void threadSetUp(): called"); - - try - { - // Call the set up method in the super class. This creates a PingClient pinger. - super.threadSetUp(); - - // Create the chained message listener, only if it has not already been created. This is set up with the - // batch size property, to tell it what batch size to output results on. A synchronized block is used to - // ensure that only one thread creates this. - synchronized (this) - { - if (batchedResultsListener == null) - { - int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME)); - batchedResultsListener = new BatchedResultsListener(batchSize); - } - } - - // Get the set up that the super class created. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Register the chained message listener on the pinger to do its asynchronous test timings from. - perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener); - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - - /** - * BatchedResultsListener is a {@link org.apache.qpid.requestreply.PingPongProducer.ChainedMessageListener} that can - * be attached to the pinger, in order to receive notifications about every message received and the number - * remaining to be received. Whenever the number remaining crosses a batch size boundary this results listener - * outputs a test timing for the actual number of messages received in the current batch. - */ - private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener - { - /** The test results logging batch size. */ - int _batchSize; - private boolean _strictAMQP; - - /** - * Creates a results listener on the specified batch size. - * - * @param batchSize The batch size to use. - */ - public BatchedResultsListener(int batchSize) - { - _batchSize = batchSize; - _strictAMQP = - Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, - AMQSession.STRICT_AMQP_DEFAULT)); - } - - /** - * This callback method is called from all of the pingers that this test creates. It uses the correlation id - * from the message to identify the timing controller for the test thread that was responsible for sending those - * messages. - * - * @param message The message. - * @param remainingCount The count of messages remaining to be received with a particular correlation id. - * - * @throws javax.jms.JMSException Any underlying JMSException is allowed to fall through. - */ - public void onMessage(Message message, int remainingCount, long latency) throws JMSException - { - _logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount + "): called"); - - // Check if a batch boundary has been crossed. - if ((remainingCount % _batchSize) == 0) - { - // Extract the correlation id from the message. - String correlationId = message.getJMSCorrelationID(); - - // Get the details for the correlation id and check that they are not null. They can become null - // if a test times out. - PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId); - if (perCorrelationId != null) - { - // Get the timing controller and expected count for this correlation id. - TimingController tc = perCorrelationId._tc; - int expected = perCorrelationId._expectedCount; - - // Calculate how many messages were actually received in the last batch. This will be the batch size - // except where the number expected is not a multiple of the batch size and this is the first remaining - // count to cross a batch size boundary, in which case it will be the number expected modulo the batch - // size. - int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize; - - // Register a test result for the correlation id. - try - { - tc.completeTest(true, receivedInBatch, latency); - } - catch (InterruptedException e) - { - // Ignore this. It means the test runner wants to stop as soon as possible. - _logger.warn("Got InterruptedException.", e); - } - } - // Else ignore, test timed out. Should log a fail here? - } - } - } - - /** - * Holds state specific to each correlation id, needed to output test results. This consists of the count of the - * total expected number of messages, and the timing controller for the thread sending those message ids. - */ - private static class PerCorrelationId - { - public int _expectedCount; - public TimingController _tc; - } -} +/* + * + * 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.ping; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.requestreply.PingPongProducer; + +import org.apache.qpid.junit.extensions.TimingController; +import org.apache.qpid.junit.extensions.TimingControllerAware; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +/** + * PingLatencyTestPerf is a performance test that outputs multiple timings from its test method, using the timing + * controller interface supplied by the test runner from a seperate listener thread. It outputs round trip timings for + * individual ping messages rather than for how long a complete batch of messages took to process. It also differs from + * the {@link PingTestPerf} test that it extends because it can output timings as replies are received, rather than + * waiting until all expected replies are received. + * + *

          This test does not output timings for every single ping message, as when running at high volume, writing the test + * log for a vast number of messages would slow the testing down. Instead samples ping latency occasionally. The + * frequency of ping sampling is set using the {@link #TEST_RESULTS_BATCH_SIZE_PROPNAME} property, to override the + * default of every {@link #DEFAULT_TEST_RESULTS_BATCH_SIZE}. + * + *

          The size parameter logged for each individual ping is set to the size of the batch of messages that the + * individual timed ping was taken from, rather than 1 for a single message. This is so that the total throughput + * (messages / time) can be calculated in order to examine the relationship between throughput and latency. + * + *

          CRC Card
          Responsibilities Collaborations
          Send many ping + * messages and output timings for sampled individual pings.
          + */ +public class PingLatencyTestPerf extends PingTestPerf implements TimingControllerAware +{ + private static Logger _logger = Logger.getLogger(PingLatencyTestPerf.class); + + /** Holds the name of the property to get the test results logging batch size. */ + public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize"; + + /** Holds the default test results logging batch size. */ + public static final int DEFAULT_TEST_RESULTS_BATCH_SIZE = 1000; + + /** Used to hold the timing controller passed from the test runner. */ + private TimingController _timingController; + + /** Used to generate unique correlation ids for each test run. */ + private AtomicLong corellationIdGenerator = new AtomicLong(); + + /** + * Holds test specifics by correlation id. This consists of the expected number of messages and the timing + * controler. + */ + private Map perCorrelationIds = + Collections.synchronizedMap(new HashMap()); + + /** Holds the batched results listener, that does logging on batch boundaries. */ + private BatchedResultsListener batchedResultsListener = null; + + /** + * Creates a new asynchronous ping performance test with the specified name. + * + * @param name The test name. + */ + public PingLatencyTestPerf(String name) + { + super(name); + + // Sets up the test parameters with defaults. + ParsedProperties.setSysPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME, + Integer.toString(DEFAULT_TEST_RESULTS_BATCH_SIZE)); + } + + /** Compile all the tests into a test suite. */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Ping Latency Tests"); + + // Run performance tests in read committed mode. + suite.addTest(new PingLatencyTestPerf("testPingLatency")); + + return suite; + } + + /** + * Accepts a timing controller from the test runner. + * + * @param timingController The timing controller to register mutliple timings with. + */ + public void setTimingController(TimingController timingController) + { + _timingController = timingController; + } + + /** + * Gets the timing controller passed in by the test runner. + * + * @return The timing controller passed in by the test runner. + */ + public TimingController getTimingController() + { + return _timingController; + } + + /** + * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until all + * replies have been received or a time out occurs before exiting this method. + * + * @param numPings The number of pings to send. + */ + public void testPingLatency(int numPings) throws Exception + { + _logger.debug("public void testPingLatency(int numPings): called"); + + // Ensure that at least one ping was requeusted. + if (numPings == 0) + { + _logger.error("Number of pings requested was zero."); + } + + // Get the per thread test setup to run the test through. + PerThreadSetup perThreadSetup = threadSetup.get(); + PingClient pingClient = perThreadSetup._pingClient; + + // Advance the correlation id of messages to send, to make it unique for this run. + String messageCorrelationId = Long.toString(corellationIdGenerator.incrementAndGet()); + _logger.debug("messageCorrelationId = " + messageCorrelationId); + + // Initialize the count and timing controller for the new correlation id. + PerCorrelationId perCorrelationId = new PerCorrelationId(); + TimingController tc = getTimingController().getControllerForCurrentThread(); + perCorrelationId._tc = tc; + perCorrelationId._expectedCount = numPings; + perCorrelationIds.put(messageCorrelationId, perCorrelationId); + + // Attach the chained message listener to the ping producer to listen asynchronously for the replies to these + // messages. + pingClient.setChainedMessageListener(batchedResultsListener); + + // Generate a sample message of the specified size. + Message msg = + pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), + testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), + testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); + + // Send the requested number of messages, and wait until they have all been received. + long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); + int numReplies = pingClient.pingAndWaitForReply(msg, numPings, timeout, null); + + // Check that all the replies were received and log a fail if they were not. + if (numReplies < numPings) + { + tc.completeTest(false, 0); + } + + // Remove the chained message listener from the ping producer. + pingClient.removeChainedMessageListener(); + + // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up. + perCorrelationIds.remove(messageCorrelationId); + } + + /** Performs test fixture creation on a per thread basis. This will only be called once for each test thread. */ + public void threadSetUp() + { + _logger.debug("public void threadSetUp(): called"); + + try + { + // Call the set up method in the super class. This creates a PingClient pinger. + super.threadSetUp(); + + // Create the chained message listener, only if it has not already been created. This is set up with the + // batch size property, to tell it what batch size to output results on. A synchronized block is used to + // ensure that only one thread creates this. + synchronized (this) + { + if (batchedResultsListener == null) + { + int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME)); + batchedResultsListener = new BatchedResultsListener(batchSize); + } + } + + // Get the set up that the super class created. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Register the chained message listener on the pinger to do its asynchronous test timings from. + perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener); + } + catch (Exception e) + { + _logger.warn("There was an exception during per thread setup.", e); + } + } + + /** + * BatchedResultsListener is a {@link org.apache.qpid.requestreply.PingPongProducer.ChainedMessageListener} that can + * be attached to the pinger, in order to receive notifications about every message received and the number + * remaining to be received. Whenever the number remaining crosses a batch size boundary this results listener + * outputs a test timing for the actual number of messages received in the current batch. + */ + private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener + { + /** The test results logging batch size. */ + int _batchSize; + private boolean _strictAMQP; + + /** + * Creates a results listener on the specified batch size. + * + * @param batchSize The batch size to use. + */ + public BatchedResultsListener(int batchSize) + { + _batchSize = batchSize; + _strictAMQP = + Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, + AMQSession.STRICT_AMQP_DEFAULT)); + } + + /** + * This callback method is called from all of the pingers that this test creates. It uses the correlation id + * from the message to identify the timing controller for the test thread that was responsible for sending those + * messages. + * + * @param message The message. + * @param remainingCount The count of messages remaining to be received with a particular correlation id. + * + * @throws javax.jms.JMSException Any underlying JMSException is allowed to fall through. + */ + public void onMessage(Message message, int remainingCount, long latency) throws JMSException + { + _logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount + "): called"); + + // Check if a batch boundary has been crossed. + if ((remainingCount % _batchSize) == 0) + { + // Extract the correlation id from the message. + String correlationId = message.getJMSCorrelationID(); + + // Get the details for the correlation id and check that they are not null. They can become null + // if a test times out. + PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId); + if (perCorrelationId != null) + { + // Get the timing controller and expected count for this correlation id. + TimingController tc = perCorrelationId._tc; + int expected = perCorrelationId._expectedCount; + + // Calculate how many messages were actually received in the last batch. This will be the batch size + // except where the number expected is not a multiple of the batch size and this is the first remaining + // count to cross a batch size boundary, in which case it will be the number expected modulo the batch + // size. + int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize; + + // Register a test result for the correlation id. + try + { + tc.completeTest(true, receivedInBatch, latency); + } + catch (InterruptedException e) + { + // Ignore this. It means the test runner wants to stop as soon as possible. + _logger.warn("Got InterruptedException.", e); + } + } + // Else ignore, test timed out. Should log a fail here? + } + } + } + + /** + * Holds state specific to each correlation id, needed to output test results. This consists of the count of the + * total expected number of messages, and the timing controller for the thread sending those message ids. + */ + private static class PerCorrelationId + { + public int _expectedCount; + public TimingController _tc; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java index 2879f0c322..04e723069a 100644 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java +++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java @@ -1,93 +1,93 @@ -/* - * - * 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.ping; - -import java.util.Properties; - -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.ObjectMessage; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.message.TestMessageFactory; -import org.apache.qpid.util.CommandLineParser; - -/** - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - */ -public class PingSendOnlyClient extends PingDurableClient -{ - private static final Logger log = Logger.getLogger(PingSendOnlyClient.class); - - public PingSendOnlyClient(Properties overrides) throws Exception - { - super(overrides); - } - - /** - * Starts the ping/wait/receive process. - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - // Create a ping producer overriding its defaults with all options passed on the command line. - Properties options = CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); - PingSendOnlyClient pingProducer = new PingSendOnlyClient(options); - - // Create a shutdown hook to terminate the ping-pong producer. - Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); - - // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. - // pingProducer.getConnection().setExceptionListener(pingProducer); - - // Run the test procedure. - int sent = pingProducer.send(); - pingProducer.waitForUser("Press return to close connection and quit."); - pingProducer.closeConnection(); - - System.exit(0); - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(1); - } - } - - public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException - { - Message msg = TestMessageFactory.newTextMessage(_producerSession, messageSize); - - // Timestamp the message in nanoseconds. - msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); - - return msg; - } -} +/* + * + * 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.ping; + +import java.util.Properties; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.ObjectMessage; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.message.TestMessageFactory; +import org.apache.qpid.util.CommandLineParser; + +/** + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + */ +public class PingSendOnlyClient extends PingDurableClient +{ + private static final Logger log = Logger.getLogger(PingSendOnlyClient.class); + + public PingSendOnlyClient(Properties overrides) throws Exception + { + super(overrides); + } + + /** + * Starts the ping/wait/receive process. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + // Create a ping producer overriding its defaults with all options passed on the command line. + Properties options = CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); + PingSendOnlyClient pingProducer = new PingSendOnlyClient(options); + + // Create a shutdown hook to terminate the ping-pong producer. + Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); + + // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. + // pingProducer.getConnection().setExceptionListener(pingProducer); + + // Run the test procedure. + int sent = pingProducer.send(); + pingProducer.waitForUser("Press return to close connection and quit."); + pingProducer.closeConnection(); + + System.exit(0); + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(1); + } + } + + public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException + { + Message msg = TestMessageFactory.newTextMessage(_producerSession, messageSize); + + // Timestamp the message in nanoseconds. + msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); + + return msg; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java index fb071a87fe..94b8ea662e 100644 --- a/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java +++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java @@ -1,196 +1,196 @@ -/* - * - * 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.ping; - -import junit.framework.Assert; -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.requestreply.PingPongProducer; - -import org.apache.qpid.junit.extensions.AsymptoticTestCase; -import org.apache.qpid.junit.extensions.TestThreadAware; -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -/** - * PingTestPerf is a ping test, that has been written with the intention of being scaled up to run many times - * simultaneously to simluate many clients/producers/connections. - * - *

          A single run of the test using the default JUnit test runner will result in the sending and timing of a single - * full round trip ping. This test may be scaled up using a suitable JUnit test runner. - * - *

          The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a - * temporary queue for replies. This setup is only established once for all the test repeats/threads that may be run, - * except if the connection is lost in which case an attempt to re-establish the setup is made. - * - *

          The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that - * is the name of the temporary queue, fires off a message on the original queue and waits for a response on the - * temporary queue. - * - *

          Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - */ -public class PingTestPerf extends AsymptoticTestCase implements TestThreadAware -{ - private static Logger _logger = Logger.getLogger(PingTestPerf.class); - - /** Thread local to hold the per-thread test setup fields. */ - ThreadLocal threadSetup = new ThreadLocal(); - - /** Holds a property reader to extract the test parameters from. */ - protected ParsedProperties testParameters = - TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); - - public PingTestPerf(String name) - { - super(name); - - _logger.debug("testParameters = " + testParameters); - } - - /** - * Compile all the tests into a test suite. - * @return The test method testPingOk. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Ping Performance Tests"); - - // Run performance tests in read committed mode. - suite.addTest(new PingTestPerf("testPingOk")); - - return suite; - } - - public void testPingOk(int numPings) throws Exception - { - if (numPings == 0) - { - Assert.fail("Number of pings requested was zero."); - } - - // Get the per thread test setup to run the test through. - PerThreadSetup perThreadSetup = threadSetup.get(); - - if (perThreadSetup == null) - { - Assert.fail("Could not get per thread test setup, it was null."); - } - - // Generate a sample message. This message is already time stamped and has its reply-to destination set. - Message msg = - perThreadSetup._pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), - testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), - testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); - - // start the test - long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); - int numReplies = perThreadSetup._pingClient.pingAndWaitForReply(msg, numPings, timeout, null); - - // Fail the test if the timeout was exceeded. - if (numReplies != perThreadSetup._pingClient.getExpectedNumPings(numPings)) - { - Assert.fail("The ping timed out after " + timeout + " ms. Messages Sent = " + numPings + ", MessagesReceived = " - + numReplies); - } - } - - /** - * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. - */ - public void threadSetUp() - { - _logger.debug("public void threadSetUp(): called"); - - try - { - PerThreadSetup perThreadSetup = new PerThreadSetup(); - - // This is synchronized because there is a race condition, which causes one connection to sleep if - // all threads try to create connection concurrently. - synchronized (this) - { - // Establish a client to ping a Destination and listen the reply back from same Destination - perThreadSetup._pingClient = new PingClient(testParameters); - perThreadSetup._pingClient.establishConnection(true, true); - } - // Start the client connection - perThreadSetup._pingClient.start(); - - // Attach the per-thread set to the thread. - threadSetup.set(perThreadSetup); - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - - /** - * Performs test fixture clean - */ - public void threadTearDown() - { - _logger.debug("public void threadTearDown(): called"); - - try - { - // Get the per thread test fixture. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Close the pingers so that it cleans up its connection cleanly. - synchronized (this) - { - if ((perThreadSetup != null) && (perThreadSetup._pingClient != null)) - { - perThreadSetup._pingClient.close(); - } - } - } - catch (JMSException e) - { - _logger.warn("There was an exception during per thread tear down."); - } - finally - { - // Ensure the per thread fixture is reclaimed. - threadSetup.remove(); - } - } - - protected static class PerThreadSetup - { - /** - * Holds the test ping client. - */ - protected PingClient _pingClient; - protected String _correlationId; - } -} +/* + * + * 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.ping; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.requestreply.PingPongProducer; + +import org.apache.qpid.junit.extensions.AsymptoticTestCase; +import org.apache.qpid.junit.extensions.TestThreadAware; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +/** + * PingTestPerf is a ping test, that has been written with the intention of being scaled up to run many times + * simultaneously to simluate many clients/producers/connections. + * + *

          A single run of the test using the default JUnit test runner will result in the sending and timing of a single + * full round trip ping. This test may be scaled up using a suitable JUnit test runner. + * + *

          The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a + * temporary queue for replies. This setup is only established once for all the test repeats/threads that may be run, + * except if the connection is lost in which case an attempt to re-establish the setup is made. + * + *

          The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that + * is the name of the temporary queue, fires off a message on the original queue and waits for a response on the + * temporary queue. + * + *

          Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + */ +public class PingTestPerf extends AsymptoticTestCase implements TestThreadAware +{ + private static Logger _logger = Logger.getLogger(PingTestPerf.class); + + /** Thread local to hold the per-thread test setup fields. */ + ThreadLocal threadSetup = new ThreadLocal(); + + /** Holds a property reader to extract the test parameters from. */ + protected ParsedProperties testParameters = + TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); + + public PingTestPerf(String name) + { + super(name); + + _logger.debug("testParameters = " + testParameters); + } + + /** + * Compile all the tests into a test suite. + * @return The test method testPingOk. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Ping Performance Tests"); + + // Run performance tests in read committed mode. + suite.addTest(new PingTestPerf("testPingOk")); + + return suite; + } + + public void testPingOk(int numPings) throws Exception + { + if (numPings == 0) + { + Assert.fail("Number of pings requested was zero."); + } + + // Get the per thread test setup to run the test through. + PerThreadSetup perThreadSetup = threadSetup.get(); + + if (perThreadSetup == null) + { + Assert.fail("Could not get per thread test setup, it was null."); + } + + // Generate a sample message. This message is already time stamped and has its reply-to destination set. + Message msg = + perThreadSetup._pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0), + testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), + testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); + + // start the test + long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME)); + int numReplies = perThreadSetup._pingClient.pingAndWaitForReply(msg, numPings, timeout, null); + + // Fail the test if the timeout was exceeded. + if (numReplies != perThreadSetup._pingClient.getExpectedNumPings(numPings)) + { + Assert.fail("The ping timed out after " + timeout + " ms. Messages Sent = " + numPings + ", MessagesReceived = " + + numReplies); + } + } + + /** + * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. + */ + public void threadSetUp() + { + _logger.debug("public void threadSetUp(): called"); + + try + { + PerThreadSetup perThreadSetup = new PerThreadSetup(); + + // This is synchronized because there is a race condition, which causes one connection to sleep if + // all threads try to create connection concurrently. + synchronized (this) + { + // Establish a client to ping a Destination and listen the reply back from same Destination + perThreadSetup._pingClient = new PingClient(testParameters); + perThreadSetup._pingClient.establishConnection(true, true); + } + // Start the client connection + perThreadSetup._pingClient.start(); + + // Attach the per-thread set to the thread. + threadSetup.set(perThreadSetup); + } + catch (Exception e) + { + _logger.warn("There was an exception during per thread setup.", e); + } + } + + /** + * Performs test fixture clean + */ + public void threadTearDown() + { + _logger.debug("public void threadTearDown(): called"); + + try + { + // Get the per thread test fixture. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Close the pingers so that it cleans up its connection cleanly. + synchronized (this) + { + if ((perThreadSetup != null) && (perThreadSetup._pingClient != null)) + { + perThreadSetup._pingClient.close(); + } + } + } + catch (JMSException e) + { + _logger.warn("There was an exception during per thread tear down."); + } + finally + { + // Ensure the per thread fixture is reclaimed. + threadSetup.remove(); + } + } + + protected static class PerThreadSetup + { + /** + * Holds the test ping client. + */ + protected PingClient _pingClient; + protected String _correlationId; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java index 0712557383..8e010ccf07 100644 --- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java +++ b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java @@ -1,453 +1,453 @@ -/* - * - * 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.requestreply; - -import java.io.IOException; -import java.net.InetAddress; -import java.text.SimpleDateFormat; -import java.util.Date; - -import javax.jms.*; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.AMQTopic; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.jms.Session; -import org.apache.qpid.topic.Config; -import org.apache.qpid.exchange.ExchangeDefaults; - -/** - * PingPongBouncer is a message listener the bounces back messages to their reply to destination. This is used to return - * ping messages generated by {@link org.apache.qpid.requestreply.PingPongProducer} but could be used for other purposes - * too. - * - *

          The correlation id from the received message is extracted, and placed into the reply as the correlation id. Messages - * are bounced back to their reply-to destination. The original sender of the message has the option to use either a unique - * temporary queue or the correlation id to correlate the original message to the reply. - * - *

          There is a verbose mode flag which causes information about each ping to be output to the console - * (info level logging, so usually console). This can be helpfull to check the bounce backs are happening but should - * be disabled for real timing tests as writing to the console will slow things down. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Bounce back messages to their reply to destination. - *
          Provide command line invocation to start the bounce back on a configurable broker url. - *
          - * - * @todo Replace the command line parsing with a neater tool. - * - * @todo Make verbose accept a number of messages, only prints to console every X messages. - */ -public class PingPongBouncer implements MessageListener -{ - private static final Logger _logger = Logger.getLogger(PingPongBouncer.class); - - /** The default prefetch size for the message consumer. */ - private static final int PREFETCH = 1; - - /** The default no local flag for the message consumer. */ - private static final boolean NO_LOCAL = true; - - private static final String DEFAULT_DESTINATION_NAME = "ping"; - - /** The default exclusive flag for the message consumer. */ - private static final boolean EXCLUSIVE = false; - - /** A convenient formatter to use when time stamping output. */ - protected static final SimpleDateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); - - /** Used to indicate that the reply generator should log timing info to the console (logger info level). */ - private boolean _verbose = false; - - /** Determines whether this bounce back client bounces back messages persistently. */ - private boolean _persistent = false; - - private Destination _consumerDestination; - - /** Keeps track of the response destination of the previous message for the last reply to producer cache. */ - private Destination _lastResponseDest; - - /** The producer for sending replies with. */ - private MessageProducer _replyProducer; - - /** The consumer controlSession. */ - private Session _consumerSession; - - /** The producer controlSession. */ - private Session _producerSession; - - /** Holds the connection to the broker. */ - private AMQConnection _connection; - - /** Flag used to indicate if this is a point to point or pub/sub ping client. */ - private boolean _isPubSub = false; - - /** - * This flag is used to indicate that the user should be prompted to kill a broker, in order to test - * failover, immediately before committing a transaction. - */ - protected boolean _failBeforeCommit = false; - - /** - * This flag is used to indicate that the user should be prompted to a kill a broker, in order to test - * failover, immediate after committing a transaction. - */ - protected boolean _failAfterCommit = false; - - /** - * Creates a PingPongBouncer on the specified producer and consumer sessions. - * - * @param brokerDetails The addresses of the brokers to connect to. - * @param username The broker username. - * @param password The broker password. - * @param virtualpath The virtual host name within the broker. - * @param destinationName The name of the queue to receive pings on - * (or root of the queue name where many queues are generated). - * @param persistent A flag to indicate that persistent message should be used. - * @param transacted A flag to indicate that pings should be sent within transactions. - * @param selector A message selector to filter received pings with. - * @param verbose A flag to indicate that message timings should be sent to the console. - * - * @throws Exception All underlying exceptions allowed to fall through. This is only test code... - */ - public PingPongBouncer(String brokerDetails, String username, String password, String virtualpath, - String destinationName, boolean persistent, boolean transacted, String selector, boolean verbose, - boolean pubsub) throws Exception - { - // Create a client id to uniquely identify this client. - InetAddress address = InetAddress.getLocalHost(); - String clientId = address.getHostName() + System.currentTimeMillis(); - _verbose = verbose; - _persistent = persistent; - setPubSub(pubsub); - // Connect to the broker. - setConnection(new AMQConnection(brokerDetails, username, password, clientId, virtualpath)); - _logger.info("Connected with URL:" + getConnection().toURL()); - - // Set up the failover notifier. - getConnection().setConnectionListener(new FailoverNotifier()); - - // Create a controlSession to listen for messages on and one to send replies on, transactional depending on the - // command line option. - _consumerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); - _producerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); - - // Create the queue to listen for message on. - createConsumerDestination(destinationName); - MessageConsumer consumer = - _consumerSession.createConsumer(_consumerDestination, PREFETCH, NO_LOCAL, EXCLUSIVE, selector); - - // Create a producer for the replies, without a default destination. - _replyProducer = _producerSession.createProducer(null); - _replyProducer.setDisableMessageTimestamp(true); - _replyProducer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); - - // Set this up to listen for messages on the queue. - consumer.setMessageListener(this); - } - - /** - * Starts a stand alone ping-pong client running in verbose mode. - * - * @param args - */ - public static void main(String[] args) throws Exception - { - System.out.println("Starting..."); - - // Display help on the command line. - if (args.length == 0) - { - _logger.info("Running test with default values..."); - //usage(); - //System.exit(0); - } - - // Extract all command line parameters. - Config config = new Config(); - config.setOptions(args); - String brokerDetails = config.getHost() + ":" + config.getPort(); - String virtualpath = "test"; - String destinationName = config.getDestination(); - if (destinationName == null) - { - destinationName = DEFAULT_DESTINATION_NAME; - } - - String selector = config.getSelector(); - boolean transacted = config.isTransacted(); - boolean persistent = config.usePersistentMessages(); - boolean pubsub = config.isPubSub(); - boolean verbose = true; - - //String selector = null; - - // Instantiate the ping pong client with the command line options and start it running. - PingPongBouncer pingBouncer = - new PingPongBouncer(brokerDetails, "guest", "guest", virtualpath, destinationName, persistent, transacted, - selector, verbose, pubsub); - pingBouncer.getConnection().start(); - - System.out.println("Waiting..."); - } - - private static void usage() - { - System.err.println("Usage: PingPongBouncer \n" + "-host : broker host\n" + "-port : broker port\n" - + "-destinationname : queue/topic name\n" + "-transacted : (true/false). Default is false\n" - + "-persistent : (true/false). Default is false\n" - + "-pubsub : (true/false). Default is false\n" + "-selector : selector string\n"); - } - - /** - * This is a callback method that is notified of all messages for which this has been registered as a message - * listener on a message consumer. It sends a reply (pong) to all messages it receieves on the reply to - * destination of the message. - * - * @param message The message that triggered this callback. - */ - public void onMessage(Message message) - { - try - { - String messageCorrelationId = message.getJMSCorrelationID(); - if (_verbose) - { - _logger.info(timestampFormatter.format(new Date()) + ": Got ping with correlation id, " - + messageCorrelationId); - } - - // Get the reply to destination from the message and check it is set. - Destination responseDest = message.getJMSReplyTo(); - - if (responseDest == null) - { - _logger.debug("Cannot send reply because reply-to destination is null."); - - return; - } - - // Spew out some timing information if verbose mode is on. - if (_verbose) - { - Long timestamp = message.getLongProperty("timestamp"); - - if (timestamp != null) - { - long diff = System.currentTimeMillis() - timestamp; - _logger.info("Time to bounce point: " + diff); - } - } - - // Correlate the reply to the original. - message.setJMSCorrelationID(messageCorrelationId); - - // Send the receieved message as the pong reply. - _replyProducer.send(responseDest, message); - - if (_verbose) - { - _logger.info(timestampFormatter.format(new Date()) + ": Sent reply with correlation id, " - + messageCorrelationId); - } - - // Commit the transaction if running in transactional mode. - commitTx(_producerSession); - } - catch (JMSException e) - { - _logger.debug("There was a JMSException: " + e.getMessage(), e); - } - } - - /** - * Gets the underlying connection that this ping client is running on. - * - * @return The underlying connection that this ping client is running on. - */ - public AMQConnection getConnection() - { - return _connection; - } - - /** - * Sets the connection that this ping client is using. - * - * @param connection The ping connection. - */ - public void setConnection(AMQConnection connection) - { - this._connection = connection; - } - - /** - * Sets or clears the pub/sub flag to indiciate whether this client is pinging a queue or a topic. - * - * @param pubsub true if this client is pinging a topic, false if it is pinging a queue. - */ - public void setPubSub(boolean pubsub) - { - _isPubSub = pubsub; - } - - /** - * Checks whether this client is a p2p or pub/sub ping client. - * - * @return true if this client is pinging a topic, false if it is pinging a queue. - */ - public boolean isPubSub() - { - return _isPubSub; - } - - /** - * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not - * a transactional controlSession, this method does nothing. - * - *

          If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the - * commit is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker - * after the commit is applied. - * - * @throws javax.jms.JMSException If the commit fails and then the rollback fails. - */ - protected void commitTx(Session session) throws JMSException - { - if (session.getTransacted()) - { - try - { - if (_failBeforeCommit) - { - _logger.debug("Failing Before Commit"); - doFailover(); - } - - session.commit(); - - if (_failAfterCommit) - { - _logger.debug("Failing After Commit"); - doFailover(); - } - - _logger.debug("Session Commited."); - } - catch (JMSException e) - { - _logger.trace("JMSException on commit:" + e.getMessage(), e); - - try - { - session.rollback(); - _logger.debug("Message rolled back."); - } - catch (JMSException jmse) - { - _logger.trace("JMSE on rollback:" + jmse.getMessage(), jmse); - - // Both commit and rollback failed. Throw the rollback exception. - throw jmse; - } - } - } - } - - /** - * Prompts the user to terminate the named broker, in order to test failover functionality. This method will block - * until the user supplied some input on the terminal. - * - * @param broker The name of the broker to terminate. - */ - protected void doFailover(String broker) - { - System.out.println("Kill Broker " + broker + " now."); - try - { - System.in.read(); - } - catch (IOException e) - { } - - System.out.println("Continuing."); - } - - /** - * Prompts the user to terminate the broker, in order to test failover functionality. This method will block - * until the user supplied some input on the terminal. - */ - protected void doFailover() - { - System.out.println("Kill Broker now."); - try - { - System.in.read(); - } - catch (IOException e) - { } - - System.out.println("Continuing."); - - } - - private void createConsumerDestination(String name) - { - if (isPubSub()) - { - _consumerDestination = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, name); - } - else - { - _consumerDestination = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, name); - } - } - - /** - * A connection listener that logs out any failover complete events. Could do more interesting things with this - * at some point... - */ - public static class FailoverNotifier implements ConnectionListener - { - public void bytesSent(long count) - { } - - public void bytesReceived(long count) - { } - - public boolean preFailover(boolean redirect) - { - return true; - } - - public boolean preResubscribe() - { - return true; - } - - public void failoverComplete() - { - _logger.info("App got failover complete callback."); - } - } -} +/* + * + * 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.requestreply; + +import java.io.IOException; +import java.net.InetAddress; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.jms.*; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.jms.Session; +import org.apache.qpid.topic.Config; +import org.apache.qpid.exchange.ExchangeDefaults; + +/** + * PingPongBouncer is a message listener the bounces back messages to their reply to destination. This is used to return + * ping messages generated by {@link org.apache.qpid.requestreply.PingPongProducer} but could be used for other purposes + * too. + * + *

          The correlation id from the received message is extracted, and placed into the reply as the correlation id. Messages + * are bounced back to their reply-to destination. The original sender of the message has the option to use either a unique + * temporary queue or the correlation id to correlate the original message to the reply. + * + *

          There is a verbose mode flag which causes information about each ping to be output to the console + * (info level logging, so usually console). This can be helpfull to check the bounce backs are happening but should + * be disabled for real timing tests as writing to the console will slow things down. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Bounce back messages to their reply to destination. + *
          Provide command line invocation to start the bounce back on a configurable broker url. + *
          + * + * @todo Replace the command line parsing with a neater tool. + * + * @todo Make verbose accept a number of messages, only prints to console every X messages. + */ +public class PingPongBouncer implements MessageListener +{ + private static final Logger _logger = Logger.getLogger(PingPongBouncer.class); + + /** The default prefetch size for the message consumer. */ + private static final int PREFETCH = 1; + + /** The default no local flag for the message consumer. */ + private static final boolean NO_LOCAL = true; + + private static final String DEFAULT_DESTINATION_NAME = "ping"; + + /** The default exclusive flag for the message consumer. */ + private static final boolean EXCLUSIVE = false; + + /** A convenient formatter to use when time stamping output. */ + protected static final SimpleDateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); + + /** Used to indicate that the reply generator should log timing info to the console (logger info level). */ + private boolean _verbose = false; + + /** Determines whether this bounce back client bounces back messages persistently. */ + private boolean _persistent = false; + + private Destination _consumerDestination; + + /** Keeps track of the response destination of the previous message for the last reply to producer cache. */ + private Destination _lastResponseDest; + + /** The producer for sending replies with. */ + private MessageProducer _replyProducer; + + /** The consumer controlSession. */ + private Session _consumerSession; + + /** The producer controlSession. */ + private Session _producerSession; + + /** Holds the connection to the broker. */ + private AMQConnection _connection; + + /** Flag used to indicate if this is a point to point or pub/sub ping client. */ + private boolean _isPubSub = false; + + /** + * This flag is used to indicate that the user should be prompted to kill a broker, in order to test + * failover, immediately before committing a transaction. + */ + protected boolean _failBeforeCommit = false; + + /** + * This flag is used to indicate that the user should be prompted to a kill a broker, in order to test + * failover, immediate after committing a transaction. + */ + protected boolean _failAfterCommit = false; + + /** + * Creates a PingPongBouncer on the specified producer and consumer sessions. + * + * @param brokerDetails The addresses of the brokers to connect to. + * @param username The broker username. + * @param password The broker password. + * @param virtualpath The virtual host name within the broker. + * @param destinationName The name of the queue to receive pings on + * (or root of the queue name where many queues are generated). + * @param persistent A flag to indicate that persistent message should be used. + * @param transacted A flag to indicate that pings should be sent within transactions. + * @param selector A message selector to filter received pings with. + * @param verbose A flag to indicate that message timings should be sent to the console. + * + * @throws Exception All underlying exceptions allowed to fall through. This is only test code... + */ + public PingPongBouncer(String brokerDetails, String username, String password, String virtualpath, + String destinationName, boolean persistent, boolean transacted, String selector, boolean verbose, + boolean pubsub) throws Exception + { + // Create a client id to uniquely identify this client. + InetAddress address = InetAddress.getLocalHost(); + String clientId = address.getHostName() + System.currentTimeMillis(); + _verbose = verbose; + _persistent = persistent; + setPubSub(pubsub); + // Connect to the broker. + setConnection(new AMQConnection(brokerDetails, username, password, clientId, virtualpath)); + _logger.info("Connected with URL:" + getConnection().toURL()); + + // Set up the failover notifier. + getConnection().setConnectionListener(new FailoverNotifier()); + + // Create a controlSession to listen for messages on and one to send replies on, transactional depending on the + // command line option. + _consumerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); + _producerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE); + + // Create the queue to listen for message on. + createConsumerDestination(destinationName); + MessageConsumer consumer = + _consumerSession.createConsumer(_consumerDestination, PREFETCH, NO_LOCAL, EXCLUSIVE, selector); + + // Create a producer for the replies, without a default destination. + _replyProducer = _producerSession.createProducer(null); + _replyProducer.setDisableMessageTimestamp(true); + _replyProducer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); + + // Set this up to listen for messages on the queue. + consumer.setMessageListener(this); + } + + /** + * Starts a stand alone ping-pong client running in verbose mode. + * + * @param args + */ + public static void main(String[] args) throws Exception + { + System.out.println("Starting..."); + + // Display help on the command line. + if (args.length == 0) + { + _logger.info("Running test with default values..."); + //usage(); + //System.exit(0); + } + + // Extract all command line parameters. + Config config = new Config(); + config.setOptions(args); + String brokerDetails = config.getHost() + ":" + config.getPort(); + String virtualpath = "test"; + String destinationName = config.getDestination(); + if (destinationName == null) + { + destinationName = DEFAULT_DESTINATION_NAME; + } + + String selector = config.getSelector(); + boolean transacted = config.isTransacted(); + boolean persistent = config.usePersistentMessages(); + boolean pubsub = config.isPubSub(); + boolean verbose = true; + + //String selector = null; + + // Instantiate the ping pong client with the command line options and start it running. + PingPongBouncer pingBouncer = + new PingPongBouncer(brokerDetails, "guest", "guest", virtualpath, destinationName, persistent, transacted, + selector, verbose, pubsub); + pingBouncer.getConnection().start(); + + System.out.println("Waiting..."); + } + + private static void usage() + { + System.err.println("Usage: PingPongBouncer \n" + "-host : broker host\n" + "-port : broker port\n" + + "-destinationname : queue/topic name\n" + "-transacted : (true/false). Default is false\n" + + "-persistent : (true/false). Default is false\n" + + "-pubsub : (true/false). Default is false\n" + "-selector : selector string\n"); + } + + /** + * This is a callback method that is notified of all messages for which this has been registered as a message + * listener on a message consumer. It sends a reply (pong) to all messages it receieves on the reply to + * destination of the message. + * + * @param message The message that triggered this callback. + */ + public void onMessage(Message message) + { + try + { + String messageCorrelationId = message.getJMSCorrelationID(); + if (_verbose) + { + _logger.info(timestampFormatter.format(new Date()) + ": Got ping with correlation id, " + + messageCorrelationId); + } + + // Get the reply to destination from the message and check it is set. + Destination responseDest = message.getJMSReplyTo(); + + if (responseDest == null) + { + _logger.debug("Cannot send reply because reply-to destination is null."); + + return; + } + + // Spew out some timing information if verbose mode is on. + if (_verbose) + { + Long timestamp = message.getLongProperty("timestamp"); + + if (timestamp != null) + { + long diff = System.currentTimeMillis() - timestamp; + _logger.info("Time to bounce point: " + diff); + } + } + + // Correlate the reply to the original. + message.setJMSCorrelationID(messageCorrelationId); + + // Send the receieved message as the pong reply. + _replyProducer.send(responseDest, message); + + if (_verbose) + { + _logger.info(timestampFormatter.format(new Date()) + ": Sent reply with correlation id, " + + messageCorrelationId); + } + + // Commit the transaction if running in transactional mode. + commitTx(_producerSession); + } + catch (JMSException e) + { + _logger.debug("There was a JMSException: " + e.getMessage(), e); + } + } + + /** + * Gets the underlying connection that this ping client is running on. + * + * @return The underlying connection that this ping client is running on. + */ + public AMQConnection getConnection() + { + return _connection; + } + + /** + * Sets the connection that this ping client is using. + * + * @param connection The ping connection. + */ + public void setConnection(AMQConnection connection) + { + this._connection = connection; + } + + /** + * Sets or clears the pub/sub flag to indiciate whether this client is pinging a queue or a topic. + * + * @param pubsub true if this client is pinging a topic, false if it is pinging a queue. + */ + public void setPubSub(boolean pubsub) + { + _isPubSub = pubsub; + } + + /** + * Checks whether this client is a p2p or pub/sub ping client. + * + * @return true if this client is pinging a topic, false if it is pinging a queue. + */ + public boolean isPubSub() + { + return _isPubSub; + } + + /** + * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not + * a transactional controlSession, this method does nothing. + * + *

          If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the + * commit is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker + * after the commit is applied. + * + * @throws javax.jms.JMSException If the commit fails and then the rollback fails. + */ + protected void commitTx(Session session) throws JMSException + { + if (session.getTransacted()) + { + try + { + if (_failBeforeCommit) + { + _logger.debug("Failing Before Commit"); + doFailover(); + } + + session.commit(); + + if (_failAfterCommit) + { + _logger.debug("Failing After Commit"); + doFailover(); + } + + _logger.debug("Session Commited."); + } + catch (JMSException e) + { + _logger.trace("JMSException on commit:" + e.getMessage(), e); + + try + { + session.rollback(); + _logger.debug("Message rolled back."); + } + catch (JMSException jmse) + { + _logger.trace("JMSE on rollback:" + jmse.getMessage(), jmse); + + // Both commit and rollback failed. Throw the rollback exception. + throw jmse; + } + } + } + } + + /** + * Prompts the user to terminate the named broker, in order to test failover functionality. This method will block + * until the user supplied some input on the terminal. + * + * @param broker The name of the broker to terminate. + */ + protected void doFailover(String broker) + { + System.out.println("Kill Broker " + broker + " now."); + try + { + System.in.read(); + } + catch (IOException e) + { } + + System.out.println("Continuing."); + } + + /** + * Prompts the user to terminate the broker, in order to test failover functionality. This method will block + * until the user supplied some input on the terminal. + */ + protected void doFailover() + { + System.out.println("Kill Broker now."); + try + { + System.in.read(); + } + catch (IOException e) + { } + + System.out.println("Continuing."); + + } + + private void createConsumerDestination(String name) + { + if (isPubSub()) + { + _consumerDestination = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, name); + } + else + { + _consumerDestination = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, name); + } + } + + /** + * A connection listener that logs out any failover complete events. Could do more interesting things with this + * at some point... + */ + public static class FailoverNotifier implements ConnectionListener + { + public void bytesSent(long count) + { } + + public void bytesReceived(long count) + { } + + public boolean preFailover(boolean redirect) + { + return true; + } + + public boolean preResubscribe() + { + return true; + } + + public void failoverComplete() + { + _logger.info("App got failover complete callback."); + } + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java index f328675488..4d8a736ec8 100644 --- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java +++ b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java @@ -1,1720 +1,1720 @@ -/* - * - * 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.requestreply; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import org.apache.qpid.test.framework.TestUtils; - -import org.apache.qpid.junit.extensions.BatchedThrottle; -import org.apache.qpid.junit.extensions.Throttle; -import org.apache.qpid.junit.extensions.util.CommandLineParser; -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.*; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; - -import java.io.*; -import java.net.InetAddress; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -/** - * PingPongProducer is a client that sends test messages, and waits for replies to these messages. The replies may - * either be generated by another client (see {@link PingPongBouncer}, or an extension of it may be used that listens - * to its own messages and does not send replies (see {@link org.apache.qpid.ping.PingClient}). The intention of ping - * pong producer is that it is a swiss-army knife test client that makes almost every aspect of its behaviour - * configurable. - * - *

          The pings are sent with a reply-to field set to a single temporary queue, which is the same for all pings. This - * means that this class has to do some work to correlate pings with pongs; it expectes the original message correlation - * id in the ping to be bounced back in the reply correlation id. - * - *

          This ping tool accepts a vast number of configuration options, all of which are passed in to the constructor. It - * can ping topics or queues; ping multiple destinations; do persistent pings; send messages of any size; do pings within - * transactions; control the number of pings to send in each transaction; limit its sending rate; and perform failover - * testing. A complete list of accepted parameters, default values and comments on their usage is provided here: - * - *

          - *
          Parameters
          Parameter Default Comments - *
          messageSize 0 Message size in bytes. Not including any headers. - *
          destinationName ping The root name to use to generate destination names to ping. - *
          persistent false Determines whether peristent delivery is used. - *
          transacted false Determines whether messages are sent/received in transactions. - *
          broker tcp://localhost:5672 Determines the broker to connect to. - *
          virtualHost test Determines the virtual host to send all ping over. - *
          rate 0 The maximum rate (in hertz) to send messages at. 0 means no limit. - *
          verbose false The verbose flag for debugging. Prints to console on every message. - *
          pubsub false Whether to ping topics or queues. Uses p2p by default. - *
          failAfterCommit false Whether to prompt user to kill broker after a commit batch. - *
          failBeforeCommit false Whether to prompt user to kill broker before a commit batch. - *
          failAfterSend false Whether to prompt user to kill broker after a send. - *
          failBeforeSend false Whether to prompt user to kill broker before a send. - *
          failOnce true Whether to prompt for failover only once. - *
          username guest The username to access the broker with. - *
          password guest The password to access the broker with. - *
          selector null Not used. Defines a message selector to filter pings with. - *
          destinationCount 1 The number of destinations to send pings to. - *
          numConsumers 1 The number of consumers on each destination. - *
          timeout 30000 In milliseconds. The timeout to stop waiting for replies. - *
          commitBatchSize 1 The number of messages per transaction in transactional mode. - *
          uniqueDests true Whether each receivers only listens to one ping destination or all. - *
          durableDests false Whether or not durable destinations are used. - *
          ackMode AUTO_ACK The message acknowledgement mode. Possible values are: - * 0 - SESSION_TRANSACTED - * 1 - AUTO_ACKNOWLEDGE - * 2 - CLIENT_ACKNOWLEDGE - * 3 - DUPS_OK_ACKNOWLEDGE - * 257 - NO_ACKNOWLEDGE - * 258 - PRE_ACKNOWLEDGE - *
          consTransacted false Whether or not consumers use transactions. Defaults to the same value - * as the 'transacted' option if not seperately defined. - *
          consAckMode AUTO_ACK The message acknowledgement mode for consumers. Defaults to the same - * value as 'ackMode' if not seperately defined. - *
          maxPending 0 The maximum size in bytes, of messages sent but not yet received. - * Limits the volume of messages currently buffered on the client - * or broker. Can help scale test clients by limiting amount of buffered - * data to avoid out of memory errors. - *
          - * - *

          This implements the Runnable interface with a run method that implements an infinite ping loop. The ping loop - * does all its work through helper methods, so that code wishing to run a ping-pong cycle is not forced to do so by - * starting a new thread. The command line invocation does take advantage of this ping loop. A shutdown hook is also - * registered to terminate the ping-pong loop cleanly. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Provide a ping and wait for all responses cycle. - *
          Provide command line invocation to loop the ping cycle on a configurable broker url. - *
          - * - * @todo Use read/write lock in the onmessage, not for reading writing but to make use of a shared and exlcusive lock pair. - * Obtain read lock on all messages, before decrementing the message count. At the end of the on message method add a - * block that obtains the write lock for the very last message, releases any waiting producer. Means that the last - * message waits until all other messages have been handled before releasing producers but allows messages to be - * processed concurrently, unlike the current synchronized block. - */ -public class PingPongProducer implements Runnable, ExceptionListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(PingPongProducer.class); - - /** Holds the name of the property to determine whether of not client id is overridden at connection time. */ - public static final String OVERRIDE_CLIENT_ID_PROPNAME = "overrideClientId"; - - /** Holds the default value of the override client id flag. */ - public static final String OVERRIDE_CLIENT_ID_DEAFULT = "false"; - - /** Holds the name of the property to define the JNDI factory name with. */ - public static final String FACTORY_NAME_PROPNAME = "factoryName"; - - /** Holds the default JNDI name of the connection factory. */ - public static final String FACTORY_NAME_DEAFULT = "local"; - - /** Holds the name of the property to set the JNDI initial context properties with. */ - public static final String FILE_PROPERTIES_PROPNAME = "properties"; - - /** Holds the default file name of the JNDI initial context properties. */ - public static final String FILE_PROPERTIES_DEAFULT = "perftests.properties"; - - /** Holds the name of the property to get the test message size from. */ - public static final String MESSAGE_SIZE_PROPNAME = "messageSize"; - - /** Used to set up a default message size. */ - public static final int MESSAGE_SIZE_DEAFULT = 0; - - /** Holds the name of the property to get the ping queue name from. */ - public static final String PING_QUEUE_NAME_PROPNAME = "destinationName"; - - /** Holds the name of the default destination to send pings on. */ - public static final String PING_QUEUE_NAME_DEFAULT = "ping"; - - /** Holds the name of the property to get the queue name postfix from. */ - public static final String QUEUE_NAME_POSTFIX_PROPNAME = "queueNamePostfix"; - - /** Holds the default queue name postfix value. */ - public static final String QUEUE_NAME_POSTFIX_DEFAULT = ""; - - /** Holds the name of the property to get the test delivery mode from. */ - public static final String PERSISTENT_MODE_PROPNAME = "persistent"; - - /** Holds the message delivery mode to use for the test. */ - public static final boolean PERSISTENT_MODE_DEFAULT = false; - - /** Holds the name of the property to get the test transactional mode from. */ - public static final String TRANSACTED_PROPNAME = "transacted"; - - /** Holds the transactional mode to use for the test. */ - public static final boolean TRANSACTED_DEFAULT = false; - - /** Holds the name of the property to get the test consumer transacted mode from. */ - public static final String CONSUMER_TRANSACTED_PROPNAME = "consTransacted"; - - /** Holds the consumer transactional mode default setting. */ - public static final boolean CONSUMER_TRANSACTED_DEFAULT = false; - - /** Holds the name of the property to get the test broker url from. */ - public static final String BROKER_PROPNAME = "broker"; - - /** Holds the default broker url for the test. */ - public static final String BROKER_DEFAULT = "tcp://localhost:5672"; - - /** Holds the name of the property to get the test broker virtual path. */ - public static final String VIRTUAL_HOST_PROPNAME = "virtualHost"; - - /** Holds the default virtual path for the test. */ - public static final String VIRTUAL_HOST_DEFAULT = ""; - - /** Holds the name of the property to get the message rate from. */ - public static final String RATE_PROPNAME = "rate"; - - /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */ - public static final int RATE_DEFAULT = 0; - - /** Holds the name of the property to get the verbose mode proeprty from. */ - public static final String VERBOSE_PROPNAME = "verbose"; - - /** Holds the default verbose mode. */ - public static final boolean VERBOSE_DEFAULT = false; - - /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */ - public static final String PUBSUB_PROPNAME = "pubsub"; - - /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */ - public static final boolean PUBSUB_DEFAULT = false; - - /** Holds the name of the property to get the fail after commit flag from. */ - public static final String FAIL_AFTER_COMMIT_PROPNAME = "failAfterCommit"; - - /** Holds the default failover after commit test flag. */ - public static final boolean FAIL_AFTER_COMMIT_DEFAULT = false; - - /** Holds the name of the proeprty to get the fail before commit flag from. */ - public static final String FAIL_BEFORE_COMMIT_PROPNAME = "failBeforeCommit"; - - /** Holds the default failover before commit test flag. */ - public static final boolean FAIL_BEFORE_COMMIT_DEFAULT = false; - - /** Holds the name of the proeprty to get the fail after send flag from. */ - public static final String FAIL_AFTER_SEND_PROPNAME = "failAfterSend"; - - /** Holds the default failover after send test flag. */ - public static final boolean FAIL_AFTER_SEND_DEFAULT = false; - - /** Holds the name of the property to get the fail before send flag from. */ - public static final String FAIL_BEFORE_SEND_PROPNAME = "failBeforeSend"; - - /** Holds the default failover before send test flag. */ - public static final boolean FAIL_BEFORE_SEND_DEFAULT = false; - - /** Holds the name of the property to get the fail once flag from. */ - public static final String FAIL_ONCE_PROPNAME = "failOnce"; - - /** The default failover once flag, true means only do one failover, false means failover on every commit cycle. */ - public static final boolean FAIL_ONCE_DEFAULT = true; - - /** Holds the name of the property to get the broker access username from. */ - public static final String USERNAME_PROPNAME = "username"; - - /** Holds the default broker log on username. */ - public static final String USERNAME_DEFAULT = "guest"; - - /** Holds the name of the property to get the broker access password from. */ - public static final String PASSWORD_PROPNAME = "password"; - - /** Holds the default broker log on password. */ - public static final String PASSWORD_DEFAULT = "guest"; - - /** Holds the name of the proeprty to get the. */ - public static final String SELECTOR_PROPNAME = "selector"; - - /** Holds the default message selector. */ - public static final String SELECTOR_DEFAULT = ""; - - /** Holds the name of the property to get the destination count from. */ - public static final String DESTINATION_COUNT_PROPNAME = "destinationCount"; - - /** Defines the default number of destinations to ping. */ - public static final int DESTINATION_COUNT_DEFAULT = 1; - - /** Holds the name of the property to get the number of consumers per destination from. */ - public static final String NUM_CONSUMERS_PROPNAME = "numConsumers"; - - /** Defines the default number consumers per destination. */ - public static final int NUM_CONSUMERS_DEFAULT = 1; - - /** Holds the name of the property to get the waiting timeout for response messages. */ - public static final String TIMEOUT_PROPNAME = "timeout"; - - /** Default time to wait before assuming that a ping has timed out. */ - public static final long TIMEOUT_DEFAULT = 30000; - - /** Holds the name of the property to get the commit batch size from. */ - public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize"; - - /** Defines the default number of pings to send in each transaction when running transactionally. */ - public static final int TX_BATCH_SIZE_DEFAULT = 1; - - /** Holds the name of the property to get the unique destinations flag from. */ - public static final String UNIQUE_DESTS_PROPNAME = "uniqueDests"; - - /** Defines the default value for the unique destinations property. */ - public static final boolean UNIQUE_DESTS_DEFAULT = true; - - /** Holds the name of the property to get the durable destinations flag from. */ - public static final String DURABLE_DESTS_PROPNAME = "durableDests"; - - /** Defines the default value of the durable destinations flag. */ - public static final boolean DURABLE_DESTS_DEFAULT = false; - - /** Holds the name of the proeprty to get the message acknowledgement mode from. */ - public static final String ACK_MODE_PROPNAME = "ackMode"; - - /** Defines the default message acknowledgement mode. */ - public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; - - /** Holds the name of the property to get the consumers message acknowledgement mode from. */ - public static final String CONSUMER_ACK_MODE_PROPNAME = "consAckMode"; - - /** Defines the default consumers message acknowledgement mode. */ - public static final int CONSUMER_ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; - - /** Holds the name of the property to get the maximum pending message size setting from. */ - public static final String MAX_PENDING_PROPNAME = "maxPending"; - - /** Defines the default value for the maximum pending message size setting. 0 means no limit. */ - public static final int MAX_PENDING_DEFAULT = 0; - - /** Defines the default prefetch size to use when consuming messages. */ - public static final int PREFETCH_DEFAULT = 100; - - /** Defines the default value of the no local flag to use when consuming messages. */ - public static final boolean NO_LOCAL_DEFAULT = false; - - /** Defines the default value of the exclusive flag to use when consuming messages. */ - public static final boolean EXCLUSIVE_DEFAULT = false; - - /** Holds the name of the property to store nanosecond timestamps in ping messages with. */ - public static final String MESSAGE_TIMESTAMP_PROPNAME = "timestamp"; - - /** Holds the default configuration properties. */ - public static ParsedProperties defaults = new ParsedProperties(); - - static - { - defaults.setPropertyIfNull(OVERRIDE_CLIENT_ID_PROPNAME, OVERRIDE_CLIENT_ID_DEAFULT); - defaults.setPropertyIfNull(FILE_PROPERTIES_PROPNAME, FILE_PROPERTIES_DEAFULT); - defaults.setPropertyIfNull(FACTORY_NAME_PROPNAME, FACTORY_NAME_DEAFULT); - defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); - defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); - defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); - defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); - defaults.setPropertyIfNull(PING_QUEUE_NAME_PROPNAME, PING_QUEUE_NAME_DEFAULT); - defaults.setPropertyIfNull(QUEUE_NAME_POSTFIX_PROPNAME, QUEUE_NAME_POSTFIX_DEFAULT); - defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); - defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); - defaults.setPropertyIfNull(CONSUMER_TRANSACTED_PROPNAME, CONSUMER_TRANSACTED_DEFAULT); - defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); - defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); - defaults.setPropertyIfNull(CONSUMER_ACK_MODE_PROPNAME, CONSUMER_ACK_MODE_DEFAULT); - defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); - defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); - defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); - defaults.setPropertyIfNull(UNIQUE_DESTS_PROPNAME, UNIQUE_DESTS_DEFAULT); - defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); - defaults.setPropertyIfNull(FAIL_BEFORE_COMMIT_PROPNAME, FAIL_BEFORE_COMMIT_DEFAULT); - defaults.setPropertyIfNull(FAIL_AFTER_COMMIT_PROPNAME, FAIL_AFTER_COMMIT_DEFAULT); - defaults.setPropertyIfNull(FAIL_BEFORE_SEND_PROPNAME, FAIL_BEFORE_SEND_DEFAULT); - defaults.setPropertyIfNull(FAIL_AFTER_SEND_PROPNAME, FAIL_AFTER_SEND_DEFAULT); - defaults.setPropertyIfNull(FAIL_ONCE_PROPNAME, FAIL_ONCE_DEFAULT); - defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); - defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); - defaults.setPropertyIfNull(NUM_CONSUMERS_PROPNAME, NUM_CONSUMERS_DEFAULT); - defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); - defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); - defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); - } - - /** Allows setting of client ID on the connection, rather than through the connection URL. */ - protected boolean _overrideClientId; - - /** Holds the JNDI name of the JMS connection factory. */ - protected String _factoryName; - - /** Holds the name of the properties file to configure JNDI with. */ - protected String _fileProperties; - - /** Holds the broker url. */ - protected String _brokerDetails; - - /** Holds the username to access the broker with. */ - protected String _username; - - /** Holds the password to access the broker with. */ - protected String _password; - - /** Holds the virtual host on the broker to run the tests through. */ - protected String _virtualpath; - - /** Holds the root name from which to generate test destination names. */ - protected String _destinationName; - - /** Holds the default queue name postfix value. */ - protected String _queueNamePostfix; - - /** Holds the message selector to filter the pings with. */ - protected String _selector; - - /** Holds the producers transactional mode flag. */ - protected boolean _transacted; - - /** Holds the consumers transactional mode flag. */ - protected boolean _consTransacted; - - /** Determines whether this producer sends persistent messages. */ - protected boolean _persistent; - - /** Holds the acknowledgement mode used for the producers. */ - protected int _ackMode; - - /** Holds the acknowledgement mode setting for the consumers. */ - protected int _consAckMode; - - /** Determines what size of messages this producer sends. */ - protected int _messageSize; - - /** Used to indicate that the ping loop should print out whenever it pings. */ - protected boolean _verbose; - - /** Flag used to indicate if this is a point to point or pub/sub ping client. */ - protected boolean _isPubSub; - - /** Flag used to indicate if the destinations should be unique client. */ - protected boolean _isUnique; - - /** Flag used to indicate that durable destination should be used. */ - protected boolean _isDurable; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a commit. */ - protected boolean _failBeforeCommit; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a commit. */ - protected boolean _failAfterCommit; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a send. */ - protected boolean _failBeforeSend; - - /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a send. */ - protected boolean _failAfterSend; - - /** Flag used to indicate that failover prompting should only be done on the first commit, not on every commit. */ - protected boolean _failOnce; - - /** Holds the number of sends that should be performed in every transaction when using transactions. */ - protected int _txBatchSize; - - /** Holds the number of destinations to ping. */ - protected int _noOfDestinations; - - /** Holds the number of consumers per destination. */ - protected int _noOfConsumers; - - /** Holds the maximum send rate in herz. */ - protected int _rate; - - /** - * Holds the size of the maximum amount of pending data that the client should buffer, sending is suspended - * if this limit is breached. - */ - protected int _maxPendingSize; - - /** A source for providing sequential unique correlation ids. These will be unique within the same JVM. */ - private static AtomicLong _correlationIdGenerator = new AtomicLong(0L); - - /** A source for providing sequential unqiue ids for instances of this class to be identifed with. */ - private static AtomicInteger _instanceIdGenerator = new AtomicInteger(0); - - /** Holds this instances unique id. */ - private int instanceId; - - /** - * Holds a map from message ids to latches on which threads wait for replies. This map is shared accross multiple - * ping producers on the same JVM. - */ - private static Map perCorrelationIds = - Collections.synchronizedMap(new HashMap()); - - /** A convenient formatter to use when time stamping output. */ - protected static final DateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); - - /** Holds the connection for the message producer. */ - protected Connection _connection; - - /** Holds the consumer connections. */ - protected Connection[] _consumerConnection; - - /** Holds the controlSession on which ping replies are received. */ - protected Session[] _consumerSession; - - /** Holds the producer controlSession, needed to create ping messages. */ - protected Session _producerSession; - - /** Holds the destination where the response messages will arrive. */ - protected Destination _replyDestination; - - /** Holds the set of destinations that this ping producer pings. */ - protected List _pingDestinations; - - /** Used to restrict the sending rate to a specified limit. */ - protected Throttle _rateLimiter; - - /** Holds a message listener that this message listener chains all its messages to. */ - protected ChainedMessageListener _chainedMessageListener = null; - - /** - * This id generator is used to generate ids to append to the queue name to ensure that queues can be unique when - * creating multiple ping producers in the same JVM. - */ - protected static AtomicInteger _queueJVMSequenceID = new AtomicInteger(); - - /** - * This id generator is used to generates ids that are only unique within this pinger. Creating multiple pingers - * on the same JVM using this id generator will allow them to ping on the same queues. - */ - protected AtomicInteger _queueSharedID = new AtomicInteger(); - - /** Used to tell the ping loop when to terminate, it only runs while this is true. */ - protected boolean _publish = true; - - /** Holds the message producer to send the pings through. */ - protected MessageProducer _producer; - - /** Holds the message consumer to receive the ping replies through. */ - protected MessageConsumer[] _consumer; - - /** The prompt to display when asking the user to kill the broker for failover testing. */ - private static final String KILL_BROKER_PROMPT = "Kill broker now, then press Return."; - - /** Holds the name for this test client to be identified to the broker with. */ - private String _clientID; - - /** Keeps count of the total messages sent purely for debugging purposes. */ - private static AtomicInteger numSent = new AtomicInteger(); - - /** - * Holds a monitor which is used to synchronize sender and receivers threads, where the sender has elected - * to wait until the number of unreceived message is reduced before continuing to send. This monitor is a - * fair SynchronousQueue becuase that provides fair scheduling, to ensure that all producer threads get an - * equal chance to produce messages. - */ - static final SynchronousQueue _sendPauseMonitor = new SynchronousQueue(true); - - /** Keeps a count of the number of message currently sent but not received. */ - static AtomicInteger _unreceived = new AtomicInteger(0); - - /** - * Creates a ping producer with the specified parameters, of which there are many. See the class level comments - * for details. This constructor creates a connection to the broker and creates producer and consumer sessions on - * it, to send and recieve its pings and replies on. - * - * @param overrides Properties containing any desired overrides to the defaults. - * - * @throws Exception Any exceptions are allowed to fall through. - */ - public PingPongProducer(Properties overrides) throws Exception - { - // log.debug("public PingPongProducer(Properties overrides = " + overrides + "): called"); - instanceId = _instanceIdGenerator.getAndIncrement(); - - // Create a set of parsed properties from the defaults overriden by the passed in values. - ParsedProperties properties = new ParsedProperties(defaults); - properties.putAll(overrides); - - // Extract the configuration properties to set the pinger up with. - _overrideClientId = properties.getPropertyAsBoolean(OVERRIDE_CLIENT_ID_PROPNAME); - _factoryName = properties.getProperty(FACTORY_NAME_PROPNAME); - _fileProperties = properties.getProperty(FILE_PROPERTIES_PROPNAME); - _brokerDetails = properties.getProperty(BROKER_PROPNAME); - _username = properties.getProperty(USERNAME_PROPNAME); - _password = properties.getProperty(PASSWORD_PROPNAME); - _virtualpath = properties.getProperty(VIRTUAL_HOST_PROPNAME); - _destinationName = properties.getProperty(PING_QUEUE_NAME_PROPNAME); - _queueNamePostfix = properties.getProperty(QUEUE_NAME_POSTFIX_PROPNAME); - _selector = properties.getProperty(SELECTOR_PROPNAME); - _transacted = properties.getPropertyAsBoolean(TRANSACTED_PROPNAME); - _consTransacted = properties.getPropertyAsBoolean(CONSUMER_TRANSACTED_PROPNAME); - _persistent = properties.getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME); - _messageSize = properties.getPropertyAsInteger(MESSAGE_SIZE_PROPNAME); - _verbose = properties.getPropertyAsBoolean(VERBOSE_PROPNAME); - _failAfterCommit = properties.getPropertyAsBoolean(FAIL_AFTER_COMMIT_PROPNAME); - _failBeforeCommit = properties.getPropertyAsBoolean(FAIL_BEFORE_COMMIT_PROPNAME); - _failAfterSend = properties.getPropertyAsBoolean(FAIL_AFTER_SEND_PROPNAME); - _failBeforeSend = properties.getPropertyAsBoolean(FAIL_BEFORE_SEND_PROPNAME); - _failOnce = properties.getPropertyAsBoolean(FAIL_ONCE_PROPNAME); - _txBatchSize = properties.getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME); - _noOfDestinations = properties.getPropertyAsInteger(DESTINATION_COUNT_PROPNAME); - _noOfConsumers = properties.getPropertyAsInteger(NUM_CONSUMERS_PROPNAME); - _rate = properties.getPropertyAsInteger(RATE_PROPNAME); - _isPubSub = properties.getPropertyAsBoolean(PUBSUB_PROPNAME); - _isUnique = properties.getPropertyAsBoolean(UNIQUE_DESTS_PROPNAME); - _isDurable = properties.getPropertyAsBoolean(DURABLE_DESTS_PROPNAME); - _ackMode = _transacted ? 0 : properties.getPropertyAsInteger(ACK_MODE_PROPNAME); - _consAckMode = _consTransacted ? 0 : properties.getPropertyAsInteger(CONSUMER_ACK_MODE_PROPNAME); - _maxPendingSize = properties.getPropertyAsInteger(MAX_PENDING_PROPNAME); - - // Check that one or more destinations were specified. - if (_noOfDestinations < 1) - { - throw new IllegalArgumentException("There must be at least one destination."); - } - - // Set up a throttle to control the send rate, if a rate > 0 is specified. - if (_rate > 0) - { - _rateLimiter = new BatchedThrottle(); - _rateLimiter.setRate(_rate); - } - - // Create the connection and message producers/consumers. - // establishConnection(true, true); - } - - /** - * Establishes a connection to the broker and creates message consumers and producers based on the parameters - * that this ping client was created with. - * - * @param producer Flag to indicate whether or not the producer should be set up. - * @param consumer Flag to indicate whether or not the consumers should be set up. - * - * @throws Exception Any exceptions are allowed to fall through. - */ - public void establishConnection(boolean producer, boolean consumer) throws Exception - { - // log.debug("public void establishConnection(): called"); - - // Generate a unique identifying name for this client, based on it ip address and the current time. - InetAddress address = InetAddress.getLocalHost(); - // _clientID = address.getHostName() + System.currentTimeMillis(); - _clientID = "perftest_" + instanceId; - - // Create a connection to the broker. - createConnection(_clientID); - - // Create transactional or non-transactional sessions, based on the command line arguments. - _producerSession = _connection.createSession(_transacted, _ackMode); - - _consumerSession = new Session[_noOfConsumers]; - - for (int i = 0; i < _noOfConsumers; i++) - { - _consumerSession[i] = _consumerConnection[i].createSession(_consTransacted, _consAckMode); - } - - // Create the destinations to send pings to and receive replies from. - _replyDestination = _consumerSession[0].createTemporaryQueue(); - createPingDestinations(_noOfDestinations, _selector, _destinationName, _isUnique, _isDurable); - - // Create the message producer only if instructed to. - if (producer) - { - createProducer(); - } - - // Create the message consumer only if instructed to. - if (consumer) - { - createReplyConsumers(getReplyDestinations(), _selector); - } - } - - /** - * Establishes a connection to the broker, based on the configuration parameters that this ping client was - * created with. - * - * @param clientID The clients identifier. - * - * @throws JMSException Underlying exceptions allowed to fall through. - * @throws NamingException Underlying exceptions allowed to fall through. - * @throws IOException Underlying exceptions allowed to fall through. - */ - protected void createConnection(String clientID) throws JMSException, NamingException, IOException - { - // _log.debug("protected void createConnection(String clientID = " + clientID + "): called"); - - // _log.debug("Creating a connection for the message producer."); - File propsFile = new File(_fileProperties); - InputStream is = new FileInputStream(propsFile); - Properties properties = new Properties(); - properties.load(is); - - Context context = new InitialContext(properties); - ConnectionFactory factory = (ConnectionFactory) context.lookup(_factoryName); - _connection = factory.createConnection(_username, _password); - - if (_overrideClientId) - { - _connection.setClientID(clientID); - } - - // _log.debug("Creating " + _noOfConsumers + " connections for the consumers."); - - _consumerConnection = new Connection[_noOfConsumers]; - - for (int i = 0; i < _noOfConsumers; i++) - { - _consumerConnection[i] = factory.createConnection(_username, _password); - // _consumerConnection[i].setClientID(clientID); - } - } - - /** - * Starts a ping-pong loop running from the command line. The bounce back client {@link PingPongBouncer} also needs - * to be started to bounce the pings back again. - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - try - { - Properties options = - CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); - - // Create a ping producer overriding its defaults with all options passed on the command line. - PingPongProducer pingProducer = new PingPongProducer(options); - pingProducer.establishConnection(true, true); - - // Start the ping producers dispatch thread running. - pingProducer._connection.start(); - - // Create a shutdown hook to terminate the ping-pong producer. - Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); - - // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. - pingProducer._connection.setExceptionListener(pingProducer); - - // Create the ping loop thread and run it until it is terminated by the shutdown hook or exception. - Thread pingThread = new Thread(pingProducer); - pingThread.run(); - pingThread.join(); - } - catch (Exception e) - { - System.err.println(e.getMessage()); - log.error("Top level handler caught execption.", e); - System.exit(1); - } - } - - /** - * Convenience method for a short pause. - * - * @param sleepTime The time in milliseconds to pause for. - */ - public static void pause(long sleepTime) - { - if (sleepTime > 0) - { - try - { - Thread.sleep(sleepTime); - } - catch (InterruptedException ie) - { } - } - } - - /** - * Gets all the reply destinations (to listen for replies on). In this case this will just be the single reply to - * destination of this pinger. - * - * @return The single reply to destination of this pinger, wrapped in a list. - */ - public List getReplyDestinations() - { - // log.debug("public List getReplyDestinations(): called"); - - List replyDestinations = new ArrayList(); - replyDestinations.add(_replyDestination); - - // log.debug("replyDestinations = " + replyDestinations); - - return replyDestinations; - } - - /** - * Creates the producer to send the pings on. This is created without a default destination. Its persistent delivery - * flag is set accoring the ping producer creation options. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - public void createProducer() throws JMSException - { - // log.debug("public void createProducer(): called"); - - _producer = (MessageProducer) _producerSession.createProducer(null); - _producer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); - - // log.debug("Created producer for " + (_persistent ? "persistent" : "non-persistent") + " messages."); - } - - /** - * Creates consumers for the specified number of destinations. The destinations themselves are also created by this - * method. - * - * @param noOfDestinations The number of destinations to create consumers for. - * @param selector The message selector to filter the consumers with. - * @param rootName The root of the name, or actual name if only one is being created. - * @param unique true to make the destinations unique to this pinger, false to share the - * numbering with all pingers on the same JVM. - * @param durable If the destinations are durable topics. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - public void createPingDestinations(int noOfDestinations, String selector, String rootName, boolean unique, - boolean durable) throws JMSException - { - /*log.debug("public void createPingDestinations(int noOfDestinations = " + noOfDestinations + ", String selector = " - + selector + ", String rootName = " + rootName + ", boolean unique = " + unique + ", boolean durable = " - + durable + "): called");*/ - - _pingDestinations = new ArrayList(); - - // Create the desired number of ping destinations and consumers for them. - // log.debug("Creating " + noOfDestinations + " destinations to ping."); - - for (int i = 0; i < noOfDestinations; i++) - { - Destination destination; - String id; - - // Generate an id, unique within this pinger or to the whole JVM depending on the unique flag. - if (unique) - { - // log.debug("Creating unique destinations."); - id = "_" + _queueJVMSequenceID.incrementAndGet() + "_" + _connection.getClientID(); - } - else - { - // log.debug("Creating shared destinations."); - id = "_" + _queueSharedID.incrementAndGet(); - } - - // Check if this is a pub/sub pinger, in which case create topics. - if (_isPubSub) - { - destination = _producerSession.createTopic(rootName + id); - // log.debug("Created non-durable topic " + destination); - - if (durable) - { - _producerSession.createDurableSubscriber((Topic) destination, _connection.getClientID()); - } - } - // Otherwise this is a p2p pinger, in which case create queues. - else - { - destination = _producerSession.createQueue(rootName + id + _queueNamePostfix); - // log.debug("Created queue " + destination); - } - - // Keep the destination. - _pingDestinations.add(destination); - } - } - - /** - * Creates consumers for the specified destinations and registers this pinger to listen to their messages. - * - * @param destinations The destinations to listen to. - * @param selector A selector to filter the messages with. - * - * @throws javax.jms.JMSException Any JMSExceptions are allowed to fall through. - */ - public void createReplyConsumers(Collection destinations, String selector) throws JMSException - { - /*log.debug("public void createReplyConsumers(Collection destinations = " + destinations - + ", String selector = " + selector + "): called");*/ - - log.debug("There are " + destinations.size() + " destinations."); - log.debug("Creating " + _noOfConsumers + " consumers on each destination."); - log.debug("Total number of consumers is: " + (destinations.size() * _noOfConsumers)); - - for (Destination destination : destinations) - { - _consumer = new MessageConsumer[_noOfConsumers]; - - for (int i = 0; i < _noOfConsumers; i++) - { - // Create a consumer for the destination and set this pinger to listen to its messages. - _consumer[i] = _consumerSession[i].createConsumer(destination, selector, NO_LOCAL_DEFAULT); - - final int consumerNo = i; - - _consumer[i].setMessageListener(new MessageListener() - { - public void onMessage(Message message) - { - onMessageWithConsumerNo(message, consumerNo); - } - }); - - log.debug("Set consumer " + i + " to listen to replies sent to destination: " + destination); - } - } - } - - /** - * Stores the received message in the replies map, then resets the boolean latch that a thread waiting for a - * correlating reply may be waiting on. This is only done if the reply has a correlation id that is expected in the - * replies map. - * - * @param message The received message. - * @param consumerNo The consumer number within this test pinger instance. - */ - public void onMessageWithConsumerNo(Message message, int consumerNo) - { - // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo = " + consumerNo + "): called"); - try - { - long now = System.nanoTime(); - long timestamp = getTimestamp(message); - long pingTime = now - timestamp; - - // NDC.push("id" + instanceId + "/cons" + consumerNo); - - // Extract the messages correlation id. - String correlationID = message.getJMSCorrelationID(); - // log.debug("correlationID = " + correlationID); - - // int num = message.getIntProperty("MSG_NUM"); - // log.info("Message " + num + " received."); - - boolean isRedelivered = message.getJMSRedelivered(); - // log.debug("isRedelivered = " + isRedelivered); - - if (!isRedelivered) - { - // Countdown on the traffic light if there is one for the matching correlation id. - PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); - - if (perCorrelationId != null) - { - CountDownLatch trafficLight = perCorrelationId.trafficLight; - - // Restart the timeout timer on every message. - perCorrelationId.timeOutStart = System.nanoTime(); - - // log.debug("Reply was expected, decrementing the latch for the id, " + correlationID); - - // Release waiting senders if there are some and using maxPending limit. - if ((_maxPendingSize > 0)) - { - // Decrement the count of sent but not yet received messages. - int unreceived = _unreceived.decrementAndGet(); - int unreceivedSize = - (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) - / (_isPubSub ? getConsumersPerDestination() : 1); - - // log.debug("unreceived = " + unreceived); - // log.debug("unreceivedSize = " + unreceivedSize); - - // synchronized (_sendPauseMonitor) - // { - if (unreceivedSize < _maxPendingSize) - { - _sendPauseMonitor.poll(); - } - // } - } - - // Decrement the countdown latch. Before this point, it is possible that two threads might enter this - // method simultanesouly with the same correlation id. Decrementing the latch in a synchronized block - // ensures that each thread will get a unique value for the remaining messages. - long trueCount; - long remainingCount; - - synchronized (trafficLight) - { - trafficLight.countDown(); - - trueCount = trafficLight.getCount(); - remainingCount = trueCount - 1; - - // NDC.push("/rem" + remainingCount); - - // log.debug("remainingCount = " + remainingCount); - // log.debug("trueCount = " + trueCount); - - // Commit on transaction batch size boundaries. At this point in time the waiting producer - // remains blocked, even on the last message. - // Commit count is divided by noOfConsumers in p2p mode, so that each consumer only commits on - // each batch boundary. For pub/sub each consumer gets every message so no division is done. - // When running in client ack mode, an ack is done instead of a commit, on the commit batch - // size boundaries. - long commitCount = _isPubSub ? remainingCount : (remainingCount / _noOfConsumers); - // log.debug("commitCount = " + commitCount); - - if ((commitCount % _txBatchSize) == 0) - { - if (_consAckMode == 2) - { - // log.debug("Doing client ack for consumer " + consumerNo + "."); - message.acknowledge(); - } - else - { - // log.debug("Trying commit for consumer " + consumerNo + "."); - commitTx(_consumerSession[consumerNo]); - // log.info("Tx committed on consumer " + consumerNo); - } - } - - // Forward the message and remaining count to any interested chained message listener. - if (_chainedMessageListener != null) - { - _chainedMessageListener.onMessage(message, (int) remainingCount, pingTime); - } - - // Check if this is the last message, in which case release any waiting producers. This is done - // after the transaction has been committed and any listeners notified. - if (trueCount == 1) - { - trafficLight.countDown(); - } - } - } - else - { - log.warn("Got unexpected message with correlationId: " + correlationID); - } - } - else - { - log.warn("Got redelivered message, ignoring."); - } - } - catch (JMSException e) - { - log.warn("There was a JMSException: " + e.getMessage(), e); - } - finally - { - // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo): ending"); - // NDC.clear(); - } - } - - /** - * Sends the specified number of ping message and then waits for all correlating replies. If the wait times out - * before a reply arrives, then a null reply is returned from this method. This method allows the caller to specify - * the correlation id. - * - * @param message The message to send. If this is null, one is generated. - * @param numPings The number of ping messages to send. - * @param timeout The timeout in milliseconds. - * @param messageCorrelationId The message correlation id. If this is null, one is generated. - * - * @return The number of replies received. This may be less than the number sent if the timeout terminated the wait - * for all prematurely. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - * @throws InterruptedException When interrupted by a timeout - */ - public int pingAndWaitForReply(Message message, int numPings, long timeout, String messageCorrelationId) - throws JMSException, InterruptedException - { - /*log.debug("public int pingAndWaitForReply(Message message, int numPings = " + numPings + ", long timeout = " - + timeout + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ - - // Generate a unique correlation id to put on the messages before sending them, if one was not specified. - if (messageCorrelationId == null) - { - messageCorrelationId = Long.toString(_correlationIdGenerator.incrementAndGet()); - } - - try - { - // NDC.push("prod"); - - // Create a count down latch to count the number of replies with. This is created before the messages are - // sent so that the replies cannot be received before the count down is created. - // One is added to this, so that the last reply becomes a special case. The special case is that the - // chained message listener must be called before this sender can be unblocked, but that decrementing the - // countdown needs to be done before the chained listener can be called. - PerCorrelationId perCorrelationId = new PerCorrelationId(); - - perCorrelationId.trafficLight = new CountDownLatch(getExpectedNumPings(numPings) + 1); - perCorrelationIds.put(messageCorrelationId, perCorrelationId); - - // Set up the current time as the start time for pinging on the correlation id. This is used to determine - // timeouts. - perCorrelationId.timeOutStart = System.nanoTime(); - - // Send the specifed number of messages. - pingNoWaitForReply(message, numPings, messageCorrelationId); - - boolean timedOut; - boolean allMessagesReceived; - int numReplies; - - do - { - // Block the current thread until replies to all the messages are received, or it times out. - perCorrelationId.trafficLight.await(timeout, TimeUnit.MILLISECONDS); - - // Work out how many replies were receieved. - numReplies = getExpectedNumPings(numPings) - (int) perCorrelationId.trafficLight.getCount(); - - allMessagesReceived = numReplies == getExpectedNumPings(numPings); - - // log.debug("numReplies = " + numReplies); - // log.debug("allMessagesReceived = " + allMessagesReceived); - - // Recheck the timeout condition. - long now = System.nanoTime(); - long lastMessageReceievedAt = perCorrelationId.timeOutStart; - timedOut = (now - lastMessageReceievedAt) > (timeout * 1000000); - - // log.debug("now = " + now); - // log.debug("lastMessageReceievedAt = " + lastMessageReceievedAt); - } - while (!timedOut && !allMessagesReceived); - - if ((numReplies < getExpectedNumPings(numPings)) && _verbose) - { - log.info("Timed out (" + timeout + " ms) before all replies received on id, " + messageCorrelationId); - } - else if (_verbose) - { - log.info("Got all replies on id, " + messageCorrelationId); - } - - // commitTx(_consumerSession); - - // log.debug("public int pingAndWaitForReply(Message message, int numPings, long timeout): ending"); - - return numReplies; - } - // Ensure that the message countdown latch is always removed from the reply map. The reply map is long lived, - // so will be a memory leak if this is not done. - finally - { - // NDC.pop(); - perCorrelationIds.remove(messageCorrelationId); - } - } - - /** - * Sends the specified number of ping messages and does not wait for correlating replies. - * - * @param message The message to send. - * @param numPings The number of pings to send. - * @param messageCorrelationId A correlation id to place on all messages sent. - * - * @throws JMSException All underlying JMSExceptions are allowed to fall through. - */ - public void pingNoWaitForReply(Message message, int numPings, String messageCorrelationId) throws JMSException - { - /*log.debug("public void pingNoWaitForReply(Message message, int numPings = " + numPings - + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ - - if (message == null) - { - message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); - } - - message.setJMSCorrelationID(messageCorrelationId); - - // Set up a committed flag to detect uncommitted messages at the end of the send loop. This may occurr if the - // transaction batch size is not a factor of the number of pings. In which case an extra commit at the end is - // needed. - boolean committed = false; - - // Send all of the ping messages. - for (int i = 0; i < numPings; i++) - { - // Re-timestamp the message. - // message.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); - - // Send the message, passing in the message count. - committed = sendMessage(i, message); - - // Spew out per message timings on every message sonly in verbose mode. - /*if (_verbose) - { - log.info(timestampFormatter.format(new Date()) + ": Pinged at with correlation id, " + messageCorrelationId); - }*/ - } - - // Call commit if the send loop finished before reaching a batch size boundary so there may still be uncommitted messages. - if (!committed) - { - commitTx(_producerSession); - } - } - - /** - * Sends the sepcified message, applies rate limiting and possibly commits the current transaction. The count of - * messages sent so far must be specified and is used to round robin the ping destinations (where there are more - * than one), and to determine if the transaction batch size has been reached and the sent messages should be - * committed. - * - * @param i The count of messages sent so far in a loop of multiple calls to this send method. - * @param message The message to send. - * - * @return true if the messages were committed, false otherwise. - * - * @throws JMSException All underlyiung JMSExceptions are allowed to fall through. - */ - protected boolean sendMessage(int i, Message message) throws JMSException - { - try - { - NDC.push("id" + instanceId + "/prod"); - - // log.debug("protected boolean sendMessage(int i = " + i + ", Message message): called"); - // log.debug("_txBatchSize = " + _txBatchSize); - - // Round robin the destinations as the messages are sent. - Destination destination = _pingDestinations.get(i % _pingDestinations.size()); - - // Prompt the user to kill the broker when doing failover testing. - _failBeforeSend = waitForUserToPromptOnFailure(_failBeforeSend); - - // Get the test setup for the correlation id. - String correlationID = message.getJMSCorrelationID(); - PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); - - // If necessary, wait until the max pending message size comes within its limit. - if (_maxPendingSize > 0) - { - synchronized (_sendPauseMonitor) - { - // Used to keep track of the number of times that send has to wait. - int numWaits = 0; - - // The maximum number of waits before the test gives up and fails. This has been chosen to correspond with - // the test timeout. - int waitLimit = (int) (TIMEOUT_DEFAULT / 10000); - - while (true) - { - // Get the size estimate of sent but not yet received messages. - int unreceived = _unreceived.get(); - int unreceivedSize = - (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) - / (_isPubSub ? getConsumersPerDestination() : 1); - - // log.debug("unreceived = " + unreceived); - // log.debug("unreceivedSize = " + unreceivedSize); - // log.debug("_maxPendingSize = " + _maxPendingSize); - - if (unreceivedSize > _maxPendingSize) - { - // log.debug("unreceived size estimate over limit = " + unreceivedSize); - - // Fail the test if the send has had to wait more than the maximum allowed number of times. - if (numWaits > waitLimit) - { - String errorMessage = - "Send has had to wait for the unreceivedSize (" + unreceivedSize - + ") to come below the maxPendingSize (" + _maxPendingSize + ") more that " + waitLimit - + " times."; - log.warn(errorMessage); - throw new RuntimeException(errorMessage); - } - - // Wait on the send pause barrier for the limit to be re-established. - try - { - long start = System.nanoTime(); - // _sendPauseMonitor.wait(10000); - _sendPauseMonitor.offer(new Object(), 10000, TimeUnit.MILLISECONDS); - long end = System.nanoTime(); - - // Count the wait only if it was for > 99% of the requested wait time. - if (((float) (end - start) / (float) (10000 * 1000000L)) > 0.99) - { - numWaits++; - } - } - catch (InterruptedException e) - { - // Restore the interrupted status - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - } - else - { - break; - } - } - } - } - - // Send the message either to its round robin destination, or its default destination. - // int num = numSent.incrementAndGet(); - // message.setIntProperty("MSG_NUM", num); - setTimestamp(message); - - if (destination == null) - { - _producer.send(message); - } - else - { - _producer.send(destination, message); - } - - // Increase the unreceived size, this may actually happen after the message is received. - // The unreceived size is incremented by the number of consumers that will get a copy of the message, - // in pub/sub mode. - if (_maxPendingSize > 0) - { - int newUnreceivedCount = _unreceived.addAndGet(_isPubSub ? getConsumersPerDestination() : 1); - // log.debug("newUnreceivedCount = " + newUnreceivedCount); - } - - // Apply message rate throttling if a rate limit has been set up. - if (_rateLimiter != null) - { - _rateLimiter.throttle(); - } - - // Call commit every time the commit batch size is reached. - boolean committed = false; - - // Commit on every transaction batch size boundary. Here i + 1 is the count of actual messages sent. - if (((i + 1) % _txBatchSize) == 0) - { - // log.debug("Trying commit on producer session."); - committed = commitTx(_producerSession); - } - - return committed; - } - finally - { - NDC.clear(); - } - } - - /** - * If the specified fail flag is set, this method waits for the user to cause a failure and then indicate to the - * test that the failure has occurred, before the method returns. - * - * @param failFlag The fail flag to test. - * - * @return The new value for the fail flag. If the {@link #_failOnce} flag is set, then each fail flag is only - * used once, then reset. - */ - private boolean waitForUserToPromptOnFailure(boolean failFlag) - { - if (failFlag) - { - if (_failOnce) - { - failFlag = false; - } - - // log.debug("Failing Before Send"); - waitForUser(KILL_BROKER_PROMPT); - } - - return failFlag; - } - - /** - * Implements a single iteration of the ping loop. This sends the number of pings specified by the transaction batch - * size property, and waits for replies to all of them. Any errors cause the publish flag to be cleared, which will - * terminate the pinger. - */ - public void pingLoop() - { - try - { - // Generate a sample message and time stamp it. - Message msg = getTestMessage(_replyDestination, _messageSize, _persistent); - // setTimestamp(msg); - - // Send the message and wait for a reply. - pingAndWaitForReply(msg, TX_BATCH_SIZE_DEFAULT, TIMEOUT_DEFAULT, null); - } - catch (JMSException e) - { - _publish = false; - // log.debug("There was a JMSException: " + e.getMessage(), e); - } - catch (InterruptedException e) - { - _publish = false; - // log.debug("There was an interruption: " + e.getMessage(), e); - } - } - - /** - * Sets a chained message listener. The message listener on this pinger, chains all its messages to the one set - * here. - * - * @param messageListener The chained message listener. - */ - public void setChainedMessageListener(ChainedMessageListener messageListener) - { - _chainedMessageListener = messageListener; - } - - /** Removes any chained message listeners from this pinger. */ - public void removeChainedMessageListener() - { - _chainedMessageListener = null; - } - - /** - * Generates a test message of the specified size, with the specified reply-to destination and persistence flag. - * - * @param replyQueue The reply-to destination for the message. - * @param messageSize The desired size of the message in bytes. - * @param persistent true if the message should use persistent delivery, false otherwise. - * - * @return A freshly generated test message. - * - * @throws javax.jms.JMSException All underlying JMSException are allowed to fall through. - */ - public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException - { - // return TestMessageFactory.newObjectMessage(_producerSession, replyQueue, messageSize, persistent); - return TestUtils.createTestMessageOfSize(_producerSession, messageSize); - } - - /** - * Sets the current time in nanoseconds as the timestamp on the message. - * - * @param msg The message to timestamp. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - protected void setTimestamp(Message msg) throws JMSException - { - /*if (((AMQSession)_producerSession).isStrictAMQP()) - { - ((AMQMessage)msg).setTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME), System.nanoTime()); - } - else - {*/ - msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); - // } - } - - /** - * Extracts the nanosecond timestamp from a message. - * - * @param msg The message to extract the time stamp from. - * - * @return The timestamp in nanos. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - protected long getTimestamp(Message msg) throws JMSException - { - /*if (((AMQSession)_producerSession).isStrictAMQP()) - { - Long value = ((AMQMessage)msg).getTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME)); - - return (value == null) ? 0L : value; - } - else - {*/ - return msg.getLongProperty(PingPongProducer.MESSAGE_TIMESTAMP_PROPNAME); - // } - } - - /** - * Stops the ping loop by clearing the publish flag. The current loop will complete when it notices that this flag - * has been cleared. - */ - public void stop() - { - _publish = false; - } - - /** - * Starts the producer and consumer connections. - * - * @throws JMSException Any JMSExceptions are allowed to fall through. - */ - public void start() throws JMSException - { - // log.debug("public void start(): called"); - - _connection.start(); - // log.debug("Producer started."); - - for (int i = 0; i < _noOfConsumers; i++) - { - _consumerConnection[i].start(); - // log.debug("Consumer " + i + " started."); - } - } - - /** Implements a ping loop that repeatedly pings until the publish flag becomes false. */ - public void run() - { - // Keep running until the publish flag is cleared. - while (_publish) - { - pingLoop(); - } - } - - /** - * Callback method, implementing ExceptionListener. This should be registered to listen for exceptions on the - * connection, this clears the publish flag which in turn will halt the ping loop. - * - * @param e The exception that triggered this callback method. - */ - public void onException(JMSException e) - { - // log.debug("public void onException(JMSException e = " + e + "): called", e); - _publish = false; - } - - /** - * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered - * with the runtime system as a shutdown hook. - * - * @return A shutdown hook for the ping loop. - */ - public Thread getShutdownHook() - { - return new Thread(new Runnable() - { - public void run() - { - stop(); - } - }); - } - - /** - * Closes all of the producer and consumer connections. - * - * @throws JMSException All JMSException are allowed to fall through. - */ - public void close() throws JMSException - { - // log.debug("public void close(): called"); - - try - { - if (_connection != null) - { - // log.debug("Before close producer connection."); - _connection.close(); - // log.debug("Closed producer connection."); - } - - for (int i = 0; i < _noOfConsumers; i++) - { - if (_consumerConnection[i] != null) - { - // log.debug("Before close consumer connection " + i + "."); - _consumerConnection[i].close(); - // log.debug("Closed consumer connection " + i + "."); - } - } - } - finally - { - _connection = null; - _producerSession = null; - _consumerSession = null; - _consumerConnection = null; - _producer = null; - _consumer = null; - _pingDestinations = null; - _replyDestination = null; - } - } - - /** - * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not a - * transactional controlSession, this method does nothing (unless the failover after send flag is set). - * - *

          If the {@link #_failAfterSend} flag is set, this will prompt the user to kill the broker before the commit is - * applied. This flag applies whether the pinger is transactional or not. - * - *

          If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the commit - * is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker after the - * commit is applied. These flags will only apply if using a transactional pinger. - * - * @param session The controlSession to commit - * - * @return true if the controlSession was committed, false if it was not. - * - * @throws javax.jms.JMSException If the commit fails and then the rollback fails. - * - * @todo Consider moving the fail after send logic into the send method. It is confusing to have it in this commit - * method, because commits only apply to transactional pingers, but fail after send applied to transactional and - * non-transactional alike. - */ - protected boolean commitTx(Session session) throws JMSException - { - // log.debug("protected void commitTx(Session session): called"); - - boolean committed = false; - - _failAfterSend = waitForUserToPromptOnFailure(_failAfterSend); - - if (session.getTransacted()) - { - // log.debug("Session is transacted."); - - try - { - _failBeforeCommit = waitForUserToPromptOnFailure(_failBeforeCommit); - - long start = System.nanoTime(); - session.commit(); - committed = true; - // log.debug("Time taken to commit :" + ((System.nanoTime() - start) / 1000000f) + " ms"); - - _failAfterCommit = waitForUserToPromptOnFailure(_failAfterCommit); - - // log.debug("Session Commited."); - } - catch (JMSException e) - { - // log.debug("JMSException on commit:" + e.getMessage(), e); - - try - { - session.rollback(); - // log.debug("Message rolled back."); - } - catch (JMSException jmse) - { - // log.debug("JMSE on rollback:" + jmse.getMessage(), jmse); - - // Both commit and rollback failed. Throw the rollback exception. - throw jmse; - } - } - } - - return committed; - } - - /** - * Outputs a prompt to the console and waits for the user to press return. - * - * @param prompt The prompt to display on the console. - */ - public void waitForUser(String prompt) - { - System.out.println(prompt); - - try - { - System.in.read(); - } - catch (IOException e) - { - // Ignored. - } - - System.out.println("Continuing."); - } - - /** - * Gets the number of consumers that are listening to each destination in the test. - * - * @return int The number of consumers subscribing to each topic. - */ - public int getConsumersPerDestination() - { - return _noOfConsumers; - } - - /** - * Calculates how many pings are expected to be received for the given number sent. - * - * @param numpings The number of pings that will be sent. - * - * @return The number that should be received, for the test to pass. - */ - public int getExpectedNumPings(int numpings) - { - // log.debug("public int getExpectedNumPings(int numpings = " + numpings + "): called"); - - // log.debug("Each ping will be received by " + (_isPubSub ? getConsumersPerDestination() : 1) + " consumers."); - - return numpings * (_isPubSub ? getConsumersPerDestination() : 1); - } - - /** - * Defines a chained message listener interface that can be attached to this pinger. Whenever this pinger's {@link - * PingPongProducer#onMessageWithConsumerNo} method is called, the chained listener set through the {@link - * PingPongProducer#setChainedMessageListener} method is passed the message, and the remaining expected count of - * messages with that correlation id. - * - *

          Provided only one pinger is producing messages with that correlation id, the chained listener will always be - * given unique message counts. It will always be called while the producer waiting for all messages to arrive is - * still blocked. - */ - public static interface ChainedMessageListener - { - /** - * Notifies interested listeners about message arrival and important test stats, the number of messages - * remaining in the test, and the messages send timestamp. - * - * @param message The newly arrived message. - * @param remainingCount The number of messages left to complete the test. - * @param latency The nanosecond latency of the message. - * - * @throws JMSException Any JMS exceptions is allowed to fall through. - */ - public void onMessage(Message message, int remainingCount, long latency) throws JMSException; - } - - /** - * Holds information on each correlation id. The countdown latch, the current timeout timer... More stuff to be - * added to this: read/write lock to make onMessage more concurrent as described in class header comment. - */ - protected static class PerCorrelationId - { - /** Holds a countdown on number of expected messages. */ - CountDownLatch trafficLight; - - /** Holds the last timestamp that the timeout was reset to. */ - Long timeOutStart; - } -} +/* + * + * 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.requestreply; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.test.framework.TestUtils; + +import org.apache.qpid.junit.extensions.BatchedThrottle; +import org.apache.qpid.junit.extensions.Throttle; +import org.apache.qpid.junit.extensions.util.CommandLineParser; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import java.io.*; +import java.net.InetAddress; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * PingPongProducer is a client that sends test messages, and waits for replies to these messages. The replies may + * either be generated by another client (see {@link PingPongBouncer}, or an extension of it may be used that listens + * to its own messages and does not send replies (see {@link org.apache.qpid.ping.PingClient}). The intention of ping + * pong producer is that it is a swiss-army knife test client that makes almost every aspect of its behaviour + * configurable. + * + *

          The pings are sent with a reply-to field set to a single temporary queue, which is the same for all pings. This + * means that this class has to do some work to correlate pings with pongs; it expectes the original message correlation + * id in the ping to be bounced back in the reply correlation id. + * + *

          This ping tool accepts a vast number of configuration options, all of which are passed in to the constructor. It + * can ping topics or queues; ping multiple destinations; do persistent pings; send messages of any size; do pings within + * transactions; control the number of pings to send in each transaction; limit its sending rate; and perform failover + * testing. A complete list of accepted parameters, default values and comments on their usage is provided here: + * + *

          + *
          Parameters
          Parameter Default Comments + *
          messageSize 0 Message size in bytes. Not including any headers. + *
          destinationName ping The root name to use to generate destination names to ping. + *
          persistent false Determines whether peristent delivery is used. + *
          transacted false Determines whether messages are sent/received in transactions. + *
          broker tcp://localhost:5672 Determines the broker to connect to. + *
          virtualHost test Determines the virtual host to send all ping over. + *
          rate 0 The maximum rate (in hertz) to send messages at. 0 means no limit. + *
          verbose false The verbose flag for debugging. Prints to console on every message. + *
          pubsub false Whether to ping topics or queues. Uses p2p by default. + *
          failAfterCommit false Whether to prompt user to kill broker after a commit batch. + *
          failBeforeCommit false Whether to prompt user to kill broker before a commit batch. + *
          failAfterSend false Whether to prompt user to kill broker after a send. + *
          failBeforeSend false Whether to prompt user to kill broker before a send. + *
          failOnce true Whether to prompt for failover only once. + *
          username guest The username to access the broker with. + *
          password guest The password to access the broker with. + *
          selector null Not used. Defines a message selector to filter pings with. + *
          destinationCount 1 The number of destinations to send pings to. + *
          numConsumers 1 The number of consumers on each destination. + *
          timeout 30000 In milliseconds. The timeout to stop waiting for replies. + *
          commitBatchSize 1 The number of messages per transaction in transactional mode. + *
          uniqueDests true Whether each receivers only listens to one ping destination or all. + *
          durableDests false Whether or not durable destinations are used. + *
          ackMode AUTO_ACK The message acknowledgement mode. Possible values are: + * 0 - SESSION_TRANSACTED + * 1 - AUTO_ACKNOWLEDGE + * 2 - CLIENT_ACKNOWLEDGE + * 3 - DUPS_OK_ACKNOWLEDGE + * 257 - NO_ACKNOWLEDGE + * 258 - PRE_ACKNOWLEDGE + *
          consTransacted false Whether or not consumers use transactions. Defaults to the same value + * as the 'transacted' option if not seperately defined. + *
          consAckMode AUTO_ACK The message acknowledgement mode for consumers. Defaults to the same + * value as 'ackMode' if not seperately defined. + *
          maxPending 0 The maximum size in bytes, of messages sent but not yet received. + * Limits the volume of messages currently buffered on the client + * or broker. Can help scale test clients by limiting amount of buffered + * data to avoid out of memory errors. + *
          + * + *

          This implements the Runnable interface with a run method that implements an infinite ping loop. The ping loop + * does all its work through helper methods, so that code wishing to run a ping-pong cycle is not forced to do so by + * starting a new thread. The command line invocation does take advantage of this ping loop. A shutdown hook is also + * registered to terminate the ping-pong loop cleanly. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Provide a ping and wait for all responses cycle. + *
          Provide command line invocation to loop the ping cycle on a configurable broker url. + *
          + * + * @todo Use read/write lock in the onmessage, not for reading writing but to make use of a shared and exlcusive lock pair. + * Obtain read lock on all messages, before decrementing the message count. At the end of the on message method add a + * block that obtains the write lock for the very last message, releases any waiting producer. Means that the last + * message waits until all other messages have been handled before releasing producers but allows messages to be + * processed concurrently, unlike the current synchronized block. + */ +public class PingPongProducer implements Runnable, ExceptionListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(PingPongProducer.class); + + /** Holds the name of the property to determine whether of not client id is overridden at connection time. */ + public static final String OVERRIDE_CLIENT_ID_PROPNAME = "overrideClientId"; + + /** Holds the default value of the override client id flag. */ + public static final String OVERRIDE_CLIENT_ID_DEAFULT = "false"; + + /** Holds the name of the property to define the JNDI factory name with. */ + public static final String FACTORY_NAME_PROPNAME = "factoryName"; + + /** Holds the default JNDI name of the connection factory. */ + public static final String FACTORY_NAME_DEAFULT = "local"; + + /** Holds the name of the property to set the JNDI initial context properties with. */ + public static final String FILE_PROPERTIES_PROPNAME = "properties"; + + /** Holds the default file name of the JNDI initial context properties. */ + public static final String FILE_PROPERTIES_DEAFULT = "perftests.properties"; + + /** Holds the name of the property to get the test message size from. */ + public static final String MESSAGE_SIZE_PROPNAME = "messageSize"; + + /** Used to set up a default message size. */ + public static final int MESSAGE_SIZE_DEAFULT = 0; + + /** Holds the name of the property to get the ping queue name from. */ + public static final String PING_QUEUE_NAME_PROPNAME = "destinationName"; + + /** Holds the name of the default destination to send pings on. */ + public static final String PING_QUEUE_NAME_DEFAULT = "ping"; + + /** Holds the name of the property to get the queue name postfix from. */ + public static final String QUEUE_NAME_POSTFIX_PROPNAME = "queueNamePostfix"; + + /** Holds the default queue name postfix value. */ + public static final String QUEUE_NAME_POSTFIX_DEFAULT = ""; + + /** Holds the name of the property to get the test delivery mode from. */ + public static final String PERSISTENT_MODE_PROPNAME = "persistent"; + + /** Holds the message delivery mode to use for the test. */ + public static final boolean PERSISTENT_MODE_DEFAULT = false; + + /** Holds the name of the property to get the test transactional mode from. */ + public static final String TRANSACTED_PROPNAME = "transacted"; + + /** Holds the transactional mode to use for the test. */ + public static final boolean TRANSACTED_DEFAULT = false; + + /** Holds the name of the property to get the test consumer transacted mode from. */ + public static final String CONSUMER_TRANSACTED_PROPNAME = "consTransacted"; + + /** Holds the consumer transactional mode default setting. */ + public static final boolean CONSUMER_TRANSACTED_DEFAULT = false; + + /** Holds the name of the property to get the test broker url from. */ + public static final String BROKER_PROPNAME = "broker"; + + /** Holds the default broker url for the test. */ + public static final String BROKER_DEFAULT = "tcp://localhost:5672"; + + /** Holds the name of the property to get the test broker virtual path. */ + public static final String VIRTUAL_HOST_PROPNAME = "virtualHost"; + + /** Holds the default virtual path for the test. */ + public static final String VIRTUAL_HOST_DEFAULT = ""; + + /** Holds the name of the property to get the message rate from. */ + public static final String RATE_PROPNAME = "rate"; + + /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */ + public static final int RATE_DEFAULT = 0; + + /** Holds the name of the property to get the verbose mode proeprty from. */ + public static final String VERBOSE_PROPNAME = "verbose"; + + /** Holds the default verbose mode. */ + public static final boolean VERBOSE_DEFAULT = false; + + /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */ + public static final String PUBSUB_PROPNAME = "pubsub"; + + /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */ + public static final boolean PUBSUB_DEFAULT = false; + + /** Holds the name of the property to get the fail after commit flag from. */ + public static final String FAIL_AFTER_COMMIT_PROPNAME = "failAfterCommit"; + + /** Holds the default failover after commit test flag. */ + public static final boolean FAIL_AFTER_COMMIT_DEFAULT = false; + + /** Holds the name of the proeprty to get the fail before commit flag from. */ + public static final String FAIL_BEFORE_COMMIT_PROPNAME = "failBeforeCommit"; + + /** Holds the default failover before commit test flag. */ + public static final boolean FAIL_BEFORE_COMMIT_DEFAULT = false; + + /** Holds the name of the proeprty to get the fail after send flag from. */ + public static final String FAIL_AFTER_SEND_PROPNAME = "failAfterSend"; + + /** Holds the default failover after send test flag. */ + public static final boolean FAIL_AFTER_SEND_DEFAULT = false; + + /** Holds the name of the property to get the fail before send flag from. */ + public static final String FAIL_BEFORE_SEND_PROPNAME = "failBeforeSend"; + + /** Holds the default failover before send test flag. */ + public static final boolean FAIL_BEFORE_SEND_DEFAULT = false; + + /** Holds the name of the property to get the fail once flag from. */ + public static final String FAIL_ONCE_PROPNAME = "failOnce"; + + /** The default failover once flag, true means only do one failover, false means failover on every commit cycle. */ + public static final boolean FAIL_ONCE_DEFAULT = true; + + /** Holds the name of the property to get the broker access username from. */ + public static final String USERNAME_PROPNAME = "username"; + + /** Holds the default broker log on username. */ + public static final String USERNAME_DEFAULT = "guest"; + + /** Holds the name of the property to get the broker access password from. */ + public static final String PASSWORD_PROPNAME = "password"; + + /** Holds the default broker log on password. */ + public static final String PASSWORD_DEFAULT = "guest"; + + /** Holds the name of the proeprty to get the. */ + public static final String SELECTOR_PROPNAME = "selector"; + + /** Holds the default message selector. */ + public static final String SELECTOR_DEFAULT = ""; + + /** Holds the name of the property to get the destination count from. */ + public static final String DESTINATION_COUNT_PROPNAME = "destinationCount"; + + /** Defines the default number of destinations to ping. */ + public static final int DESTINATION_COUNT_DEFAULT = 1; + + /** Holds the name of the property to get the number of consumers per destination from. */ + public static final String NUM_CONSUMERS_PROPNAME = "numConsumers"; + + /** Defines the default number consumers per destination. */ + public static final int NUM_CONSUMERS_DEFAULT = 1; + + /** Holds the name of the property to get the waiting timeout for response messages. */ + public static final String TIMEOUT_PROPNAME = "timeout"; + + /** Default time to wait before assuming that a ping has timed out. */ + public static final long TIMEOUT_DEFAULT = 30000; + + /** Holds the name of the property to get the commit batch size from. */ + public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize"; + + /** Defines the default number of pings to send in each transaction when running transactionally. */ + public static final int TX_BATCH_SIZE_DEFAULT = 1; + + /** Holds the name of the property to get the unique destinations flag from. */ + public static final String UNIQUE_DESTS_PROPNAME = "uniqueDests"; + + /** Defines the default value for the unique destinations property. */ + public static final boolean UNIQUE_DESTS_DEFAULT = true; + + /** Holds the name of the property to get the durable destinations flag from. */ + public static final String DURABLE_DESTS_PROPNAME = "durableDests"; + + /** Defines the default value of the durable destinations flag. */ + public static final boolean DURABLE_DESTS_DEFAULT = false; + + /** Holds the name of the proeprty to get the message acknowledgement mode from. */ + public static final String ACK_MODE_PROPNAME = "ackMode"; + + /** Defines the default message acknowledgement mode. */ + public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; + + /** Holds the name of the property to get the consumers message acknowledgement mode from. */ + public static final String CONSUMER_ACK_MODE_PROPNAME = "consAckMode"; + + /** Defines the default consumers message acknowledgement mode. */ + public static final int CONSUMER_ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; + + /** Holds the name of the property to get the maximum pending message size setting from. */ + public static final String MAX_PENDING_PROPNAME = "maxPending"; + + /** Defines the default value for the maximum pending message size setting. 0 means no limit. */ + public static final int MAX_PENDING_DEFAULT = 0; + + /** Defines the default prefetch size to use when consuming messages. */ + public static final int PREFETCH_DEFAULT = 100; + + /** Defines the default value of the no local flag to use when consuming messages. */ + public static final boolean NO_LOCAL_DEFAULT = false; + + /** Defines the default value of the exclusive flag to use when consuming messages. */ + public static final boolean EXCLUSIVE_DEFAULT = false; + + /** Holds the name of the property to store nanosecond timestamps in ping messages with. */ + public static final String MESSAGE_TIMESTAMP_PROPNAME = "timestamp"; + + /** Holds the default configuration properties. */ + public static ParsedProperties defaults = new ParsedProperties(); + + static + { + defaults.setPropertyIfNull(OVERRIDE_CLIENT_ID_PROPNAME, OVERRIDE_CLIENT_ID_DEAFULT); + defaults.setPropertyIfNull(FILE_PROPERTIES_PROPNAME, FILE_PROPERTIES_DEAFULT); + defaults.setPropertyIfNull(FACTORY_NAME_PROPNAME, FACTORY_NAME_DEAFULT); + defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); + defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); + defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); + defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); + defaults.setPropertyIfNull(PING_QUEUE_NAME_PROPNAME, PING_QUEUE_NAME_DEFAULT); + defaults.setPropertyIfNull(QUEUE_NAME_POSTFIX_PROPNAME, QUEUE_NAME_POSTFIX_DEFAULT); + defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); + defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); + defaults.setPropertyIfNull(CONSUMER_TRANSACTED_PROPNAME, CONSUMER_TRANSACTED_DEFAULT); + defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); + defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); + defaults.setPropertyIfNull(CONSUMER_ACK_MODE_PROPNAME, CONSUMER_ACK_MODE_DEFAULT); + defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); + defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); + defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); + defaults.setPropertyIfNull(UNIQUE_DESTS_PROPNAME, UNIQUE_DESTS_DEFAULT); + defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); + defaults.setPropertyIfNull(FAIL_BEFORE_COMMIT_PROPNAME, FAIL_BEFORE_COMMIT_DEFAULT); + defaults.setPropertyIfNull(FAIL_AFTER_COMMIT_PROPNAME, FAIL_AFTER_COMMIT_DEFAULT); + defaults.setPropertyIfNull(FAIL_BEFORE_SEND_PROPNAME, FAIL_BEFORE_SEND_DEFAULT); + defaults.setPropertyIfNull(FAIL_AFTER_SEND_PROPNAME, FAIL_AFTER_SEND_DEFAULT); + defaults.setPropertyIfNull(FAIL_ONCE_PROPNAME, FAIL_ONCE_DEFAULT); + defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); + defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); + defaults.setPropertyIfNull(NUM_CONSUMERS_PROPNAME, NUM_CONSUMERS_DEFAULT); + defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); + defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); + defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); + } + + /** Allows setting of client ID on the connection, rather than through the connection URL. */ + protected boolean _overrideClientId; + + /** Holds the JNDI name of the JMS connection factory. */ + protected String _factoryName; + + /** Holds the name of the properties file to configure JNDI with. */ + protected String _fileProperties; + + /** Holds the broker url. */ + protected String _brokerDetails; + + /** Holds the username to access the broker with. */ + protected String _username; + + /** Holds the password to access the broker with. */ + protected String _password; + + /** Holds the virtual host on the broker to run the tests through. */ + protected String _virtualpath; + + /** Holds the root name from which to generate test destination names. */ + protected String _destinationName; + + /** Holds the default queue name postfix value. */ + protected String _queueNamePostfix; + + /** Holds the message selector to filter the pings with. */ + protected String _selector; + + /** Holds the producers transactional mode flag. */ + protected boolean _transacted; + + /** Holds the consumers transactional mode flag. */ + protected boolean _consTransacted; + + /** Determines whether this producer sends persistent messages. */ + protected boolean _persistent; + + /** Holds the acknowledgement mode used for the producers. */ + protected int _ackMode; + + /** Holds the acknowledgement mode setting for the consumers. */ + protected int _consAckMode; + + /** Determines what size of messages this producer sends. */ + protected int _messageSize; + + /** Used to indicate that the ping loop should print out whenever it pings. */ + protected boolean _verbose; + + /** Flag used to indicate if this is a point to point or pub/sub ping client. */ + protected boolean _isPubSub; + + /** Flag used to indicate if the destinations should be unique client. */ + protected boolean _isUnique; + + /** Flag used to indicate that durable destination should be used. */ + protected boolean _isDurable; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a commit. */ + protected boolean _failBeforeCommit; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a commit. */ + protected boolean _failAfterCommit; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a send. */ + protected boolean _failBeforeSend; + + /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a send. */ + protected boolean _failAfterSend; + + /** Flag used to indicate that failover prompting should only be done on the first commit, not on every commit. */ + protected boolean _failOnce; + + /** Holds the number of sends that should be performed in every transaction when using transactions. */ + protected int _txBatchSize; + + /** Holds the number of destinations to ping. */ + protected int _noOfDestinations; + + /** Holds the number of consumers per destination. */ + protected int _noOfConsumers; + + /** Holds the maximum send rate in herz. */ + protected int _rate; + + /** + * Holds the size of the maximum amount of pending data that the client should buffer, sending is suspended + * if this limit is breached. + */ + protected int _maxPendingSize; + + /** A source for providing sequential unique correlation ids. These will be unique within the same JVM. */ + private static AtomicLong _correlationIdGenerator = new AtomicLong(0L); + + /** A source for providing sequential unqiue ids for instances of this class to be identifed with. */ + private static AtomicInteger _instanceIdGenerator = new AtomicInteger(0); + + /** Holds this instances unique id. */ + private int instanceId; + + /** + * Holds a map from message ids to latches on which threads wait for replies. This map is shared accross multiple + * ping producers on the same JVM. + */ + private static Map perCorrelationIds = + Collections.synchronizedMap(new HashMap()); + + /** A convenient formatter to use when time stamping output. */ + protected static final DateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS"); + + /** Holds the connection for the message producer. */ + protected Connection _connection; + + /** Holds the consumer connections. */ + protected Connection[] _consumerConnection; + + /** Holds the controlSession on which ping replies are received. */ + protected Session[] _consumerSession; + + /** Holds the producer controlSession, needed to create ping messages. */ + protected Session _producerSession; + + /** Holds the destination where the response messages will arrive. */ + protected Destination _replyDestination; + + /** Holds the set of destinations that this ping producer pings. */ + protected List _pingDestinations; + + /** Used to restrict the sending rate to a specified limit. */ + protected Throttle _rateLimiter; + + /** Holds a message listener that this message listener chains all its messages to. */ + protected ChainedMessageListener _chainedMessageListener = null; + + /** + * This id generator is used to generate ids to append to the queue name to ensure that queues can be unique when + * creating multiple ping producers in the same JVM. + */ + protected static AtomicInteger _queueJVMSequenceID = new AtomicInteger(); + + /** + * This id generator is used to generates ids that are only unique within this pinger. Creating multiple pingers + * on the same JVM using this id generator will allow them to ping on the same queues. + */ + protected AtomicInteger _queueSharedID = new AtomicInteger(); + + /** Used to tell the ping loop when to terminate, it only runs while this is true. */ + protected boolean _publish = true; + + /** Holds the message producer to send the pings through. */ + protected MessageProducer _producer; + + /** Holds the message consumer to receive the ping replies through. */ + protected MessageConsumer[] _consumer; + + /** The prompt to display when asking the user to kill the broker for failover testing. */ + private static final String KILL_BROKER_PROMPT = "Kill broker now, then press Return."; + + /** Holds the name for this test client to be identified to the broker with. */ + private String _clientID; + + /** Keeps count of the total messages sent purely for debugging purposes. */ + private static AtomicInteger numSent = new AtomicInteger(); + + /** + * Holds a monitor which is used to synchronize sender and receivers threads, where the sender has elected + * to wait until the number of unreceived message is reduced before continuing to send. This monitor is a + * fair SynchronousQueue becuase that provides fair scheduling, to ensure that all producer threads get an + * equal chance to produce messages. + */ + static final SynchronousQueue _sendPauseMonitor = new SynchronousQueue(true); + + /** Keeps a count of the number of message currently sent but not received. */ + static AtomicInteger _unreceived = new AtomicInteger(0); + + /** + * Creates a ping producer with the specified parameters, of which there are many. See the class level comments + * for details. This constructor creates a connection to the broker and creates producer and consumer sessions on + * it, to send and recieve its pings and replies on. + * + * @param overrides Properties containing any desired overrides to the defaults. + * + * @throws Exception Any exceptions are allowed to fall through. + */ + public PingPongProducer(Properties overrides) throws Exception + { + // log.debug("public PingPongProducer(Properties overrides = " + overrides + "): called"); + instanceId = _instanceIdGenerator.getAndIncrement(); + + // Create a set of parsed properties from the defaults overriden by the passed in values. + ParsedProperties properties = new ParsedProperties(defaults); + properties.putAll(overrides); + + // Extract the configuration properties to set the pinger up with. + _overrideClientId = properties.getPropertyAsBoolean(OVERRIDE_CLIENT_ID_PROPNAME); + _factoryName = properties.getProperty(FACTORY_NAME_PROPNAME); + _fileProperties = properties.getProperty(FILE_PROPERTIES_PROPNAME); + _brokerDetails = properties.getProperty(BROKER_PROPNAME); + _username = properties.getProperty(USERNAME_PROPNAME); + _password = properties.getProperty(PASSWORD_PROPNAME); + _virtualpath = properties.getProperty(VIRTUAL_HOST_PROPNAME); + _destinationName = properties.getProperty(PING_QUEUE_NAME_PROPNAME); + _queueNamePostfix = properties.getProperty(QUEUE_NAME_POSTFIX_PROPNAME); + _selector = properties.getProperty(SELECTOR_PROPNAME); + _transacted = properties.getPropertyAsBoolean(TRANSACTED_PROPNAME); + _consTransacted = properties.getPropertyAsBoolean(CONSUMER_TRANSACTED_PROPNAME); + _persistent = properties.getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME); + _messageSize = properties.getPropertyAsInteger(MESSAGE_SIZE_PROPNAME); + _verbose = properties.getPropertyAsBoolean(VERBOSE_PROPNAME); + _failAfterCommit = properties.getPropertyAsBoolean(FAIL_AFTER_COMMIT_PROPNAME); + _failBeforeCommit = properties.getPropertyAsBoolean(FAIL_BEFORE_COMMIT_PROPNAME); + _failAfterSend = properties.getPropertyAsBoolean(FAIL_AFTER_SEND_PROPNAME); + _failBeforeSend = properties.getPropertyAsBoolean(FAIL_BEFORE_SEND_PROPNAME); + _failOnce = properties.getPropertyAsBoolean(FAIL_ONCE_PROPNAME); + _txBatchSize = properties.getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME); + _noOfDestinations = properties.getPropertyAsInteger(DESTINATION_COUNT_PROPNAME); + _noOfConsumers = properties.getPropertyAsInteger(NUM_CONSUMERS_PROPNAME); + _rate = properties.getPropertyAsInteger(RATE_PROPNAME); + _isPubSub = properties.getPropertyAsBoolean(PUBSUB_PROPNAME); + _isUnique = properties.getPropertyAsBoolean(UNIQUE_DESTS_PROPNAME); + _isDurable = properties.getPropertyAsBoolean(DURABLE_DESTS_PROPNAME); + _ackMode = _transacted ? 0 : properties.getPropertyAsInteger(ACK_MODE_PROPNAME); + _consAckMode = _consTransacted ? 0 : properties.getPropertyAsInteger(CONSUMER_ACK_MODE_PROPNAME); + _maxPendingSize = properties.getPropertyAsInteger(MAX_PENDING_PROPNAME); + + // Check that one or more destinations were specified. + if (_noOfDestinations < 1) + { + throw new IllegalArgumentException("There must be at least one destination."); + } + + // Set up a throttle to control the send rate, if a rate > 0 is specified. + if (_rate > 0) + { + _rateLimiter = new BatchedThrottle(); + _rateLimiter.setRate(_rate); + } + + // Create the connection and message producers/consumers. + // establishConnection(true, true); + } + + /** + * Establishes a connection to the broker and creates message consumers and producers based on the parameters + * that this ping client was created with. + * + * @param producer Flag to indicate whether or not the producer should be set up. + * @param consumer Flag to indicate whether or not the consumers should be set up. + * + * @throws Exception Any exceptions are allowed to fall through. + */ + public void establishConnection(boolean producer, boolean consumer) throws Exception + { + // log.debug("public void establishConnection(): called"); + + // Generate a unique identifying name for this client, based on it ip address and the current time. + InetAddress address = InetAddress.getLocalHost(); + // _clientID = address.getHostName() + System.currentTimeMillis(); + _clientID = "perftest_" + instanceId; + + // Create a connection to the broker. + createConnection(_clientID); + + // Create transactional or non-transactional sessions, based on the command line arguments. + _producerSession = _connection.createSession(_transacted, _ackMode); + + _consumerSession = new Session[_noOfConsumers]; + + for (int i = 0; i < _noOfConsumers; i++) + { + _consumerSession[i] = _consumerConnection[i].createSession(_consTransacted, _consAckMode); + } + + // Create the destinations to send pings to and receive replies from. + _replyDestination = _consumerSession[0].createTemporaryQueue(); + createPingDestinations(_noOfDestinations, _selector, _destinationName, _isUnique, _isDurable); + + // Create the message producer only if instructed to. + if (producer) + { + createProducer(); + } + + // Create the message consumer only if instructed to. + if (consumer) + { + createReplyConsumers(getReplyDestinations(), _selector); + } + } + + /** + * Establishes a connection to the broker, based on the configuration parameters that this ping client was + * created with. + * + * @param clientID The clients identifier. + * + * @throws JMSException Underlying exceptions allowed to fall through. + * @throws NamingException Underlying exceptions allowed to fall through. + * @throws IOException Underlying exceptions allowed to fall through. + */ + protected void createConnection(String clientID) throws JMSException, NamingException, IOException + { + // _log.debug("protected void createConnection(String clientID = " + clientID + "): called"); + + // _log.debug("Creating a connection for the message producer."); + File propsFile = new File(_fileProperties); + InputStream is = new FileInputStream(propsFile); + Properties properties = new Properties(); + properties.load(is); + + Context context = new InitialContext(properties); + ConnectionFactory factory = (ConnectionFactory) context.lookup(_factoryName); + _connection = factory.createConnection(_username, _password); + + if (_overrideClientId) + { + _connection.setClientID(clientID); + } + + // _log.debug("Creating " + _noOfConsumers + " connections for the consumers."); + + _consumerConnection = new Connection[_noOfConsumers]; + + for (int i = 0; i < _noOfConsumers; i++) + { + _consumerConnection[i] = factory.createConnection(_username, _password); + // _consumerConnection[i].setClientID(clientID); + } + } + + /** + * Starts a ping-pong loop running from the command line. The bounce back client {@link PingPongBouncer} also needs + * to be started to bounce the pings back again. + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + try + { + Properties options = + CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties()); + + // Create a ping producer overriding its defaults with all options passed on the command line. + PingPongProducer pingProducer = new PingPongProducer(options); + pingProducer.establishConnection(true, true); + + // Start the ping producers dispatch thread running. + pingProducer._connection.start(); + + // Create a shutdown hook to terminate the ping-pong producer. + Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook()); + + // Ensure that the ping pong producer is registered to listen for exceptions on the connection too. + pingProducer._connection.setExceptionListener(pingProducer); + + // Create the ping loop thread and run it until it is terminated by the shutdown hook or exception. + Thread pingThread = new Thread(pingProducer); + pingThread.run(); + pingThread.join(); + } + catch (Exception e) + { + System.err.println(e.getMessage()); + log.error("Top level handler caught execption.", e); + System.exit(1); + } + } + + /** + * Convenience method for a short pause. + * + * @param sleepTime The time in milliseconds to pause for. + */ + public static void pause(long sleepTime) + { + if (sleepTime > 0) + { + try + { + Thread.sleep(sleepTime); + } + catch (InterruptedException ie) + { } + } + } + + /** + * Gets all the reply destinations (to listen for replies on). In this case this will just be the single reply to + * destination of this pinger. + * + * @return The single reply to destination of this pinger, wrapped in a list. + */ + public List getReplyDestinations() + { + // log.debug("public List getReplyDestinations(): called"); + + List replyDestinations = new ArrayList(); + replyDestinations.add(_replyDestination); + + // log.debug("replyDestinations = " + replyDestinations); + + return replyDestinations; + } + + /** + * Creates the producer to send the pings on. This is created without a default destination. Its persistent delivery + * flag is set accoring the ping producer creation options. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + public void createProducer() throws JMSException + { + // log.debug("public void createProducer(): called"); + + _producer = (MessageProducer) _producerSession.createProducer(null); + _producer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); + + // log.debug("Created producer for " + (_persistent ? "persistent" : "non-persistent") + " messages."); + } + + /** + * Creates consumers for the specified number of destinations. The destinations themselves are also created by this + * method. + * + * @param noOfDestinations The number of destinations to create consumers for. + * @param selector The message selector to filter the consumers with. + * @param rootName The root of the name, or actual name if only one is being created. + * @param unique true to make the destinations unique to this pinger, false to share the + * numbering with all pingers on the same JVM. + * @param durable If the destinations are durable topics. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + public void createPingDestinations(int noOfDestinations, String selector, String rootName, boolean unique, + boolean durable) throws JMSException + { + /*log.debug("public void createPingDestinations(int noOfDestinations = " + noOfDestinations + ", String selector = " + + selector + ", String rootName = " + rootName + ", boolean unique = " + unique + ", boolean durable = " + + durable + "): called");*/ + + _pingDestinations = new ArrayList(); + + // Create the desired number of ping destinations and consumers for them. + // log.debug("Creating " + noOfDestinations + " destinations to ping."); + + for (int i = 0; i < noOfDestinations; i++) + { + Destination destination; + String id; + + // Generate an id, unique within this pinger or to the whole JVM depending on the unique flag. + if (unique) + { + // log.debug("Creating unique destinations."); + id = "_" + _queueJVMSequenceID.incrementAndGet() + "_" + _connection.getClientID(); + } + else + { + // log.debug("Creating shared destinations."); + id = "_" + _queueSharedID.incrementAndGet(); + } + + // Check if this is a pub/sub pinger, in which case create topics. + if (_isPubSub) + { + destination = _producerSession.createTopic(rootName + id); + // log.debug("Created non-durable topic " + destination); + + if (durable) + { + _producerSession.createDurableSubscriber((Topic) destination, _connection.getClientID()); + } + } + // Otherwise this is a p2p pinger, in which case create queues. + else + { + destination = _producerSession.createQueue(rootName + id + _queueNamePostfix); + // log.debug("Created queue " + destination); + } + + // Keep the destination. + _pingDestinations.add(destination); + } + } + + /** + * Creates consumers for the specified destinations and registers this pinger to listen to their messages. + * + * @param destinations The destinations to listen to. + * @param selector A selector to filter the messages with. + * + * @throws javax.jms.JMSException Any JMSExceptions are allowed to fall through. + */ + public void createReplyConsumers(Collection destinations, String selector) throws JMSException + { + /*log.debug("public void createReplyConsumers(Collection destinations = " + destinations + + ", String selector = " + selector + "): called");*/ + + log.debug("There are " + destinations.size() + " destinations."); + log.debug("Creating " + _noOfConsumers + " consumers on each destination."); + log.debug("Total number of consumers is: " + (destinations.size() * _noOfConsumers)); + + for (Destination destination : destinations) + { + _consumer = new MessageConsumer[_noOfConsumers]; + + for (int i = 0; i < _noOfConsumers; i++) + { + // Create a consumer for the destination and set this pinger to listen to its messages. + _consumer[i] = _consumerSession[i].createConsumer(destination, selector, NO_LOCAL_DEFAULT); + + final int consumerNo = i; + + _consumer[i].setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + onMessageWithConsumerNo(message, consumerNo); + } + }); + + log.debug("Set consumer " + i + " to listen to replies sent to destination: " + destination); + } + } + } + + /** + * Stores the received message in the replies map, then resets the boolean latch that a thread waiting for a + * correlating reply may be waiting on. This is only done if the reply has a correlation id that is expected in the + * replies map. + * + * @param message The received message. + * @param consumerNo The consumer number within this test pinger instance. + */ + public void onMessageWithConsumerNo(Message message, int consumerNo) + { + // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo = " + consumerNo + "): called"); + try + { + long now = System.nanoTime(); + long timestamp = getTimestamp(message); + long pingTime = now - timestamp; + + // NDC.push("id" + instanceId + "/cons" + consumerNo); + + // Extract the messages correlation id. + String correlationID = message.getJMSCorrelationID(); + // log.debug("correlationID = " + correlationID); + + // int num = message.getIntProperty("MSG_NUM"); + // log.info("Message " + num + " received."); + + boolean isRedelivered = message.getJMSRedelivered(); + // log.debug("isRedelivered = " + isRedelivered); + + if (!isRedelivered) + { + // Countdown on the traffic light if there is one for the matching correlation id. + PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); + + if (perCorrelationId != null) + { + CountDownLatch trafficLight = perCorrelationId.trafficLight; + + // Restart the timeout timer on every message. + perCorrelationId.timeOutStart = System.nanoTime(); + + // log.debug("Reply was expected, decrementing the latch for the id, " + correlationID); + + // Release waiting senders if there are some and using maxPending limit. + if ((_maxPendingSize > 0)) + { + // Decrement the count of sent but not yet received messages. + int unreceived = _unreceived.decrementAndGet(); + int unreceivedSize = + (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) + / (_isPubSub ? getConsumersPerDestination() : 1); + + // log.debug("unreceived = " + unreceived); + // log.debug("unreceivedSize = " + unreceivedSize); + + // synchronized (_sendPauseMonitor) + // { + if (unreceivedSize < _maxPendingSize) + { + _sendPauseMonitor.poll(); + } + // } + } + + // Decrement the countdown latch. Before this point, it is possible that two threads might enter this + // method simultanesouly with the same correlation id. Decrementing the latch in a synchronized block + // ensures that each thread will get a unique value for the remaining messages. + long trueCount; + long remainingCount; + + synchronized (trafficLight) + { + trafficLight.countDown(); + + trueCount = trafficLight.getCount(); + remainingCount = trueCount - 1; + + // NDC.push("/rem" + remainingCount); + + // log.debug("remainingCount = " + remainingCount); + // log.debug("trueCount = " + trueCount); + + // Commit on transaction batch size boundaries. At this point in time the waiting producer + // remains blocked, even on the last message. + // Commit count is divided by noOfConsumers in p2p mode, so that each consumer only commits on + // each batch boundary. For pub/sub each consumer gets every message so no division is done. + // When running in client ack mode, an ack is done instead of a commit, on the commit batch + // size boundaries. + long commitCount = _isPubSub ? remainingCount : (remainingCount / _noOfConsumers); + // log.debug("commitCount = " + commitCount); + + if ((commitCount % _txBatchSize) == 0) + { + if (_consAckMode == 2) + { + // log.debug("Doing client ack for consumer " + consumerNo + "."); + message.acknowledge(); + } + else + { + // log.debug("Trying commit for consumer " + consumerNo + "."); + commitTx(_consumerSession[consumerNo]); + // log.info("Tx committed on consumer " + consumerNo); + } + } + + // Forward the message and remaining count to any interested chained message listener. + if (_chainedMessageListener != null) + { + _chainedMessageListener.onMessage(message, (int) remainingCount, pingTime); + } + + // Check if this is the last message, in which case release any waiting producers. This is done + // after the transaction has been committed and any listeners notified. + if (trueCount == 1) + { + trafficLight.countDown(); + } + } + } + else + { + log.warn("Got unexpected message with correlationId: " + correlationID); + } + } + else + { + log.warn("Got redelivered message, ignoring."); + } + } + catch (JMSException e) + { + log.warn("There was a JMSException: " + e.getMessage(), e); + } + finally + { + // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo): ending"); + // NDC.clear(); + } + } + + /** + * Sends the specified number of ping message and then waits for all correlating replies. If the wait times out + * before a reply arrives, then a null reply is returned from this method. This method allows the caller to specify + * the correlation id. + * + * @param message The message to send. If this is null, one is generated. + * @param numPings The number of ping messages to send. + * @param timeout The timeout in milliseconds. + * @param messageCorrelationId The message correlation id. If this is null, one is generated. + * + * @return The number of replies received. This may be less than the number sent if the timeout terminated the wait + * for all prematurely. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + * @throws InterruptedException When interrupted by a timeout + */ + public int pingAndWaitForReply(Message message, int numPings, long timeout, String messageCorrelationId) + throws JMSException, InterruptedException + { + /*log.debug("public int pingAndWaitForReply(Message message, int numPings = " + numPings + ", long timeout = " + + timeout + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ + + // Generate a unique correlation id to put on the messages before sending them, if one was not specified. + if (messageCorrelationId == null) + { + messageCorrelationId = Long.toString(_correlationIdGenerator.incrementAndGet()); + } + + try + { + // NDC.push("prod"); + + // Create a count down latch to count the number of replies with. This is created before the messages are + // sent so that the replies cannot be received before the count down is created. + // One is added to this, so that the last reply becomes a special case. The special case is that the + // chained message listener must be called before this sender can be unblocked, but that decrementing the + // countdown needs to be done before the chained listener can be called. + PerCorrelationId perCorrelationId = new PerCorrelationId(); + + perCorrelationId.trafficLight = new CountDownLatch(getExpectedNumPings(numPings) + 1); + perCorrelationIds.put(messageCorrelationId, perCorrelationId); + + // Set up the current time as the start time for pinging on the correlation id. This is used to determine + // timeouts. + perCorrelationId.timeOutStart = System.nanoTime(); + + // Send the specifed number of messages. + pingNoWaitForReply(message, numPings, messageCorrelationId); + + boolean timedOut; + boolean allMessagesReceived; + int numReplies; + + do + { + // Block the current thread until replies to all the messages are received, or it times out. + perCorrelationId.trafficLight.await(timeout, TimeUnit.MILLISECONDS); + + // Work out how many replies were receieved. + numReplies = getExpectedNumPings(numPings) - (int) perCorrelationId.trafficLight.getCount(); + + allMessagesReceived = numReplies == getExpectedNumPings(numPings); + + // log.debug("numReplies = " + numReplies); + // log.debug("allMessagesReceived = " + allMessagesReceived); + + // Recheck the timeout condition. + long now = System.nanoTime(); + long lastMessageReceievedAt = perCorrelationId.timeOutStart; + timedOut = (now - lastMessageReceievedAt) > (timeout * 1000000); + + // log.debug("now = " + now); + // log.debug("lastMessageReceievedAt = " + lastMessageReceievedAt); + } + while (!timedOut && !allMessagesReceived); + + if ((numReplies < getExpectedNumPings(numPings)) && _verbose) + { + log.info("Timed out (" + timeout + " ms) before all replies received on id, " + messageCorrelationId); + } + else if (_verbose) + { + log.info("Got all replies on id, " + messageCorrelationId); + } + + // commitTx(_consumerSession); + + // log.debug("public int pingAndWaitForReply(Message message, int numPings, long timeout): ending"); + + return numReplies; + } + // Ensure that the message countdown latch is always removed from the reply map. The reply map is long lived, + // so will be a memory leak if this is not done. + finally + { + // NDC.pop(); + perCorrelationIds.remove(messageCorrelationId); + } + } + + /** + * Sends the specified number of ping messages and does not wait for correlating replies. + * + * @param message The message to send. + * @param numPings The number of pings to send. + * @param messageCorrelationId A correlation id to place on all messages sent. + * + * @throws JMSException All underlying JMSExceptions are allowed to fall through. + */ + public void pingNoWaitForReply(Message message, int numPings, String messageCorrelationId) throws JMSException + { + /*log.debug("public void pingNoWaitForReply(Message message, int numPings = " + numPings + + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/ + + if (message == null) + { + message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent); + } + + message.setJMSCorrelationID(messageCorrelationId); + + // Set up a committed flag to detect uncommitted messages at the end of the send loop. This may occurr if the + // transaction batch size is not a factor of the number of pings. In which case an extra commit at the end is + // needed. + boolean committed = false; + + // Send all of the ping messages. + for (int i = 0; i < numPings; i++) + { + // Re-timestamp the message. + // message.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); + + // Send the message, passing in the message count. + committed = sendMessage(i, message); + + // Spew out per message timings on every message sonly in verbose mode. + /*if (_verbose) + { + log.info(timestampFormatter.format(new Date()) + ": Pinged at with correlation id, " + messageCorrelationId); + }*/ + } + + // Call commit if the send loop finished before reaching a batch size boundary so there may still be uncommitted messages. + if (!committed) + { + commitTx(_producerSession); + } + } + + /** + * Sends the sepcified message, applies rate limiting and possibly commits the current transaction. The count of + * messages sent so far must be specified and is used to round robin the ping destinations (where there are more + * than one), and to determine if the transaction batch size has been reached and the sent messages should be + * committed. + * + * @param i The count of messages sent so far in a loop of multiple calls to this send method. + * @param message The message to send. + * + * @return true if the messages were committed, false otherwise. + * + * @throws JMSException All underlyiung JMSExceptions are allowed to fall through. + */ + protected boolean sendMessage(int i, Message message) throws JMSException + { + try + { + NDC.push("id" + instanceId + "/prod"); + + // log.debug("protected boolean sendMessage(int i = " + i + ", Message message): called"); + // log.debug("_txBatchSize = " + _txBatchSize); + + // Round robin the destinations as the messages are sent. + Destination destination = _pingDestinations.get(i % _pingDestinations.size()); + + // Prompt the user to kill the broker when doing failover testing. + _failBeforeSend = waitForUserToPromptOnFailure(_failBeforeSend); + + // Get the test setup for the correlation id. + String correlationID = message.getJMSCorrelationID(); + PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID); + + // If necessary, wait until the max pending message size comes within its limit. + if (_maxPendingSize > 0) + { + synchronized (_sendPauseMonitor) + { + // Used to keep track of the number of times that send has to wait. + int numWaits = 0; + + // The maximum number of waits before the test gives up and fails. This has been chosen to correspond with + // the test timeout. + int waitLimit = (int) (TIMEOUT_DEFAULT / 10000); + + while (true) + { + // Get the size estimate of sent but not yet received messages. + int unreceived = _unreceived.get(); + int unreceivedSize = + (unreceived * ((_messageSize == 0) ? 1 : _messageSize)) + / (_isPubSub ? getConsumersPerDestination() : 1); + + // log.debug("unreceived = " + unreceived); + // log.debug("unreceivedSize = " + unreceivedSize); + // log.debug("_maxPendingSize = " + _maxPendingSize); + + if (unreceivedSize > _maxPendingSize) + { + // log.debug("unreceived size estimate over limit = " + unreceivedSize); + + // Fail the test if the send has had to wait more than the maximum allowed number of times. + if (numWaits > waitLimit) + { + String errorMessage = + "Send has had to wait for the unreceivedSize (" + unreceivedSize + + ") to come below the maxPendingSize (" + _maxPendingSize + ") more that " + waitLimit + + " times."; + log.warn(errorMessage); + throw new RuntimeException(errorMessage); + } + + // Wait on the send pause barrier for the limit to be re-established. + try + { + long start = System.nanoTime(); + // _sendPauseMonitor.wait(10000); + _sendPauseMonitor.offer(new Object(), 10000, TimeUnit.MILLISECONDS); + long end = System.nanoTime(); + + // Count the wait only if it was for > 99% of the requested wait time. + if (((float) (end - start) / (float) (10000 * 1000000L)) > 0.99) + { + numWaits++; + } + } + catch (InterruptedException e) + { + // Restore the interrupted status + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + else + { + break; + } + } + } + } + + // Send the message either to its round robin destination, or its default destination. + // int num = numSent.incrementAndGet(); + // message.setIntProperty("MSG_NUM", num); + setTimestamp(message); + + if (destination == null) + { + _producer.send(message); + } + else + { + _producer.send(destination, message); + } + + // Increase the unreceived size, this may actually happen after the message is received. + // The unreceived size is incremented by the number of consumers that will get a copy of the message, + // in pub/sub mode. + if (_maxPendingSize > 0) + { + int newUnreceivedCount = _unreceived.addAndGet(_isPubSub ? getConsumersPerDestination() : 1); + // log.debug("newUnreceivedCount = " + newUnreceivedCount); + } + + // Apply message rate throttling if a rate limit has been set up. + if (_rateLimiter != null) + { + _rateLimiter.throttle(); + } + + // Call commit every time the commit batch size is reached. + boolean committed = false; + + // Commit on every transaction batch size boundary. Here i + 1 is the count of actual messages sent. + if (((i + 1) % _txBatchSize) == 0) + { + // log.debug("Trying commit on producer session."); + committed = commitTx(_producerSession); + } + + return committed; + } + finally + { + NDC.clear(); + } + } + + /** + * If the specified fail flag is set, this method waits for the user to cause a failure and then indicate to the + * test that the failure has occurred, before the method returns. + * + * @param failFlag The fail flag to test. + * + * @return The new value for the fail flag. If the {@link #_failOnce} flag is set, then each fail flag is only + * used once, then reset. + */ + private boolean waitForUserToPromptOnFailure(boolean failFlag) + { + if (failFlag) + { + if (_failOnce) + { + failFlag = false; + } + + // log.debug("Failing Before Send"); + waitForUser(KILL_BROKER_PROMPT); + } + + return failFlag; + } + + /** + * Implements a single iteration of the ping loop. This sends the number of pings specified by the transaction batch + * size property, and waits for replies to all of them. Any errors cause the publish flag to be cleared, which will + * terminate the pinger. + */ + public void pingLoop() + { + try + { + // Generate a sample message and time stamp it. + Message msg = getTestMessage(_replyDestination, _messageSize, _persistent); + // setTimestamp(msg); + + // Send the message and wait for a reply. + pingAndWaitForReply(msg, TX_BATCH_SIZE_DEFAULT, TIMEOUT_DEFAULT, null); + } + catch (JMSException e) + { + _publish = false; + // log.debug("There was a JMSException: " + e.getMessage(), e); + } + catch (InterruptedException e) + { + _publish = false; + // log.debug("There was an interruption: " + e.getMessage(), e); + } + } + + /** + * Sets a chained message listener. The message listener on this pinger, chains all its messages to the one set + * here. + * + * @param messageListener The chained message listener. + */ + public void setChainedMessageListener(ChainedMessageListener messageListener) + { + _chainedMessageListener = messageListener; + } + + /** Removes any chained message listeners from this pinger. */ + public void removeChainedMessageListener() + { + _chainedMessageListener = null; + } + + /** + * Generates a test message of the specified size, with the specified reply-to destination and persistence flag. + * + * @param replyQueue The reply-to destination for the message. + * @param messageSize The desired size of the message in bytes. + * @param persistent true if the message should use persistent delivery, false otherwise. + * + * @return A freshly generated test message. + * + * @throws javax.jms.JMSException All underlying JMSException are allowed to fall through. + */ + public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException + { + // return TestMessageFactory.newObjectMessage(_producerSession, replyQueue, messageSize, persistent); + return TestUtils.createTestMessageOfSize(_producerSession, messageSize); + } + + /** + * Sets the current time in nanoseconds as the timestamp on the message. + * + * @param msg The message to timestamp. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + protected void setTimestamp(Message msg) throws JMSException + { + /*if (((AMQSession)_producerSession).isStrictAMQP()) + { + ((AMQMessage)msg).setTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME), System.nanoTime()); + } + else + {*/ + msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime()); + // } + } + + /** + * Extracts the nanosecond timestamp from a message. + * + * @param msg The message to extract the time stamp from. + * + * @return The timestamp in nanos. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + protected long getTimestamp(Message msg) throws JMSException + { + /*if (((AMQSession)_producerSession).isStrictAMQP()) + { + Long value = ((AMQMessage)msg).getTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME)); + + return (value == null) ? 0L : value; + } + else + {*/ + return msg.getLongProperty(PingPongProducer.MESSAGE_TIMESTAMP_PROPNAME); + // } + } + + /** + * Stops the ping loop by clearing the publish flag. The current loop will complete when it notices that this flag + * has been cleared. + */ + public void stop() + { + _publish = false; + } + + /** + * Starts the producer and consumer connections. + * + * @throws JMSException Any JMSExceptions are allowed to fall through. + */ + public void start() throws JMSException + { + // log.debug("public void start(): called"); + + _connection.start(); + // log.debug("Producer started."); + + for (int i = 0; i < _noOfConsumers; i++) + { + _consumerConnection[i].start(); + // log.debug("Consumer " + i + " started."); + } + } + + /** Implements a ping loop that repeatedly pings until the publish flag becomes false. */ + public void run() + { + // Keep running until the publish flag is cleared. + while (_publish) + { + pingLoop(); + } + } + + /** + * Callback method, implementing ExceptionListener. This should be registered to listen for exceptions on the + * connection, this clears the publish flag which in turn will halt the ping loop. + * + * @param e The exception that triggered this callback method. + */ + public void onException(JMSException e) + { + // log.debug("public void onException(JMSException e = " + e + "): called", e); + _publish = false; + } + + /** + * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered + * with the runtime system as a shutdown hook. + * + * @return A shutdown hook for the ping loop. + */ + public Thread getShutdownHook() + { + return new Thread(new Runnable() + { + public void run() + { + stop(); + } + }); + } + + /** + * Closes all of the producer and consumer connections. + * + * @throws JMSException All JMSException are allowed to fall through. + */ + public void close() throws JMSException + { + // log.debug("public void close(): called"); + + try + { + if (_connection != null) + { + // log.debug("Before close producer connection."); + _connection.close(); + // log.debug("Closed producer connection."); + } + + for (int i = 0; i < _noOfConsumers; i++) + { + if (_consumerConnection[i] != null) + { + // log.debug("Before close consumer connection " + i + "."); + _consumerConnection[i].close(); + // log.debug("Closed consumer connection " + i + "."); + } + } + } + finally + { + _connection = null; + _producerSession = null; + _consumerSession = null; + _consumerConnection = null; + _producer = null; + _consumer = null; + _pingDestinations = null; + _replyDestination = null; + } + } + + /** + * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not a + * transactional controlSession, this method does nothing (unless the failover after send flag is set). + * + *

          If the {@link #_failAfterSend} flag is set, this will prompt the user to kill the broker before the commit is + * applied. This flag applies whether the pinger is transactional or not. + * + *

          If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the commit + * is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker after the + * commit is applied. These flags will only apply if using a transactional pinger. + * + * @param session The controlSession to commit + * + * @return true if the controlSession was committed, false if it was not. + * + * @throws javax.jms.JMSException If the commit fails and then the rollback fails. + * + * @todo Consider moving the fail after send logic into the send method. It is confusing to have it in this commit + * method, because commits only apply to transactional pingers, but fail after send applied to transactional and + * non-transactional alike. + */ + protected boolean commitTx(Session session) throws JMSException + { + // log.debug("protected void commitTx(Session session): called"); + + boolean committed = false; + + _failAfterSend = waitForUserToPromptOnFailure(_failAfterSend); + + if (session.getTransacted()) + { + // log.debug("Session is transacted."); + + try + { + _failBeforeCommit = waitForUserToPromptOnFailure(_failBeforeCommit); + + long start = System.nanoTime(); + session.commit(); + committed = true; + // log.debug("Time taken to commit :" + ((System.nanoTime() - start) / 1000000f) + " ms"); + + _failAfterCommit = waitForUserToPromptOnFailure(_failAfterCommit); + + // log.debug("Session Commited."); + } + catch (JMSException e) + { + // log.debug("JMSException on commit:" + e.getMessage(), e); + + try + { + session.rollback(); + // log.debug("Message rolled back."); + } + catch (JMSException jmse) + { + // log.debug("JMSE on rollback:" + jmse.getMessage(), jmse); + + // Both commit and rollback failed. Throw the rollback exception. + throw jmse; + } + } + } + + return committed; + } + + /** + * Outputs a prompt to the console and waits for the user to press return. + * + * @param prompt The prompt to display on the console. + */ + public void waitForUser(String prompt) + { + System.out.println(prompt); + + try + { + System.in.read(); + } + catch (IOException e) + { + // Ignored. + } + + System.out.println("Continuing."); + } + + /** + * Gets the number of consumers that are listening to each destination in the test. + * + * @return int The number of consumers subscribing to each topic. + */ + public int getConsumersPerDestination() + { + return _noOfConsumers; + } + + /** + * Calculates how many pings are expected to be received for the given number sent. + * + * @param numpings The number of pings that will be sent. + * + * @return The number that should be received, for the test to pass. + */ + public int getExpectedNumPings(int numpings) + { + // log.debug("public int getExpectedNumPings(int numpings = " + numpings + "): called"); + + // log.debug("Each ping will be received by " + (_isPubSub ? getConsumersPerDestination() : 1) + " consumers."); + + return numpings * (_isPubSub ? getConsumersPerDestination() : 1); + } + + /** + * Defines a chained message listener interface that can be attached to this pinger. Whenever this pinger's {@link + * PingPongProducer#onMessageWithConsumerNo} method is called, the chained listener set through the {@link + * PingPongProducer#setChainedMessageListener} method is passed the message, and the remaining expected count of + * messages with that correlation id. + * + *

          Provided only one pinger is producing messages with that correlation id, the chained listener will always be + * given unique message counts. It will always be called while the producer waiting for all messages to arrive is + * still blocked. + */ + public static interface ChainedMessageListener + { + /** + * Notifies interested listeners about message arrival and important test stats, the number of messages + * remaining in the test, and the messages send timestamp. + * + * @param message The newly arrived message. + * @param remainingCount The number of messages left to complete the test. + * @param latency The nanosecond latency of the message. + * + * @throws JMSException Any JMS exceptions is allowed to fall through. + */ + public void onMessage(Message message, int remainingCount, long latency) throws JMSException; + } + + /** + * Holds information on each correlation id. The countdown latch, the current timeout timer... More stuff to be + * added to this: read/write lock to make onMessage more concurrent as described in class header comment. + */ + protected static class PerCorrelationId + { + /** Holds a countdown on number of expected messages. */ + CountDownLatch trafficLight; + + /** Holds the last timestamp that the timeout was reset to. */ + Long timeOutStart; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java index 2610b32220..009254c612 100644 --- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java +++ b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java @@ -1,251 +1,251 @@ -/* - * - * 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.requestreply; - -import junit.framework.Assert; -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.AsymptoticTestCase; -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -/** - * PingPongTestPerf is a full round trip ping test, that has been written with the intention of being scaled up to run - * many times simultaneously to simluate many clients/producer/connections. A full round trip ping sends a message from - * a producer to a conumer, then the consumer replies to the message on a temporary queue. - * - *

          A single run of the test using the default JUnit test runner will result in the sending and timing of the number - * of pings specified by the test size and time how long it takes for all of these to complete. This test may be scaled - * up using a suitable JUnit test runner. See {@link org.apache.qpid.junit.extensions.TKTestRunner} for more - * information on how to do this. - * - *

          The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a - * temporary queue for replies. This setup is only established once for all the test repeats, but each test threads - * gets its own connection/producer/consumer, this is only re-established if the connection is lost. - * - *

          The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that - * is the name of the temporary queue, fires off many messages on the original queue and waits for them all to come - * back on the temporary queue. - * - *

          Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - */ -public class PingPongTestPerf extends AsymptoticTestCase -{ - private static Logger _logger = Logger.getLogger(PingPongTestPerf.class); - - /** Thread local to hold the per-thread test setup fields. */ - ThreadLocal threadSetup = new ThreadLocal(); - - // Set up a property reader to extract the test parameters from. Once ContextualProperties is available in - // the project dependencies, use it to get property overrides for configurable tests and to notify the test runner - // of the test parameters to log with the results. It also providers some basic type parsing convenience methods. - // private Properties testParameters = System.getProperties(); - private ParsedProperties testParameters = - TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); - - public PingPongTestPerf(String name) - { - super(name); - - _logger.debug(testParameters); - - // Sets up the test parameters with defaults. - /*testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, - Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.MESSAGE_SIZE_PROPNAME, - Integer.toString(PingPongProducer.MESSAGE_SIZE_DEAFULT)); - testParameters.setPropertyIfNull(PingPongProducer.PING_QUEUE_NAME_PROPNAME, - PingPongProducer.PING_QUEUE_NAME_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.PERSISTENT_MODE_PROPNAME, - Boolean.toString(PingPongProducer.PERSISTENT_MODE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.TRANSACTED_PROPNAME, - Boolean.toString(PingPongProducer.TRANSACTED_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.BROKER_PROPNAME, PingPongProducer.BROKER_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.USERNAME_PROPNAME, PingPongProducer.USERNAME_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.PASSWORD_PROPNAME, PingPongProducer.PASSWORD_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.VIRTUAL_HOST_PROPNAME, PingPongProducer.VIRTUAL_HOST_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.VERBOSE_PROPNAME, - Boolean.toString(PingPongProducer.VERBOSE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.RATE_PROPNAME, Integer.toString(PingPongProducer.RATE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.PUBSUB_PROPNAME, - Boolean.toString(PingPongProducer.PUBSUB_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, - Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.TIMEOUT_PROPNAME, Long.toString(PingPongProducer.TIMEOUT_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.DESTINATION_COUNT_PROPNAME, - Integer.toString(PingPongProducer.DESTINATION_COUNT_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_COMMIT_PROPNAME, - PingPongProducer.FAIL_AFTER_COMMIT_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_COMMIT_PROPNAME, - PingPongProducer.FAIL_BEFORE_COMMIT_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_SEND_PROPNAME, - PingPongProducer.FAIL_AFTER_SEND_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_SEND_PROPNAME, - PingPongProducer.FAIL_BEFORE_SEND_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.FAIL_ONCE_PROPNAME, PingPongProducer.FAIL_ONCE_DEFAULT); - testParameters.setPropertyIfNull(PingPongProducer.UNIQUE_DESTS_PROPNAME, - Boolean.toString(PingPongProducer.UNIQUE_DESTS_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.ACK_MODE_PROPNAME, - Integer.toString(PingPongProducer.ACK_MODE_DEFAULT)); - testParameters.setPropertyIfNull(PingPongProducer.PAUSE_AFTER_BATCH_PROPNAME, - PingPongProducer.PAUSE_AFTER_BATCH_DEFAULT);*/ - } - - /** - * Compile all the tests into a test suite. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Ping-Pong Performance Tests"); - - // Run performance tests in read committed mode. - suite.addTest(new PingPongTestPerf("testPingPongOk")); - - return suite; - } - - private static void setSystemPropertyIfNull(String propName, String propValue) - { - if (System.getProperty(propName) == null) - { - System.setProperty(propName, propValue); - } - } - - public void testPingPongOk(int numPings) throws Exception - { - // Get the per thread test setup to run the test through. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Generate a sample message. This message is already time stamped and has its reply-to destination set. - Message msg = - perThreadSetup._testPingProducer.getTestMessage(perThreadSetup._testPingProducer.getReplyDestinations().get(0), - testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), - testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); - - // Send the message and wait for a reply. - int numReplies = - perThreadSetup._testPingProducer.pingAndWaitForReply(msg, numPings, PingPongProducer.TIMEOUT_DEFAULT, null); - - // Fail the test if the timeout was exceeded. - if (numReplies != numPings) - { - Assert.fail("The ping timed out, got " + numReplies + " out of " + numPings); - } - } - - /** - * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. - */ - public void threadSetUp() - { - try - { - PerThreadSetup perThreadSetup = new PerThreadSetup(); - - // Extract the test set up paramaeters. - String brokerDetails = testParameters.getProperty(PingPongProducer.BROKER_PROPNAME); - String username = testParameters.getProperty(PingPongProducer.USERNAME_PROPNAME); - String password = testParameters.getProperty(PingPongProducer.PASSWORD_PROPNAME); - String virtualPath = testParameters.getProperty(PingPongProducer.VIRTUAL_HOST_PROPNAME); - String destinationName = testParameters.getProperty(PingPongProducer.PING_QUEUE_NAME_PROPNAME); - boolean persistent = testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME); - boolean transacted = testParameters.getPropertyAsBoolean(PingPongProducer.TRANSACTED_PROPNAME); - String selector = testParameters.getProperty(PingPongProducer.SELECTOR_PROPNAME); - boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME); - boolean pubsub = testParameters.getPropertyAsBoolean(PingPongProducer.PUBSUB_PROPNAME); - - synchronized (this) - { - // Establish a bounce back client on the ping queue to bounce back the pings. - perThreadSetup._testPingBouncer = - new PingPongBouncer(brokerDetails, username, password, virtualPath, destinationName, persistent, - transacted, selector, verbose, pubsub); - - // Start the connections for client and producer running. - perThreadSetup._testPingBouncer.getConnection().start(); - - // Establish a ping-pong client on the ping queue to send the pings and receive replies with. - perThreadSetup._testPingProducer = new PingPongProducer(testParameters); - perThreadSetup._testPingProducer.establishConnection(true, true); - perThreadSetup._testPingProducer.start(); - } - - // Attach the per-thread set to the thread. - threadSetup.set(perThreadSetup); - } - catch (Exception e) - { - _logger.warn("There was an exception during per thread setup.", e); - } - } - - /** - * Performs test fixture clean - */ - public void threadTearDown() - { - _logger.debug("public void threadTearDown(): called"); - - try - { - // Get the per thread test fixture. - PerThreadSetup perThreadSetup = threadSetup.get(); - - // Close the pingers so that it cleans up its connection cleanly. - synchronized (this) - { - perThreadSetup._testPingProducer.close(); - // perThreadSetup._testPingBouncer.close(); - } - - // Ensure the per thread fixture is reclaimed. - threadSetup.remove(); - } - catch (JMSException e) - { - _logger.warn("There was an exception during per thread tear down."); - } - } - - protected static class PerThreadSetup - { - /** - * Holds the test ping-pong producer. - */ - private PingPongProducer _testPingProducer; - - /** - * Holds the test ping client. - */ - private PingPongBouncer _testPingBouncer; - } -} +/* + * + * 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.requestreply; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.AsymptoticTestCase; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +/** + * PingPongTestPerf is a full round trip ping test, that has been written with the intention of being scaled up to run + * many times simultaneously to simluate many clients/producer/connections. A full round trip ping sends a message from + * a producer to a conumer, then the consumer replies to the message on a temporary queue. + * + *

          A single run of the test using the default JUnit test runner will result in the sending and timing of the number + * of pings specified by the test size and time how long it takes for all of these to complete. This test may be scaled + * up using a suitable JUnit test runner. See {@link org.apache.qpid.junit.extensions.TKTestRunner} for more + * information on how to do this. + * + *

          The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a + * temporary queue for replies. This setup is only established once for all the test repeats, but each test threads + * gets its own connection/producer/consumer, this is only re-established if the connection is lost. + * + *

          The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that + * is the name of the temporary queue, fires off many messages on the original queue and waits for them all to come + * back on the temporary queue. + * + *

          Configurable test properties: message size, transacted or not, persistent or not. Broker connection details. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + */ +public class PingPongTestPerf extends AsymptoticTestCase +{ + private static Logger _logger = Logger.getLogger(PingPongTestPerf.class); + + /** Thread local to hold the per-thread test setup fields. */ + ThreadLocal threadSetup = new ThreadLocal(); + + // Set up a property reader to extract the test parameters from. Once ContextualProperties is available in + // the project dependencies, use it to get property overrides for configurable tests and to notify the test runner + // of the test parameters to log with the results. It also providers some basic type parsing convenience methods. + // private Properties testParameters = System.getProperties(); + private ParsedProperties testParameters = + TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/); + + public PingPongTestPerf(String name) + { + super(name); + + _logger.debug(testParameters); + + // Sets up the test parameters with defaults. + /*testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, + Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.MESSAGE_SIZE_PROPNAME, + Integer.toString(PingPongProducer.MESSAGE_SIZE_DEAFULT)); + testParameters.setPropertyIfNull(PingPongProducer.PING_QUEUE_NAME_PROPNAME, + PingPongProducer.PING_QUEUE_NAME_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.PERSISTENT_MODE_PROPNAME, + Boolean.toString(PingPongProducer.PERSISTENT_MODE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.TRANSACTED_PROPNAME, + Boolean.toString(PingPongProducer.TRANSACTED_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.BROKER_PROPNAME, PingPongProducer.BROKER_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.USERNAME_PROPNAME, PingPongProducer.USERNAME_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.PASSWORD_PROPNAME, PingPongProducer.PASSWORD_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.VIRTUAL_HOST_PROPNAME, PingPongProducer.VIRTUAL_HOST_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.VERBOSE_PROPNAME, + Boolean.toString(PingPongProducer.VERBOSE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.RATE_PROPNAME, Integer.toString(PingPongProducer.RATE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.PUBSUB_PROPNAME, + Boolean.toString(PingPongProducer.PUBSUB_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME, + Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.TIMEOUT_PROPNAME, Long.toString(PingPongProducer.TIMEOUT_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.DESTINATION_COUNT_PROPNAME, + Integer.toString(PingPongProducer.DESTINATION_COUNT_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_COMMIT_PROPNAME, + PingPongProducer.FAIL_AFTER_COMMIT_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_COMMIT_PROPNAME, + PingPongProducer.FAIL_BEFORE_COMMIT_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_SEND_PROPNAME, + PingPongProducer.FAIL_AFTER_SEND_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_SEND_PROPNAME, + PingPongProducer.FAIL_BEFORE_SEND_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.FAIL_ONCE_PROPNAME, PingPongProducer.FAIL_ONCE_DEFAULT); + testParameters.setPropertyIfNull(PingPongProducer.UNIQUE_DESTS_PROPNAME, + Boolean.toString(PingPongProducer.UNIQUE_DESTS_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.ACK_MODE_PROPNAME, + Integer.toString(PingPongProducer.ACK_MODE_DEFAULT)); + testParameters.setPropertyIfNull(PingPongProducer.PAUSE_AFTER_BATCH_PROPNAME, + PingPongProducer.PAUSE_AFTER_BATCH_DEFAULT);*/ + } + + /** + * Compile all the tests into a test suite. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Ping-Pong Performance Tests"); + + // Run performance tests in read committed mode. + suite.addTest(new PingPongTestPerf("testPingPongOk")); + + return suite; + } + + private static void setSystemPropertyIfNull(String propName, String propValue) + { + if (System.getProperty(propName) == null) + { + System.setProperty(propName, propValue); + } + } + + public void testPingPongOk(int numPings) throws Exception + { + // Get the per thread test setup to run the test through. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Generate a sample message. This message is already time stamped and has its reply-to destination set. + Message msg = + perThreadSetup._testPingProducer.getTestMessage(perThreadSetup._testPingProducer.getReplyDestinations().get(0), + testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME), + testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME)); + + // Send the message and wait for a reply. + int numReplies = + perThreadSetup._testPingProducer.pingAndWaitForReply(msg, numPings, PingPongProducer.TIMEOUT_DEFAULT, null); + + // Fail the test if the timeout was exceeded. + if (numReplies != numPings) + { + Assert.fail("The ping timed out, got " + numReplies + " out of " + numPings); + } + } + + /** + * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. + */ + public void threadSetUp() + { + try + { + PerThreadSetup perThreadSetup = new PerThreadSetup(); + + // Extract the test set up paramaeters. + String brokerDetails = testParameters.getProperty(PingPongProducer.BROKER_PROPNAME); + String username = testParameters.getProperty(PingPongProducer.USERNAME_PROPNAME); + String password = testParameters.getProperty(PingPongProducer.PASSWORD_PROPNAME); + String virtualPath = testParameters.getProperty(PingPongProducer.VIRTUAL_HOST_PROPNAME); + String destinationName = testParameters.getProperty(PingPongProducer.PING_QUEUE_NAME_PROPNAME); + boolean persistent = testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME); + boolean transacted = testParameters.getPropertyAsBoolean(PingPongProducer.TRANSACTED_PROPNAME); + String selector = testParameters.getProperty(PingPongProducer.SELECTOR_PROPNAME); + boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME); + boolean pubsub = testParameters.getPropertyAsBoolean(PingPongProducer.PUBSUB_PROPNAME); + + synchronized (this) + { + // Establish a bounce back client on the ping queue to bounce back the pings. + perThreadSetup._testPingBouncer = + new PingPongBouncer(brokerDetails, username, password, virtualPath, destinationName, persistent, + transacted, selector, verbose, pubsub); + + // Start the connections for client and producer running. + perThreadSetup._testPingBouncer.getConnection().start(); + + // Establish a ping-pong client on the ping queue to send the pings and receive replies with. + perThreadSetup._testPingProducer = new PingPongProducer(testParameters); + perThreadSetup._testPingProducer.establishConnection(true, true); + perThreadSetup._testPingProducer.start(); + } + + // Attach the per-thread set to the thread. + threadSetup.set(perThreadSetup); + } + catch (Exception e) + { + _logger.warn("There was an exception during per thread setup.", e); + } + } + + /** + * Performs test fixture clean + */ + public void threadTearDown() + { + _logger.debug("public void threadTearDown(): called"); + + try + { + // Get the per thread test fixture. + PerThreadSetup perThreadSetup = threadSetup.get(); + + // Close the pingers so that it cleans up its connection cleanly. + synchronized (this) + { + perThreadSetup._testPingProducer.close(); + // perThreadSetup._testPingBouncer.close(); + } + + // Ensure the per thread fixture is reclaimed. + threadSetup.remove(); + } + catch (JMSException e) + { + _logger.warn("There was an exception during per thread tear down."); + } + } + + protected static class PerThreadSetup + { + /** + * Holds the test ping-pong producer. + */ + private PingPongProducer _testPingProducer; + + /** + * Holds the test ping client. + */ + private PingPongBouncer _testPingBouncer; + } +} diff --git a/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java b/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java index f699295b06..0fcb0a8538 100644 --- a/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java +++ b/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java @@ -1,199 +1,199 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.testcases; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import org.apache.qpid.test.framework.Assertion; -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import org.apache.qpid.junit.extensions.TestThreadAware; -import org.apache.qpid.junit.extensions.TimingController; -import org.apache.qpid.junit.extensions.TimingControllerAware; -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import java.util.LinkedList; - -/** - * MessageThroughputPerf runs a test over a {@link Circuit} controlled by the test parameters. It logs timings of - * the time required to receive samples consisting of batches of messages. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Measure message throughput accross a test circuit. {@link Circuit} - *
          - * - * @todo Check that all of the messages were sent. Check that the receiving end got the same number of messages as - * the publishing end. - * - * @todo Set this up to run with zero sized tests. Size zero means send forever. Continuous sending to be interrupted - * by completion of the test duration, or shutdown hook when the user presses Ctrl-C. - */ -public class MessageThroughputPerf extends FrameworkBaseCase implements TimingControllerAware, TestThreadAware -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(MessageThroughputPerf.class); - - /** Holds the timing controller, used to log test timings from self-timed tests. */ - private TimingController timingController; - - /** Thread local to hold the per-thread test setup fields. */ - ThreadLocal threadSetup = new ThreadLocal(); - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public MessageThroughputPerf(String name) - { - super(name); - } - - /** - * Performs the a basic P2P test case. - * - * @param numMessages The number of messages to send in the test. - */ - public void testThroughput(int numMessages) - { - log.debug("public void testThroughput(): called"); - - PerThreadSetup setup = threadSetup.get(); - assertNoFailures(setup.testCircuit.test(numMessages, new LinkedList())); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as known to the test - * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test - * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case - * name "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - log.debug("public String getTestCaseNameForTestMethod(String methodName = " + methodName + "): called"); - - return "DEFAULT_CIRCUIT_TEST"; - } - - /** - * Used by test runners that can supply a {@link org.apache.qpid.junit.extensions.TimingController} to set the - * controller on an aware test. - * - * @param controller The timing controller. - */ - public void setTimingController(TimingController controller) - { - timingController = controller; - } - - /** - * Overrides the parent setUp method so that the in-vm broker creation is not done on a per test basis. - * - * @throws Exception Any exceptions allowed to fall through and fail the test. - */ - protected void setUp() throws Exception - { - NDC.push(getName()); - - testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - } - - /** - * Overrides the parent setUp method so that the in-vm broker clean-up is not done on a per test basis. - */ - protected void tearDown() - { - NDC.pop(); - } - - /** - * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. - */ - public void threadSetUp() - { - // Run the test setup tasks. This may create an in-vm broker, if a decorator has injected a task for this. - taskHandler.runSetupTasks(); - - // Get the test parameters, any overrides on the command line will have been applied. - ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - // Customize the test parameters. - testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST"); - testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue"); - - // Get the test circuit factory to create test circuits and run the standard test procedure through. - CircuitFactory circuitFactory = getCircuitFactory(); - - // Create the test circuit. This projects the circuit onto the available test nodes and connects it up. - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Store the test configuration for the thread. - PerThreadSetup setup = new PerThreadSetup(); - setup.testCircuit = testCircuit; - threadSetup.set(setup); - } - - /** - * Called when a test thread is destroyed. - */ - public void threadTearDown() - { - // Run the test teardown tasks. This may destroy the in-vm broker, if a decorator has injected a task for this. - taskHandler.runSetupTasks(); - } - - /** - * Holds the per-thread test configurations. - */ - protected static class PerThreadSetup - { - /** Holds the test circuit to run tests on. */ - Circuit testCircuit; - } - - /** - * Compiles all the tests in this class into a suite. - * - * @return The test suite. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Qpid Throughput Performance Tests"); - - suite.addTest(new MessageThroughputPerf("testThroughput")); - - return suite; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.testcases; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.test.framework.Assertion; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.TestThreadAware; +import org.apache.qpid.junit.extensions.TimingController; +import org.apache.qpid.junit.extensions.TimingControllerAware; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import java.util.LinkedList; + +/** + * MessageThroughputPerf runs a test over a {@link Circuit} controlled by the test parameters. It logs timings of + * the time required to receive samples consisting of batches of messages. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Measure message throughput accross a test circuit. {@link Circuit} + *
          + * + * @todo Check that all of the messages were sent. Check that the receiving end got the same number of messages as + * the publishing end. + * + * @todo Set this up to run with zero sized tests. Size zero means send forever. Continuous sending to be interrupted + * by completion of the test duration, or shutdown hook when the user presses Ctrl-C. + */ +public class MessageThroughputPerf extends FrameworkBaseCase implements TimingControllerAware, TestThreadAware +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(MessageThroughputPerf.class); + + /** Holds the timing controller, used to log test timings from self-timed tests. */ + private TimingController timingController; + + /** Thread local to hold the per-thread test setup fields. */ + ThreadLocal threadSetup = new ThreadLocal(); + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public MessageThroughputPerf(String name) + { + super(name); + } + + /** + * Performs the a basic P2P test case. + * + * @param numMessages The number of messages to send in the test. + */ + public void testThroughput(int numMessages) + { + log.debug("public void testThroughput(): called"); + + PerThreadSetup setup = threadSetup.get(); + assertNoFailures(setup.testCircuit.test(numMessages, new LinkedList())); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + log.debug("public String getTestCaseNameForTestMethod(String methodName = " + methodName + "): called"); + + return "DEFAULT_CIRCUIT_TEST"; + } + + /** + * Used by test runners that can supply a {@link org.apache.qpid.junit.extensions.TimingController} to set the + * controller on an aware test. + * + * @param controller The timing controller. + */ + public void setTimingController(TimingController controller) + { + timingController = controller; + } + + /** + * Overrides the parent setUp method so that the in-vm broker creation is not done on a per test basis. + * + * @throws Exception Any exceptions allowed to fall through and fail the test. + */ + protected void setUp() throws Exception + { + NDC.push(getName()); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + } + + /** + * Overrides the parent setUp method so that the in-vm broker clean-up is not done on a per test basis. + */ + protected void tearDown() + { + NDC.pop(); + } + + /** + * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. + */ + public void threadSetUp() + { + // Run the test setup tasks. This may create an in-vm broker, if a decorator has injected a task for this. + taskHandler.runSetupTasks(); + + // Get the test parameters, any overrides on the command line will have been applied. + ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + // Customize the test parameters. + testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST"); + testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue"); + + // Get the test circuit factory to create test circuits and run the standard test procedure through. + CircuitFactory circuitFactory = getCircuitFactory(); + + // Create the test circuit. This projects the circuit onto the available test nodes and connects it up. + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Store the test configuration for the thread. + PerThreadSetup setup = new PerThreadSetup(); + setup.testCircuit = testCircuit; + threadSetup.set(setup); + } + + /** + * Called when a test thread is destroyed. + */ + public void threadTearDown() + { + // Run the test teardown tasks. This may destroy the in-vm broker, if a decorator has injected a task for this. + taskHandler.runSetupTasks(); + } + + /** + * Holds the per-thread test configurations. + */ + protected static class PerThreadSetup + { + /** Holds the test circuit to run tests on. */ + Circuit testCircuit; + } + + /** + * Compiles all the tests in this class into a suite. + * + * @return The test suite. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Qpid Throughput Performance Tests"); + + suite.addTest(new MessageThroughputPerf("testThroughput")); + + return suite; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java b/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java index 3a3b7f92dd..2d89d319d7 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java +++ b/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java @@ -1,308 +1,308 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.exchange; - -import org.apache.qpid.jms.Session; -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -/** - * MessagingTestConfigProperties defines a set of property names and default values for specifying a messaging topology, - * and test parameters for running a messaging test over that topology. A Properties object holding some of these - * properties, superimposed onto the defaults, is used to establish test topologies and control test behaviour. - * - *

          A complete list of the parameters, default values and comments on their usage is provided here: - * - *

          - *
          Parameters
          Parameter Default Comments - *
          messageSize 0 Message size in bytes. Not including any headers. - *
          destinationName ping The root name to use to generate destination names to ping. - *
          persistent false Determines whether peristent delivery is used. - *
          transacted false Determines whether messages are sent/received in transactions. - *
          broker tcp://localhost:5672 Determines the broker to connect to. - *
          virtualHost test Determines the virtual host to send all ping over. - *
          rate 0 The maximum rate (in hertz) to send messages at. 0 means no limit. - *
          verbose false The verbose flag for debugging. Prints to console on every message. - *
          pubsub false Whether to ping topics or queues. Uses p2p by default. - *
          username guest The username to access the broker with. - *
          password guest The password to access the broker with. - *
          selector null Not used. Defines a message selector to filter pings with. - *
          destinationCount 1 The number of receivers listening to the pings. - *
          timeout 30000 In milliseconds. The timeout to stop waiting for replies. - *
          commitBatchSize 1 The number of messages per transaction in transactional mode. - *
          uniqueDests true Whether each receiver only listens to one ping destination or all. - *
          durableDests false Whether or not durable destinations are used. - *
          ackMode AUTO_ACK The message acknowledgement mode. Possible values are: - * 0 - SESSION_TRANSACTED - * 1 - AUTO_ACKNOWLEDGE - * 2 - CLIENT_ACKNOWLEDGE - * 3 - DUPS_OK_ACKNOWLEDGE - * 257 - NO_ACKNOWLEDGE - * 258 - PRE_ACKNOWLEDGE - *
          maxPending 0 The maximum size in bytes, of messages sent but not yet received. - * Limits the volume of messages currently buffered on the client - * or broker. Can help scale test clients by limiting amount of buffered - * data to avoid out of memory errors. - *
          - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Provide the names and defaults of all test parameters. - *
          - */ -public class MessagingTestConfigProperties -{ - // ====================== Connection Properties ================================== - - /** Holds the name of the default connection configuration. */ - public static final String CONNECTION_NAME = "broker"; - - /** Holds the name of the property to get the initial context factory name from. */ - public static final String INITIAL_CONTEXT_FACTORY_PROPNAME = "java.naming.factory.initial"; - - /** Defines the class to use as the initial context factory by default. */ - public static final String INITIAL_CONTEXT_FACTORY_DEFAULT = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"; - - /** Holds the name of the default connection factory configuration property. */ - public static final String CONNECTION_PROPNAME = "connectionfactory.broker"; - - /** Defeins the default connection configuration. */ - public static final String CONNECTION_DEFAULT = "amqp://guest:guest@clientid/?brokerlist='vm://:1'"; - - /** Holds the name of the property to get the test broker url from. */ - public static final String BROKER_PROPNAME = "qpid.test.broker"; - - /** Holds the default broker url for the test. */ - public static final String BROKER_DEFAULT = "vm://:1"; - - /** Holds the name of the property to get the test broker virtual path. */ - public static final String VIRTUAL_HOST_PROPNAME = "virtualHost"; - - /** Holds the default virtual path for the test. */ - public static final String VIRTUAL_HOST_DEFAULT = ""; - - /** Holds the name of the property to get the broker access username from. */ - public static final String USERNAME_PROPNAME = "username"; - - /** Holds the default broker log on username. */ - public static final String USERNAME_DEFAULT = "guest"; - - /** Holds the name of the property to get the broker access password from. */ - public static final String PASSWORD_PROPNAME = "password"; - - /** Holds the default broker log on password. */ - public static final String PASSWORD_DEFAULT = "guest"; - - // ====================== Messaging Topology Properties ========================== - - /** Holds the name of the property to get the bind publisher procuder flag from. */ - public static final String PUBLISHER_PRODUCER_BIND_PROPNAME = "publisherProducerBind"; - - /** Holds the default value of the publisher producer flag. */ - public static final boolean PUBLISHER_PRODUCER_BIND_DEFAULT = true; - - /** Holds the name of the property to get the bind publisher procuder flag from. */ - public static final String PUBLISHER_CONSUMER_BIND_PROPNAME = "publisherConsumerBind"; - - /** Holds the default value of the publisher consumer flag. */ - public static final boolean PUBLISHER_CONSUMER_BIND_DEFAULT = false; - - /** Holds the name of the property to get the bind receiver procuder flag from. */ - public static final String RECEIVER_PRODUCER_BIND_PROPNAME = "receiverProducerBind"; - - /** Holds the default value of the receiver producer flag. */ - public static final boolean RECEIVER_PRODUCER_BIND_DEFAULT = false; - - /** Holds the name of the property to get the bind receiver procuder flag from. */ - public static final String RECEIVER_CONSUMER_BIND_PROPNAME = "receiverConsumerBind"; - - /** Holds the default value of the receiver consumer flag. */ - public static final boolean RECEIVER_CONSUMER_BIND_DEFAULT = true; - - /** Holds the name of the property to get the destination name root from. */ - public static final String SEND_DESTINATION_NAME_ROOT_PROPNAME = "sendDestinationRoot"; - - /** Holds the root of the name of the default destination to send to. */ - public static final String SEND_DESTINATION_NAME_ROOT_DEFAULT = "sendTo"; - - /** Holds the name of the property to get the destination name root from. */ - public static final String RECEIVE_DESTINATION_NAME_ROOT_PROPNAME = "receiveDestinationRoot"; - - /** Holds the root of the name of the default destination to send to. */ - public static final String RECEIVE_DESTINATION_NAME_ROOT_DEFAULT = "receiveFrom"; - - /** Holds the name of the proeprty to get the destination count from. */ - public static final String DESTINATION_COUNT_PROPNAME = "destinationCount"; - - /** Defines the default number of destinations to ping. */ - public static final int DESTINATION_COUNT_DEFAULT = 1; - - /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */ - public static final String PUBSUB_PROPNAME = "pubsub"; - - /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */ - public static final boolean PUBSUB_DEFAULT = false; - - // ====================== JMS Options and Flags ================================= - - /** Holds the name of the property to get the test delivery mode from. */ - public static final String PERSISTENT_MODE_PROPNAME = "persistent"; - - /** Holds the message delivery mode to use for the test. */ - public static final boolean PERSISTENT_MODE_DEFAULT = false; - - /** Holds the name of the property to get the test transactional mode from. */ - public static final String TRANSACTED_PROPNAME = "transacted"; - - /** Holds the transactional mode to use for the test. */ - public static final boolean TRANSACTED_DEFAULT = false; - - /** Holds the name of the property to set the no local flag from. */ - public static final String NO_LOCAL_PROPNAME = "noLocal"; - - /** Defines the default value of the no local flag to use when consuming messages. */ - public static final boolean NO_LOCAL_DEFAULT = false; - - /** Holds the name of the property to get the message acknowledgement mode from. */ - public static final String ACK_MODE_PROPNAME = "ackMode"; - - /** Defines the default message acknowledgement mode. */ - public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; - - /** Holds the name of the property to get the durable subscriptions flag from, when doing pub/sub messaging. */ - public static final String DURABLE_SUBSCRIPTION_PROPNAME = "durableSubscription"; - - /** Defines the default value of the durable subscriptions flag. */ - public static final boolean DURABLE_SUBSCRIPTION_DEFAULT = false; - - // ====================== Qpid Options and Flags ================================ - - /** Holds the name of the property to set the exclusive flag from. */ - public static final String EXCLUSIVE_PROPNAME = "exclusive"; - - /** Defines the default value of the exclusive flag to use when consuming messages. */ - public static final boolean EXCLUSIVE_DEFAULT = false; - - /** Holds the name of the property to set the immediate flag from. */ - public static final String IMMEDIATE_PROPNAME = "immediate"; - - /** Defines the default value of the immediate flag to use when sending messages. */ - public static final boolean IMMEDIATE_DEFAULT = false; - - /** Holds the name of the property to set the mandatory flag from. */ - public static final String MANDATORY_PROPNAME = "mandatory"; - - /** Defines the default value of the mandatory flag to use when sending messages. */ - public static final boolean MANDATORY_DEFAULT = false; - - /** Holds the name of the property to get the durable destinations flag from. */ - public static final String DURABLE_DESTS_PROPNAME = "durableDests"; - - /** Default value for the durable destinations flag. */ - public static final boolean DURABLE_DESTS_DEFAULT = false; - - /** Holds the name of the proeprty to set the prefetch size from. */ - public static final String PREFECTH_PROPNAME = "prefetch"; - - /** Defines the default prefetch size to use when consuming messages. */ - public static final int PREFETCH_DEFAULT = 100; - - // ====================== Common Test Parameters ================================ - - /** Holds the name of the property to get the test message size from. */ - public static final String MESSAGE_SIZE_PROPNAME = "messageSize"; - - /** Used to set up a default message size. */ - public static final int MESSAGE_SIZE_DEAFULT = 0; - - /** Holds the name of the property to get the message rate from. */ - public static final String RATE_PROPNAME = "rate"; - - /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */ - public static final int RATE_DEFAULT = 0; - - /** Holds the name of the proeprty to get the. */ - public static final String SELECTOR_PROPNAME = "selector"; - - /** Holds the default message selector. */ - public static final String SELECTOR_DEFAULT = ""; - - /** Holds the name of the property to get the waiting timeout for response messages. */ - public static final String TIMEOUT_PROPNAME = "timeout"; - - /** Default time to wait before assuming that a ping has timed out. */ - public static final long TIMEOUT_DEFAULT = 30000; - - /** Holds the name of the property to get the commit batch size from. */ - public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize"; - - /** Defines the default number of pings to send in each transaction when running transactionally. */ - public static final int TX_BATCH_SIZE_DEFAULT = 1; - - /** Holds the name of the property to set the maximum amount of pending message data for a producer to hold. */ - public static final String MAX_PENDING_PROPNAME = "maxPending"; - - /** Defines the default maximum quantity of pending message data to allow producers to hold. */ - public static final int MAX_PENDING_DEFAULT = 0; - - /** Holds the name of the property to get the verbose mode proeprty from. */ - public static final String VERBOSE_PROPNAME = "verbose"; - - /** Holds the default verbose mode. */ - public static final boolean VERBOSE_DEFAULT = false; - - /** Holds the default configuration properties. */ - public static ParsedProperties defaults = new ParsedProperties(); - - static - { - defaults.setPropertyIfNull(INITIAL_CONTEXT_FACTORY_PROPNAME, INITIAL_CONTEXT_FACTORY_DEFAULT); - defaults.setPropertyIfNull(CONNECTION_PROPNAME, CONNECTION_DEFAULT); - defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); - defaults.setPropertyIfNull(PUBLISHER_PRODUCER_BIND_PROPNAME, PUBLISHER_PRODUCER_BIND_DEFAULT); - defaults.setPropertyIfNull(PUBLISHER_CONSUMER_BIND_PROPNAME, PUBLISHER_CONSUMER_BIND_DEFAULT); - defaults.setPropertyIfNull(RECEIVER_PRODUCER_BIND_PROPNAME, RECEIVER_PRODUCER_BIND_DEFAULT); - defaults.setPropertyIfNull(RECEIVER_CONSUMER_BIND_PROPNAME, RECEIVER_CONSUMER_BIND_DEFAULT); - defaults.setPropertyIfNull(SEND_DESTINATION_NAME_ROOT_PROPNAME, SEND_DESTINATION_NAME_ROOT_DEFAULT); - defaults.setPropertyIfNull(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME, RECEIVE_DESTINATION_NAME_ROOT_DEFAULT); - defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); - defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); - defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); - defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); - defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); - defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); - defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); - defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); - defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); - defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); - defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); - defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); - defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); - defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); - defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); - defaults.setPropertyIfNull(DURABLE_SUBSCRIPTION_PROPNAME, DURABLE_SUBSCRIPTION_DEFAULT); - defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); - defaults.setPropertyIfNull(PREFECTH_PROPNAME, PREFETCH_DEFAULT); - defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_DEFAULT); - defaults.setPropertyIfNull(EXCLUSIVE_PROPNAME, EXCLUSIVE_DEFAULT); - defaults.setPropertyIfNull(IMMEDIATE_PROPNAME, IMMEDIATE_DEFAULT); - defaults.setPropertyIfNull(MANDATORY_PROPNAME, MANDATORY_DEFAULT); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import org.apache.qpid.jms.Session; +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +/** + * MessagingTestConfigProperties defines a set of property names and default values for specifying a messaging topology, + * and test parameters for running a messaging test over that topology. A Properties object holding some of these + * properties, superimposed onto the defaults, is used to establish test topologies and control test behaviour. + * + *

          A complete list of the parameters, default values and comments on their usage is provided here: + * + *

          + *
          Parameters
          Parameter Default Comments + *
          messageSize 0 Message size in bytes. Not including any headers. + *
          destinationName ping The root name to use to generate destination names to ping. + *
          persistent false Determines whether peristent delivery is used. + *
          transacted false Determines whether messages are sent/received in transactions. + *
          broker tcp://localhost:5672 Determines the broker to connect to. + *
          virtualHost test Determines the virtual host to send all ping over. + *
          rate 0 The maximum rate (in hertz) to send messages at. 0 means no limit. + *
          verbose false The verbose flag for debugging. Prints to console on every message. + *
          pubsub false Whether to ping topics or queues. Uses p2p by default. + *
          username guest The username to access the broker with. + *
          password guest The password to access the broker with. + *
          selector null Not used. Defines a message selector to filter pings with. + *
          destinationCount 1 The number of receivers listening to the pings. + *
          timeout 30000 In milliseconds. The timeout to stop waiting for replies. + *
          commitBatchSize 1 The number of messages per transaction in transactional mode. + *
          uniqueDests true Whether each receiver only listens to one ping destination or all. + *
          durableDests false Whether or not durable destinations are used. + *
          ackMode AUTO_ACK The message acknowledgement mode. Possible values are: + * 0 - SESSION_TRANSACTED + * 1 - AUTO_ACKNOWLEDGE + * 2 - CLIENT_ACKNOWLEDGE + * 3 - DUPS_OK_ACKNOWLEDGE + * 257 - NO_ACKNOWLEDGE + * 258 - PRE_ACKNOWLEDGE + *
          maxPending 0 The maximum size in bytes, of messages sent but not yet received. + * Limits the volume of messages currently buffered on the client + * or broker. Can help scale test clients by limiting amount of buffered + * data to avoid out of memory errors. + *
          + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Provide the names and defaults of all test parameters. + *
          + */ +public class MessagingTestConfigProperties +{ + // ====================== Connection Properties ================================== + + /** Holds the name of the default connection configuration. */ + public static final String CONNECTION_NAME = "broker"; + + /** Holds the name of the property to get the initial context factory name from. */ + public static final String INITIAL_CONTEXT_FACTORY_PROPNAME = "java.naming.factory.initial"; + + /** Defines the class to use as the initial context factory by default. */ + public static final String INITIAL_CONTEXT_FACTORY_DEFAULT = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"; + + /** Holds the name of the default connection factory configuration property. */ + public static final String CONNECTION_PROPNAME = "connectionfactory.broker"; + + /** Defeins the default connection configuration. */ + public static final String CONNECTION_DEFAULT = "amqp://guest:guest@clientid/?brokerlist='vm://:1'"; + + /** Holds the name of the property to get the test broker url from. */ + public static final String BROKER_PROPNAME = "qpid.test.broker"; + + /** Holds the default broker url for the test. */ + public static final String BROKER_DEFAULT = "vm://:1"; + + /** Holds the name of the property to get the test broker virtual path. */ + public static final String VIRTUAL_HOST_PROPNAME = "virtualHost"; + + /** Holds the default virtual path for the test. */ + public static final String VIRTUAL_HOST_DEFAULT = ""; + + /** Holds the name of the property to get the broker access username from. */ + public static final String USERNAME_PROPNAME = "username"; + + /** Holds the default broker log on username. */ + public static final String USERNAME_DEFAULT = "guest"; + + /** Holds the name of the property to get the broker access password from. */ + public static final String PASSWORD_PROPNAME = "password"; + + /** Holds the default broker log on password. */ + public static final String PASSWORD_DEFAULT = "guest"; + + // ====================== Messaging Topology Properties ========================== + + /** Holds the name of the property to get the bind publisher procuder flag from. */ + public static final String PUBLISHER_PRODUCER_BIND_PROPNAME = "publisherProducerBind"; + + /** Holds the default value of the publisher producer flag. */ + public static final boolean PUBLISHER_PRODUCER_BIND_DEFAULT = true; + + /** Holds the name of the property to get the bind publisher procuder flag from. */ + public static final String PUBLISHER_CONSUMER_BIND_PROPNAME = "publisherConsumerBind"; + + /** Holds the default value of the publisher consumer flag. */ + public static final boolean PUBLISHER_CONSUMER_BIND_DEFAULT = false; + + /** Holds the name of the property to get the bind receiver procuder flag from. */ + public static final String RECEIVER_PRODUCER_BIND_PROPNAME = "receiverProducerBind"; + + /** Holds the default value of the receiver producer flag. */ + public static final boolean RECEIVER_PRODUCER_BIND_DEFAULT = false; + + /** Holds the name of the property to get the bind receiver procuder flag from. */ + public static final String RECEIVER_CONSUMER_BIND_PROPNAME = "receiverConsumerBind"; + + /** Holds the default value of the receiver consumer flag. */ + public static final boolean RECEIVER_CONSUMER_BIND_DEFAULT = true; + + /** Holds the name of the property to get the destination name root from. */ + public static final String SEND_DESTINATION_NAME_ROOT_PROPNAME = "sendDestinationRoot"; + + /** Holds the root of the name of the default destination to send to. */ + public static final String SEND_DESTINATION_NAME_ROOT_DEFAULT = "sendTo"; + + /** Holds the name of the property to get the destination name root from. */ + public static final String RECEIVE_DESTINATION_NAME_ROOT_PROPNAME = "receiveDestinationRoot"; + + /** Holds the root of the name of the default destination to send to. */ + public static final String RECEIVE_DESTINATION_NAME_ROOT_DEFAULT = "receiveFrom"; + + /** Holds the name of the proeprty to get the destination count from. */ + public static final String DESTINATION_COUNT_PROPNAME = "destinationCount"; + + /** Defines the default number of destinations to ping. */ + public static final int DESTINATION_COUNT_DEFAULT = 1; + + /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */ + public static final String PUBSUB_PROPNAME = "pubsub"; + + /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */ + public static final boolean PUBSUB_DEFAULT = false; + + // ====================== JMS Options and Flags ================================= + + /** Holds the name of the property to get the test delivery mode from. */ + public static final String PERSISTENT_MODE_PROPNAME = "persistent"; + + /** Holds the message delivery mode to use for the test. */ + public static final boolean PERSISTENT_MODE_DEFAULT = false; + + /** Holds the name of the property to get the test transactional mode from. */ + public static final String TRANSACTED_PROPNAME = "transacted"; + + /** Holds the transactional mode to use for the test. */ + public static final boolean TRANSACTED_DEFAULT = false; + + /** Holds the name of the property to set the no local flag from. */ + public static final String NO_LOCAL_PROPNAME = "noLocal"; + + /** Defines the default value of the no local flag to use when consuming messages. */ + public static final boolean NO_LOCAL_DEFAULT = false; + + /** Holds the name of the property to get the message acknowledgement mode from. */ + public static final String ACK_MODE_PROPNAME = "ackMode"; + + /** Defines the default message acknowledgement mode. */ + public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE; + + /** Holds the name of the property to get the durable subscriptions flag from, when doing pub/sub messaging. */ + public static final String DURABLE_SUBSCRIPTION_PROPNAME = "durableSubscription"; + + /** Defines the default value of the durable subscriptions flag. */ + public static final boolean DURABLE_SUBSCRIPTION_DEFAULT = false; + + // ====================== Qpid Options and Flags ================================ + + /** Holds the name of the property to set the exclusive flag from. */ + public static final String EXCLUSIVE_PROPNAME = "exclusive"; + + /** Defines the default value of the exclusive flag to use when consuming messages. */ + public static final boolean EXCLUSIVE_DEFAULT = false; + + /** Holds the name of the property to set the immediate flag from. */ + public static final String IMMEDIATE_PROPNAME = "immediate"; + + /** Defines the default value of the immediate flag to use when sending messages. */ + public static final boolean IMMEDIATE_DEFAULT = false; + + /** Holds the name of the property to set the mandatory flag from. */ + public static final String MANDATORY_PROPNAME = "mandatory"; + + /** Defines the default value of the mandatory flag to use when sending messages. */ + public static final boolean MANDATORY_DEFAULT = false; + + /** Holds the name of the property to get the durable destinations flag from. */ + public static final String DURABLE_DESTS_PROPNAME = "durableDests"; + + /** Default value for the durable destinations flag. */ + public static final boolean DURABLE_DESTS_DEFAULT = false; + + /** Holds the name of the proeprty to set the prefetch size from. */ + public static final String PREFECTH_PROPNAME = "prefetch"; + + /** Defines the default prefetch size to use when consuming messages. */ + public static final int PREFETCH_DEFAULT = 100; + + // ====================== Common Test Parameters ================================ + + /** Holds the name of the property to get the test message size from. */ + public static final String MESSAGE_SIZE_PROPNAME = "messageSize"; + + /** Used to set up a default message size. */ + public static final int MESSAGE_SIZE_DEAFULT = 0; + + /** Holds the name of the property to get the message rate from. */ + public static final String RATE_PROPNAME = "rate"; + + /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */ + public static final int RATE_DEFAULT = 0; + + /** Holds the name of the proeprty to get the. */ + public static final String SELECTOR_PROPNAME = "selector"; + + /** Holds the default message selector. */ + public static final String SELECTOR_DEFAULT = ""; + + /** Holds the name of the property to get the waiting timeout for response messages. */ + public static final String TIMEOUT_PROPNAME = "timeout"; + + /** Default time to wait before assuming that a ping has timed out. */ + public static final long TIMEOUT_DEFAULT = 30000; + + /** Holds the name of the property to get the commit batch size from. */ + public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize"; + + /** Defines the default number of pings to send in each transaction when running transactionally. */ + public static final int TX_BATCH_SIZE_DEFAULT = 1; + + /** Holds the name of the property to set the maximum amount of pending message data for a producer to hold. */ + public static final String MAX_PENDING_PROPNAME = "maxPending"; + + /** Defines the default maximum quantity of pending message data to allow producers to hold. */ + public static final int MAX_PENDING_DEFAULT = 0; + + /** Holds the name of the property to get the verbose mode proeprty from. */ + public static final String VERBOSE_PROPNAME = "verbose"; + + /** Holds the default verbose mode. */ + public static final boolean VERBOSE_DEFAULT = false; + + /** Holds the default configuration properties. */ + public static ParsedProperties defaults = new ParsedProperties(); + + static + { + defaults.setPropertyIfNull(INITIAL_CONTEXT_FACTORY_PROPNAME, INITIAL_CONTEXT_FACTORY_DEFAULT); + defaults.setPropertyIfNull(CONNECTION_PROPNAME, CONNECTION_DEFAULT); + defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); + defaults.setPropertyIfNull(PUBLISHER_PRODUCER_BIND_PROPNAME, PUBLISHER_PRODUCER_BIND_DEFAULT); + defaults.setPropertyIfNull(PUBLISHER_CONSUMER_BIND_PROPNAME, PUBLISHER_CONSUMER_BIND_DEFAULT); + defaults.setPropertyIfNull(RECEIVER_PRODUCER_BIND_PROPNAME, RECEIVER_PRODUCER_BIND_DEFAULT); + defaults.setPropertyIfNull(RECEIVER_CONSUMER_BIND_PROPNAME, RECEIVER_CONSUMER_BIND_DEFAULT); + defaults.setPropertyIfNull(SEND_DESTINATION_NAME_ROOT_PROPNAME, SEND_DESTINATION_NAME_ROOT_DEFAULT); + defaults.setPropertyIfNull(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME, RECEIVE_DESTINATION_NAME_ROOT_DEFAULT); + defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); + defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); + defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); + defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); + defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); + defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); + defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); + defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); + defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); + defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); + defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); + defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); + defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); + defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); + defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); + defaults.setPropertyIfNull(DURABLE_SUBSCRIPTION_PROPNAME, DURABLE_SUBSCRIPTION_DEFAULT); + defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); + defaults.setPropertyIfNull(PREFECTH_PROPNAME, PREFETCH_DEFAULT); + defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_DEFAULT); + defaults.setPropertyIfNull(EXCLUSIVE_PROPNAME, EXCLUSIVE_DEFAULT); + defaults.setPropertyIfNull(IMMEDIATE_PROPNAME, IMMEDIATE_DEFAULT); + defaults.setPropertyIfNull(MANDATORY_PROPNAME, MANDATORY_DEFAULT); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java b/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java index a6a2bbb80f..bae3f844d7 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java +++ b/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java @@ -1,312 +1,312 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.qpid.server.exchange; - -import junit.framework.TestCase; -import org.apache.log4j.Logger; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.util.NullApplicationRegistry; -import org.apache.qpid.client.*; -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.url.AMQBindingURL; -import org.apache.qpid.url.BindingURL; -import org.apache.qpid.exchange.ExchangeDefaults; -import org.apache.qpid.framing.FieldTable; - -import javax.jms.*; -import java.util.List; -import java.util.Collections; -import java.util.ArrayList; - -public class ReturnUnroutableMandatoryMessageTest extends TestCase implements ExceptionListener -{ - private static final Logger _logger = Logger.getLogger(ReturnUnroutableMandatoryMessageTest.class); - - private final List _bouncedMessageList = Collections.synchronizedList(new ArrayList()); - private static final String VIRTUALHOST = "test"; - private static final String BROKER = "vm://:1"; - - static - { - String workdir = System.getProperty("QPID_WORK"); - if (workdir == null || workdir.equals("")) - { - String tempdir = System.getProperty("java.io.tmpdir"); - System.out.println("QPID_WORK not set using tmp directory: " + tempdir); - System.setProperty("QPID_WORK", tempdir); - } -// DOMConfigurator.configure("../broker/etc/log4j.xml"); - } - - protected void setUp() throws Exception - { - super.setUp(); - TransportConnection.createVMBroker(1); - ApplicationRegistry.initialise(new NullApplicationRegistry(), 1); - } - - protected void tearDown() throws Exception - { - super.tearDown(); - TransportConnection.killAllVMBrokers(); - } - - /** - * Tests that mandatory message which are not routable are returned to the producer - * - * @throws Exception - */ - public void testReturnUnroutableMandatoryMessage_HEADERS() throws Exception - { - _bouncedMessageList.clear(); - Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); - - - AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); - - AMQHeadersExchange queue = new AMQHeadersExchange(new AMQBindingURL(ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'")); - FieldTable ft = new FieldTable(); - ft.setString("F1000", "1"); - MessageConsumer consumer = consumerSession.createConsumer(queue, AMQSession.DEFAULT_PREFETCH_LOW_MARK, AMQSession.DEFAULT_PREFETCH_HIGH_MARK, false, false, (String) null, ft); - - //force synch to ensure the consumer has resulted in a bound queue - //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); - // This is the default now - - Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); - - con2.setExceptionListener(this); - AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); - - // Need to start the "producer" connection in order to receive bounced messages - _logger.info("Starting producer connection"); - con2.start(); - - - MessageProducer nonMandatoryProducer = producerSession.createProducer(queue, false, false); - MessageProducer mandatoryProducer = producerSession.createProducer(queue); - - // First test - should neither be bounced nor routed - _logger.info("Sending non-routable non-mandatory message"); - TextMessage msg1 = producerSession.createTextMessage("msg1"); - nonMandatoryProducer.send(msg1); - - // Second test - should be bounced - _logger.info("Sending non-routable mandatory message"); - TextMessage msg2 = producerSession.createTextMessage("msg2"); - mandatoryProducer.send(msg2); - - // Third test - should be routed - _logger.info("Sending routable message"); - TextMessage msg3 = producerSession.createTextMessage("msg3"); - msg3.setStringProperty("F1000", "1"); - mandatoryProducer.send(msg3); - - - _logger.info("Starting consumer connection"); - con.start(); - TextMessage tm = (TextMessage) consumer.receive(1000L); - - assertTrue("No message routed to receiver", tm != null); - assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg3".equals(tm.getText())); - - try - { - Thread.sleep(1000L); - } - catch (InterruptedException e) - { - ; - } - - assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1); - Message m = _bouncedMessageList.get(0); - assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); - - - con.close(); - con2.close(); - - - } - - public void testReturnUnroutableMandatoryMessage_QUEUE() throws Exception - { - _bouncedMessageList.clear(); - Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); - - - AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); - - AMQQueue valid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE"); - AMQQueue invalid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE_INVALID"); - MessageConsumer consumer = consumerSession.createConsumer(valid_queue); - - //force synch to ensure the consumer has resulted in a bound queue - //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); - // This is the default now - - Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); - - con2.setExceptionListener(this); - AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); - - // Need to start the "producer" connection in order to receive bounced messages - _logger.info("Starting producer connection"); - con2.start(); - - - MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_queue, false, false); - MessageProducer mandatoryProducer = producerSession.createProducer(invalid_queue); - - // First test - should be routed - _logger.info("Sending non-mandatory message"); - TextMessage msg1 = producerSession.createTextMessage("msg1"); - nonMandatoryProducer.send(msg1); - - // Second test - should be bounced - _logger.info("Sending non-routable mandatory message"); - TextMessage msg2 = producerSession.createTextMessage("msg2"); - mandatoryProducer.send(msg2); - - - _logger.info("Starting consumer connection"); - con.start(); - TextMessage tm = (TextMessage) consumer.receive(1000L); - - assertTrue("No message routed to receiver", tm != null); - assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText())); - - try - { - Thread.sleep(1000L); - } - catch (InterruptedException e) - { - ; - } - - assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1); - Message m = _bouncedMessageList.get(0); - assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); - - - con.close(); - con2.close(); - } - - - public void testReturnUnroutableMandatoryMessage_TOPIC() throws Exception - { - _bouncedMessageList.clear(); - Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); - - - AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); - - AMQTopic valid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC"); - AMQTopic invalid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC.invalid"); - MessageConsumer consumer = consumerSession.createConsumer(valid_topic); - - //force synch to ensure the consumer has resulted in a bound queue - //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); - // This is the default now - - Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); - - con2.setExceptionListener(this); - AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); - - // Need to start the "producer" connection in order to receive bounced messages - _logger.info("Starting producer connection"); - con2.start(); - - - MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_topic, false, false); - MessageProducer mandatoryProducer = producerSession.createProducer(invalid_topic); - - // First test - should be routed - _logger.info("Sending non-mandatory message"); - TextMessage msg1 = producerSession.createTextMessage("msg1"); - nonMandatoryProducer.send(msg1); - - // Second test - should be bounced - _logger.info("Sending non-routable mandatory message"); - TextMessage msg2 = producerSession.createTextMessage("msg2"); - mandatoryProducer.send(msg2); - - - _logger.info("Starting consumer connection"); - con.start(); - TextMessage tm = (TextMessage) consumer.receive(1000L); - - assertTrue("No message routed to receiver", tm != null); - assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText())); - - try - { - Thread.sleep(1000L); - } - catch (InterruptedException e) - { - ; - } - - assertEquals("Wrong number of messages bounced: ", 1, _bouncedMessageList.size()); - Message m = _bouncedMessageList.get(0); - assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); - - - con.close(); - con2.close(); - } - - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(ReturnUnroutableMandatoryMessageTest.class); - } - - public void onException(JMSException jmsException) - { - - Exception linkedException = null; - try - { - linkedException = jmsException.getLinkedException(); - } catch (Exception e) - { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } - if (linkedException instanceof AMQNoRouteException) - { - AMQNoRouteException noRoute = (AMQNoRouteException) linkedException; - Message bounced = (Message) noRoute.getUndeliveredMessage(); - _bouncedMessageList.add(bounced); - _logger.info("Caught expected NoRouteException"); - } - else - { - _logger.warn("Caught exception on producer: ", jmsException); - } - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.exchange; + +import junit.framework.TestCase; +import org.apache.log4j.Logger; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.NullApplicationRegistry; +import org.apache.qpid.client.*; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.FieldTable; + +import javax.jms.*; +import java.util.List; +import java.util.Collections; +import java.util.ArrayList; + +public class ReturnUnroutableMandatoryMessageTest extends TestCase implements ExceptionListener +{ + private static final Logger _logger = Logger.getLogger(ReturnUnroutableMandatoryMessageTest.class); + + private final List _bouncedMessageList = Collections.synchronizedList(new ArrayList()); + private static final String VIRTUALHOST = "test"; + private static final String BROKER = "vm://:1"; + + static + { + String workdir = System.getProperty("QPID_WORK"); + if (workdir == null || workdir.equals("")) + { + String tempdir = System.getProperty("java.io.tmpdir"); + System.out.println("QPID_WORK not set using tmp directory: " + tempdir); + System.setProperty("QPID_WORK", tempdir); + } +// DOMConfigurator.configure("../broker/etc/log4j.xml"); + } + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + ApplicationRegistry.initialise(new NullApplicationRegistry(), 1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + /** + * Tests that mandatory message which are not routable are returned to the producer + * + * @throws Exception + */ + public void testReturnUnroutableMandatoryMessage_HEADERS() throws Exception + { + _bouncedMessageList.clear(); + Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); + + + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + AMQHeadersExchange queue = new AMQHeadersExchange(new AMQBindingURL(ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'")); + FieldTable ft = new FieldTable(); + ft.setString("F1000", "1"); + MessageConsumer consumer = consumerSession.createConsumer(queue, AMQSession.DEFAULT_PREFETCH_LOW_MARK, AMQSession.DEFAULT_PREFETCH_HIGH_MARK, false, false, (String) null, ft); + + //force synch to ensure the consumer has resulted in a bound queue + //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + // This is the default now + + Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); + + con2.setExceptionListener(this); + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Need to start the "producer" connection in order to receive bounced messages + _logger.info("Starting producer connection"); + con2.start(); + + + MessageProducer nonMandatoryProducer = producerSession.createProducer(queue, false, false); + MessageProducer mandatoryProducer = producerSession.createProducer(queue); + + // First test - should neither be bounced nor routed + _logger.info("Sending non-routable non-mandatory message"); + TextMessage msg1 = producerSession.createTextMessage("msg1"); + nonMandatoryProducer.send(msg1); + + // Second test - should be bounced + _logger.info("Sending non-routable mandatory message"); + TextMessage msg2 = producerSession.createTextMessage("msg2"); + mandatoryProducer.send(msg2); + + // Third test - should be routed + _logger.info("Sending routable message"); + TextMessage msg3 = producerSession.createTextMessage("msg3"); + msg3.setStringProperty("F1000", "1"); + mandatoryProducer.send(msg3); + + + _logger.info("Starting consumer connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(1000L); + + assertTrue("No message routed to receiver", tm != null); + assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg3".equals(tm.getText())); + + try + { + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + ; + } + + assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1); + Message m = _bouncedMessageList.get(0); + assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); + + + con.close(); + con2.close(); + + + } + + public void testReturnUnroutableMandatoryMessage_QUEUE() throws Exception + { + _bouncedMessageList.clear(); + Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); + + + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + AMQQueue valid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE"); + AMQQueue invalid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE_INVALID"); + MessageConsumer consumer = consumerSession.createConsumer(valid_queue); + + //force synch to ensure the consumer has resulted in a bound queue + //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + // This is the default now + + Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); + + con2.setExceptionListener(this); + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Need to start the "producer" connection in order to receive bounced messages + _logger.info("Starting producer connection"); + con2.start(); + + + MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_queue, false, false); + MessageProducer mandatoryProducer = producerSession.createProducer(invalid_queue); + + // First test - should be routed + _logger.info("Sending non-mandatory message"); + TextMessage msg1 = producerSession.createTextMessage("msg1"); + nonMandatoryProducer.send(msg1); + + // Second test - should be bounced + _logger.info("Sending non-routable mandatory message"); + TextMessage msg2 = producerSession.createTextMessage("msg2"); + mandatoryProducer.send(msg2); + + + _logger.info("Starting consumer connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(1000L); + + assertTrue("No message routed to receiver", tm != null); + assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText())); + + try + { + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + ; + } + + assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1); + Message m = _bouncedMessageList.get(0); + assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); + + + con.close(); + con2.close(); + } + + + public void testReturnUnroutableMandatoryMessage_TOPIC() throws Exception + { + _bouncedMessageList.clear(); + Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST); + + + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + AMQTopic valid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC"); + AMQTopic invalid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC.invalid"); + MessageConsumer consumer = consumerSession.createConsumer(valid_topic); + + //force synch to ensure the consumer has resulted in a bound queue + //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + // This is the default now + + Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST); + + con2.setExceptionListener(this); + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Need to start the "producer" connection in order to receive bounced messages + _logger.info("Starting producer connection"); + con2.start(); + + + MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_topic, false, false); + MessageProducer mandatoryProducer = producerSession.createProducer(invalid_topic); + + // First test - should be routed + _logger.info("Sending non-mandatory message"); + TextMessage msg1 = producerSession.createTextMessage("msg1"); + nonMandatoryProducer.send(msg1); + + // Second test - should be bounced + _logger.info("Sending non-routable mandatory message"); + TextMessage msg2 = producerSession.createTextMessage("msg2"); + mandatoryProducer.send(msg2); + + + _logger.info("Starting consumer connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(1000L); + + assertTrue("No message routed to receiver", tm != null); + assertTrue("Wrong message routed to receiver: " + tm.getText(), "msg1".equals(tm.getText())); + + try + { + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + ; + } + + assertEquals("Wrong number of messages bounced: ", 1, _bouncedMessageList.size()); + Message m = _bouncedMessageList.get(0); + assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2")); + + + con.close(); + con2.close(); + } + + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ReturnUnroutableMandatoryMessageTest.class); + } + + public void onException(JMSException jmsException) + { + + Exception linkedException = null; + try + { + linkedException = jmsException.getLinkedException(); + } catch (Exception e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + if (linkedException instanceof AMQNoRouteException) + { + AMQNoRouteException noRoute = (AMQNoRouteException) linkedException; + Message bounced = (Message) noRoute.getUndeliveredMessage(); + _bouncedMessageList.add(bounced); + _logger.info("Caught expected NoRouteException"); + } + else + { + _logger.warn("Caught exception on producer: ", jmsException); + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java b/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java index 706d99ffe2..13465741bd 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java @@ -1,54 +1,54 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -/** - * An AMQPPublisher represents the status of the publishing side of a test circuit that exposes AMQP specific features. - * Its provides additional assertions not available through the plain JMS {@link Publisher} interface. - * - *

          - *
          CRC Card
          Responsibilities - *
          Provide assertion that the publishers received a no consumers error code on every message. - *
          Provide assertion that the publishers received a no route error code on every message. - *
          - */ -public interface AMQPPublisher extends Publisher -{ - /** - * Provides an assertion that the publisher got a no consumers exception on every message. - * - * @param testProps The test configuration properties. - * - * @return An assertion that the publisher got a no consumers exception on every message. - */ - Assertion noConsumersAssertion(ParsedProperties testProps); - - /** - * Provides an assertion that the publisher got a no rout exception on every message. - * - * @param testProps The test configuration properties. - * - * @return An assertion that the publisher got a no rout exception on every message. - */ - Assertion noRouteAssertion(ParsedProperties testProps); -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +/** + * An AMQPPublisher represents the status of the publishing side of a test circuit that exposes AMQP specific features. + * Its provides additional assertions not available through the plain JMS {@link Publisher} interface. + * + *

          + *
          CRC Card
          Responsibilities + *
          Provide assertion that the publishers received a no consumers error code on every message. + *
          Provide assertion that the publishers received a no route error code on every message. + *
          + */ +public interface AMQPPublisher extends Publisher +{ + /** + * Provides an assertion that the publisher got a no consumers exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no consumers exception on every message. + */ + Assertion noConsumersAssertion(ParsedProperties testProps); + + /** + * Provides an assertion that the publisher got a no rout exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no rout exception on every message. + */ + Assertion noRouteAssertion(ParsedProperties testProps); +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java b/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java index e8b7da2537..41614f92fc 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java @@ -1,70 +1,70 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -/** - * BrokerLifecycleAware is an awareness interface implemented by test cases that can run control the life-cycle of - * the brokers on which they run. Its purpose is to expose additional instrumentation of brokers during testing, that - * enables tests to use an automated failure mechanism to simulate broker failures, and to re-start failed brokers. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Indicate whether or not a test case is using an in-vm broker. - *
          Track which in-vm broker is currently in use. - *
          Accept setting of a failure mechanism. {@link CauseFailure}. - *
          - * - * @todo Need to think about how to present the brokers through this interface. Thinking numbering the available - * brokers from 1 will do. Then can kill 1 and assume failing onto 2. Restart 1 and kill 2 and fail back onto - * 1 again? - */ -public interface BrokerLifecycleAware -{ - public void setInVmBrokers(); - - /** - * Indicates whether or not a test case is using in-vm brokers. - * - * @return true if the test is using in-vm brokers, false otherwise. - */ - public boolean usingInVmBroker(); - - /** - * Sets the currently live in-vm broker. - * - * @param i The currently live in-vm broker. - */ - public void setLiveBroker(int i); - - /** - * Reports the currently live in-vm broker. - * - * @return The currently live in-vm broker. - */ - public int getLiveBroker(); - - /** - * Accepts a failure mechanism. - * - * @param failureMechanism The failure mechanism. - */ - public void setFailureMechanism(CauseFailure failureMechanism); -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +/** + * BrokerLifecycleAware is an awareness interface implemented by test cases that can run control the life-cycle of + * the brokers on which they run. Its purpose is to expose additional instrumentation of brokers during testing, that + * enables tests to use an automated failure mechanism to simulate broker failures, and to re-start failed brokers. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Indicate whether or not a test case is using an in-vm broker. + *
          Track which in-vm broker is currently in use. + *
          Accept setting of a failure mechanism. {@link CauseFailure}. + *
          + * + * @todo Need to think about how to present the brokers through this interface. Thinking numbering the available + * brokers from 1 will do. Then can kill 1 and assume failing onto 2. Restart 1 and kill 2 and fail back onto + * 1 again? + */ +public interface BrokerLifecycleAware +{ + public void setInVmBrokers(); + + /** + * Indicates whether or not a test case is using in-vm brokers. + * + * @return true if the test is using in-vm brokers, false otherwise. + */ + public boolean usingInVmBroker(); + + /** + * Sets the currently live in-vm broker. + * + * @param i The currently live in-vm broker. + */ + public void setLiveBroker(int i); + + /** + * Reports the currently live in-vm broker. + * + * @return The currently live in-vm broker. + */ + public int getLiveBroker(); + + /** + * Accepts a failure mechanism. + * + * @param failureMechanism The failure mechanism. + */ + public void setFailureMechanism(CauseFailure failureMechanism); +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java b/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java index 8a5a9560a0..9bdd5a72c5 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java @@ -1,42 +1,42 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -/** - * CauseFailure provides a method to cause a failure in a messaging broker, usually used in conjunction with fail-over - * or other failure mode testing. In some cases failures may be automated, for example by shutting down an in-vm broker, - * or by sending a special control signal to a broker over a network connection. In other cases, it may be preferable - * to ask a user interactively to cause a failure scenario, in which case an implementation may display a prompt or - * dialog box asking for notification once the failure has been caused. The purpose of this interface is to abstract - * the exact cause and nature of a failure out of failure test cases. - * - *

          - *
          CRC Card
          Responsibilities - *
          Cause messaging broker failure. - *
          - */ -public interface CauseFailure -{ - /** - * Causes the active message broker to fail. - */ - void causeFailure(); -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +/** + * CauseFailure provides a method to cause a failure in a messaging broker, usually used in conjunction with fail-over + * or other failure mode testing. In some cases failures may be automated, for example by shutting down an in-vm broker, + * or by sending a special control signal to a broker over a network connection. In other cases, it may be preferable + * to ask a user interactively to cause a failure scenario, in which case an implementation may display a prompt or + * dialog box asking for notification once the failure has been caused. The purpose of this interface is to abstract + * the exact cause and nature of a failure out of failure test cases. + * + *

          + *
          CRC Card
          Responsibilities + *
          Cause messaging broker failure. + *
          + */ +public interface CauseFailure +{ + /** + * Causes the active message broker to fail. + */ + void causeFailure(); +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java b/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java index 6b96ade674..889df4ad07 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java @@ -1,65 +1,65 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -import org.apache.qpid.test.framework.CauseFailure; - -import java.io.IOException; - -/** - * Causes a message broker failure by interactively prompting the user to cause it. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Cause messaging broker failure. - *
          - */ -public class CauseFailureUserPrompt implements CauseFailure -{ - /** - * Causes the active message broker to fail. - */ - public void causeFailure() - { - waitForUser("Cause a broker failure now, then press Return."); - } - - /** - * Outputs a prompt to the console and waits for the user to press return. - * - * @param prompt The prompt to display on the console. - */ - private void waitForUser(String prompt) - { - System.out.println(prompt); - - try - { - System.in.read(); - } - catch (IOException e) - { - // Ignored. - } - - System.out.println("Continuing."); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.qpid.test.framework.CauseFailure; + +import java.io.IOException; + +/** + * Causes a message broker failure by interactively prompting the user to cause it. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Cause messaging broker failure. + *
          + */ +public class CauseFailureUserPrompt implements CauseFailure +{ + /** + * Causes the active message broker to fail. + */ + public void causeFailure() + { + waitForUser("Cause a broker failure now, then press Return."); + } + + /** + * Outputs a prompt to the console and waits for the user to press return. + * + * @param prompt The prompt to display on the console. + */ + private void waitForUser(String prompt) + { + System.out.println(prompt); + + try + { + System.in.read(); + } + catch (IOException e) + { + // Ignored. + } + + System.out.println("Continuing."); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java index e7268db8eb..9a4668e86f 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java @@ -1,48 +1,48 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -/** - * A FrameworkTestContext provides context information to test code about the current test case being run; its name, its - * parameters. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Provide the name of the current test case. - *
          Provide the test parameters. - *
          - */ -public interface FrameworkTestContext -{ - /** - * Reports the current test case name. - * - * @return The current test case name. - */ - TestCaseVector getTestCaseVector(); - - /** - * Reports the current test case parameters. - * - * @return The current test case parameters. - */ - MessagingTestConfigProperties getTestParameters(); -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +/** + * A FrameworkTestContext provides context information to test code about the current test case being run; its name, its + * parameters. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Provide the name of the current test case. + *
          Provide the test parameters. + *
          + */ +public interface FrameworkTestContext +{ + /** + * Reports the current test case name. + * + * @return The current test case name. + */ + TestCaseVector getTestCaseVector(); + + /** + * Reports the current test case parameters. + * + * @return The current test case parameters. + */ + MessagingTestConfigProperties getTestParameters(); +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java index d1fcad9cc0..7fbef06265 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java @@ -1,168 +1,168 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.test.framework.localcircuit.LocalAMQPPublisherImpl; -import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; - -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.*; - -/** - * LocalAMQPCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted - * on the same JVM, allowing AMQP/Qpid specific options to be applied to the circuit. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Provide a standard test procedure over a test circuit. - *
          Construct test circuits appropriate to a tests context. - *
          Construct test circuits the support AMQP specific options. - *
          - */ -public class LocalAMQPCircuitFactory extends LocalCircuitFactory -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(LocalAMQPCircuitFactory.class); - - /** - * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. - * - * @param connection The connection to build the circuit end on. - * @param testProps The test parameters to configure the circuit end construction. - * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. - * - * @return A circuit end suitable for the publishing side of a test circuit. - * - * @throws javax.jms.JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. - */ - public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) - throws JMSException - { - log.debug( - "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " - + uniqueId + "): called"); - - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - - Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); - - Destination destination = - props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) - : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); - - MessageProducer producer = - props.getPublisherProducerBind() - ? ((props.getImmediate() | props.getMandatory()) - ? ((AMQSession) session).createProducer(destination, props.getMandatory(), props.getImmediate()) - : session.createProducer(destination)) : null; - - MessageConsumer consumer = - props.getPublisherConsumerBind() - ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; - - MessageMonitor messageMonitor = new MessageMonitor(); - - if (consumer != null) - { - consumer.setMessageListener(messageMonitor); - } - - ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); - connection.setExceptionListener(exceptionMonitor); - - if (!props.getPublisherConsumerActive() && (consumer != null)) - { - consumer.close(); - } - - return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); - } - - /** - * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. - * - * @param connection The connection to build the circuit end on. - * @param testProps The test parameters to configure the circuit end construction. - * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. - * - * @return A circuit end suitable for the receiving side of a test circuit. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. - */ - public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) - throws JMSException - { - log.debug( - "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " - + uniqueId + "): called"); - - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - - Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); - - MessageProducer producer = - props.getReceiverProducerBind() - ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; - - Destination destination = - props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) - : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); - - MessageConsumer consumer = - props.getReceiverConsumerBind() - ? ((props.getDurableSubscription() && props.getPubsub()) - ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) - : null; - - MessageMonitor messageMonitor = new MessageMonitor(); - - if (consumer != null) - { - consumer.setMessageListener(messageMonitor); - } - - if (!props.getReceiverConsumerActive() && (consumer != null)) - { - consumer.close(); - } - - return new CircuitEndBase(producer, consumer, session, messageMonitor, null); - } - - /** - * Creates a local {@link Publisher} from a {@link CircuitEnd}. The publisher implementation provides AMQP - * specific assertion methods, for testing beyond JMS. - * - * @param publisherEnd The publishing circuit end. - * - * @return A {@link Receiver}. - */ - protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) - { - return new LocalAMQPPublisherImpl(publisherEnd); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.localcircuit.LocalAMQPPublisherImpl; +import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +/** + * LocalAMQPCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted + * on the same JVM, allowing AMQP/Qpid specific options to be applied to the circuit. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Provide a standard test procedure over a test circuit. + *
          Construct test circuits appropriate to a tests context. + *
          Construct test circuits the support AMQP specific options. + *
          + */ +public class LocalAMQPCircuitFactory extends LocalCircuitFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(LocalAMQPCircuitFactory.class); + + /** + * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the publishing side of a test circuit. + * + * @throws javax.jms.JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageProducer producer = + props.getPublisherProducerBind() + ? ((props.getImmediate() | props.getMandatory()) + ? ((AMQSession) session).createProducer(destination, props.getMandatory(), props.getImmediate()) + : session.createProducer(destination)) : null; + + MessageConsumer consumer = + props.getPublisherConsumerBind() + ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + connection.setExceptionListener(exceptionMonitor); + + if (!props.getPublisherConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the receiving side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + MessageProducer producer = + props.getReceiverProducerBind() + ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageConsumer consumer = + props.getReceiverConsumerBind() + ? ((props.getDurableSubscription() && props.getPubsub()) + ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) + : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + if (!props.getReceiverConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, null); + } + + /** + * Creates a local {@link Publisher} from a {@link CircuitEnd}. The publisher implementation provides AMQP + * specific assertion methods, for testing beyond JMS. + * + * @param publisherEnd The publishing circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) + { + return new LocalAMQPPublisherImpl(publisherEnd); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java index 38a924a4ee..68aad6ee47 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java @@ -1,316 +1,316 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; -import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; -import org.apache.qpid.test.framework.localcircuit.LocalReceiverImpl; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; -import org.apache.qpid.util.ConversationFactory; - -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.*; - -import java.util.List; -import java.util.Properties; -import java.util.concurrent.atomic.AtomicLong; - -/** - * LocalCircuitFactory is a circuit factory that creates test circuits with publishing and receiving ends rooted - * on the same JVM. The ends of the circuit are presented as {@link Publisher} and {@link Receiver} interfaces, which - * in turn provide methods to apply assertions to the circuit. The creation of the circuit ends, and the presentation - * of the ends as publisher/receiver interfaces, are designed to be overriden, so that circuits and assertions that - * use messaging features not available in JMS can be written. This provides an extension point for writing tests - * against proprietary features of JMS implementations. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Provide a standard test procedure over a test circuit. - *
          Construct test circuits appropriate to a tests context. - *
          - */ -public class LocalCircuitFactory implements CircuitFactory -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(LocalCircuitFactory.class); - - /** Used to create unique destination names for each test. */ - protected static AtomicLong uniqueDestsId = new AtomicLong(); - - /** - * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles, - * begining the test and gathering the test reports from the participants. - * - * @param testCircuit The test circuit. - * @param assertions The list of assertions to apply to the test circuit. - * @param testProperties The test case definition. - */ - public void sequenceTest(Circuit testCircuit, List assertions, Properties testProperties) - { - FrameworkBaseCase.assertNoFailures(testCircuit.test(1, assertions)); - } - - /** - * Creates a test circuit for the test, configered by the test parameters specified. - * - * @param testProperties The test parameters. - * - * @return A test circuit. - */ - public Circuit createCircuit(ParsedProperties testProperties) - { - Circuit result; - - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); - - // Create a standard publisher/receivers test client pair on a shared connection, individual sessions. - try - { - // Get a unique offset to append to destination names to make them unique to the connection. - long uniqueId = uniqueDestsId.incrementAndGet(); - - // Set up the connection. - Connection connection = TestUtils.createConnection(testProperties); - - // Add the connection exception listener to assert on exception conditions with. - // ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); - // connection.setExceptionListener(exceptionMonitor); - - // Set up the publisher. - CircuitEndBase publisherEnd = createPublisherCircuitEnd(connection, props, uniqueId); - - // Set up the receiver. - CircuitEndBase receiverEnd = createReceiverCircuitEnd(connection, props, uniqueId); - - // Start listening for incoming messages. - connection.start(); - - // Package everything up. - LocalPublisherImpl publisher = createPublisherFromCircuitEnd(publisherEnd); - LocalReceiverImpl receiver = createReceiverFromCircuitEnd(receiverEnd); - - result = new LocalCircuitImpl(testProperties, publisher, receiver, connection, publisher.getExceptionMonitor()); - } - catch (JMSException e) - { - throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e); - } - - return result; - } - - /** - * Creates a local {@link Receiver} from a {@link CircuitEnd}. Sub-classes may override this to provide more - * specialized receivers if necessary. - * - * @param receiverEnd The receiving circuit end. - * - * @return A {@link Receiver}. - */ - protected LocalReceiverImpl createReceiverFromCircuitEnd(CircuitEndBase receiverEnd) - { - return new LocalReceiverImpl(receiverEnd); - } - - /** - * Creates a local {@link Publisher} from a {@link CircuitEnd}. Sub-classes may override this to provide more - * specialized receivers if necessary. - * - * @param publisherEnd The publishing circuit end. - * - * @return A {@link Receiver}. - */ - protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) - { - return new LocalPublisherImpl(publisherEnd); - } - - /** - * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. - * - * @param connection The connection to build the circuit end on. - * @param testProps The test parameters to configure the circuit end construction. - * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. - * - * @return A circuit end suitable for the publishing side of a test circuit. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. - */ - public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) - throws JMSException - { - log.debug( - "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " - + uniqueId + "): called"); - - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - - // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. - if (props.getImmediate() || props.getMandatory()) - { - throw new RuntimeException( - "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); - } - - Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); - - Destination destination = - props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) - : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); - - MessageProducer producer = props.getPublisherProducerBind() ? session.createProducer(destination) : null; - - MessageConsumer consumer = - props.getPublisherConsumerBind() - ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; - - MessageMonitor messageMonitor = new MessageMonitor(); - - if (consumer != null) - { - consumer.setMessageListener(messageMonitor); - } - - ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); - connection.setExceptionListener(exceptionMonitor); - - if (!props.getPublisherConsumerActive() && (consumer != null)) - { - consumer.close(); - } - - return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); - } - - /** - * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. - * - * @param connection The connection to build the circuit end on. - * @param testProps The test parameters to configure the circuit end construction. - * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. - * - * @return A circuit end suitable for the receiving side of a test circuit. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. - */ - public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) - throws JMSException - { - log.debug( - "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " - + uniqueId + "): called"); - - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - - // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. - if (props.getImmediate() || props.getMandatory()) - { - throw new RuntimeException( - "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); - } - - Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); - - MessageProducer producer = - props.getReceiverProducerBind() - ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; - - Destination destination = - props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) - : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); - - MessageConsumer consumer = - props.getReceiverConsumerBind() - ? ((props.getDurableSubscription() && props.getPubsub()) - ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) - : null; - - MessageMonitor messageMonitor = new MessageMonitor(); - - if (consumer != null) - { - consumer.setMessageListener(messageMonitor); - } - - if (!props.getReceiverConsumerActive() && (consumer != null)) - { - consumer.close(); - } - - return new CircuitEndBase(producer, consumer, session, messageMonitor, null); - } - - /** - * Sets the sender test client to coordinate the test with. - * - * @param sender The contact details of the sending client in the test. - */ - public void setSender(TestClientDetails sender) - { - throw new RuntimeException("Not implemented."); - } - - /** - * Sets the receiving test client to coordinate the test with. - * - * @param receiver The contact details of the sending client in the test. - */ - public void setReceiver(TestClientDetails receiver) - { - throw new RuntimeException("Not implemented."); - } - - /** - * Supplies the sending test client. - * - * @return The sending test client. - */ - public TestClientDetails getSender() - { - throw new RuntimeException("Not implemented."); - } - - /** - * Supplies the receiving test client. - * - * @return The receiving test client. - */ - public List getReceivers() - { - throw new RuntimeException("Not implemented."); - } - - /** - * Accepts the conversation factory over which to hold the test coordinating conversation. - * - * @param conversationFactory The conversation factory to coordinate the test over. - */ - public void setConversationFactory(ConversationFactory conversationFactory) - { - throw new RuntimeException("Not implemented."); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; +import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; +import org.apache.qpid.test.framework.localcircuit.LocalReceiverImpl; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; +import org.apache.qpid.util.ConversationFactory; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +import java.util.List; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicLong; + +/** + * LocalCircuitFactory is a circuit factory that creates test circuits with publishing and receiving ends rooted + * on the same JVM. The ends of the circuit are presented as {@link Publisher} and {@link Receiver} interfaces, which + * in turn provide methods to apply assertions to the circuit. The creation of the circuit ends, and the presentation + * of the ends as publisher/receiver interfaces, are designed to be overriden, so that circuits and assertions that + * use messaging features not available in JMS can be written. This provides an extension point for writing tests + * against proprietary features of JMS implementations. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Provide a standard test procedure over a test circuit. + *
          Construct test circuits appropriate to a tests context. + *
          + */ +public class LocalCircuitFactory implements CircuitFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(LocalCircuitFactory.class); + + /** Used to create unique destination names for each test. */ + protected static AtomicLong uniqueDestsId = new AtomicLong(); + + /** + * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles, + * begining the test and gathering the test reports from the participants. + * + * @param testCircuit The test circuit. + * @param assertions The list of assertions to apply to the test circuit. + * @param testProperties The test case definition. + */ + public void sequenceTest(Circuit testCircuit, List assertions, Properties testProperties) + { + FrameworkBaseCase.assertNoFailures(testCircuit.test(1, assertions)); + } + + /** + * Creates a test circuit for the test, configered by the test parameters specified. + * + * @param testProperties The test parameters. + * + * @return A test circuit. + */ + public Circuit createCircuit(ParsedProperties testProperties) + { + Circuit result; + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); + + // Create a standard publisher/receivers test client pair on a shared connection, individual sessions. + try + { + // Get a unique offset to append to destination names to make them unique to the connection. + long uniqueId = uniqueDestsId.incrementAndGet(); + + // Set up the connection. + Connection connection = TestUtils.createConnection(testProperties); + + // Add the connection exception listener to assert on exception conditions with. + // ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + // connection.setExceptionListener(exceptionMonitor); + + // Set up the publisher. + CircuitEndBase publisherEnd = createPublisherCircuitEnd(connection, props, uniqueId); + + // Set up the receiver. + CircuitEndBase receiverEnd = createReceiverCircuitEnd(connection, props, uniqueId); + + // Start listening for incoming messages. + connection.start(); + + // Package everything up. + LocalPublisherImpl publisher = createPublisherFromCircuitEnd(publisherEnd); + LocalReceiverImpl receiver = createReceiverFromCircuitEnd(receiverEnd); + + result = new LocalCircuitImpl(testProperties, publisher, receiver, connection, publisher.getExceptionMonitor()); + } + catch (JMSException e) + { + throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e); + } + + return result; + } + + /** + * Creates a local {@link Receiver} from a {@link CircuitEnd}. Sub-classes may override this to provide more + * specialized receivers if necessary. + * + * @param receiverEnd The receiving circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalReceiverImpl createReceiverFromCircuitEnd(CircuitEndBase receiverEnd) + { + return new LocalReceiverImpl(receiverEnd); + } + + /** + * Creates a local {@link Publisher} from a {@link CircuitEnd}. Sub-classes may override this to provide more + * specialized receivers if necessary. + * + * @param publisherEnd The publishing circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) + { + return new LocalPublisherImpl(publisherEnd); + } + + /** + * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the publishing side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. + if (props.getImmediate() || props.getMandatory()) + { + throw new RuntimeException( + "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); + } + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageProducer producer = props.getPublisherProducerBind() ? session.createProducer(destination) : null; + + MessageConsumer consumer = + props.getPublisherConsumerBind() + ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + connection.setExceptionListener(exceptionMonitor); + + if (!props.getPublisherConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the receiving side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. + if (props.getImmediate() || props.getMandatory()) + { + throw new RuntimeException( + "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); + } + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + MessageProducer producer = + props.getReceiverProducerBind() + ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageConsumer consumer = + props.getReceiverConsumerBind() + ? ((props.getDurableSubscription() && props.getPubsub()) + ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) + : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + if (!props.getReceiverConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, null); + } + + /** + * Sets the sender test client to coordinate the test with. + * + * @param sender The contact details of the sending client in the test. + */ + public void setSender(TestClientDetails sender) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Sets the receiving test client to coordinate the test with. + * + * @param receiver The contact details of the sending client in the test. + */ + public void setReceiver(TestClientDetails receiver) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Supplies the sending test client. + * + * @return The sending test client. + */ + public TestClientDetails getSender() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Supplies the receiving test client. + * + * @return The receiving test client. + */ + public List getReceivers() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Accepts the conversation factory over which to hold the test coordinating conversation. + * + * @param conversationFactory The conversation factory to coordinate the test over. + */ + public void setConversationFactory(ConversationFactory conversationFactory) + { + throw new RuntimeException("Not implemented."); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java b/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java index b672b9c3ce..397c4e9fbd 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java @@ -1,167 +1,167 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -/** - * MessageIdentityVector provides a message identification scheme, that matches individual messages with test cases. - * Test messages are being sent by a number of test clients, sending messages over a set of routes, and being received - * by another set of test clients. Each test is itself, being run within a test cycle, of which there could be many. It - * is the job of the test coordinator to request and receive reports from the available test clients, on what has been - * sent, what has been received, and what errors may have occurred, and to reconcile this information against the - * assertions being applied by the test case. In order to be able to figure out which messages belong to which test, - * there needs to be an identification scheme, that the coordinator can use to correlate messages in senders and - * receiver reports. Every message sent in a test can be associated with this information. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Identify a test case, a handling client id, a circuit end within the client, and a test cycle number. - *
          - */ -public class MessageIdentityVector -{ - /** Holds the test case vector component of the message identity vector. */ - private TestCaseVector testCaseVector; - - /** The unique client id. */ - private String clientId; - - /** The unique circuit end number within the client id. */ - private int circuitEndId; - - /** - * Creates a new identity vector for test messages. - * - * @param testCase The name of the test case generating the messages. - * @param clientId The unique id of the client implementing a circuit end that is handling the messages. - * @param circuitEndId The unique id number of the circuit end within the client. - * @param testCycleNumber The cycle iteration number of the test case. - */ - public MessageIdentityVector(String testCase, String clientId, int circuitEndId, int testCycleNumber) - { - this.testCaseVector = new TestCaseVector(testCase, testCycleNumber); - this.clientId = clientId; - this.circuitEndId = circuitEndId; - } - - /** - * Reports the test case vector component of the message identity vector. - * - * @return The test case vector component of the message identity vector. - */ - public TestCaseVector getTestCaseVector() - { - return testCaseVector; - } - - /** - * Reports the name of the test case. - * - * @return The name of the test case. - */ - public String getTestCase() - { - return testCaseVector.getTestCase(); - } - - /** - * Reports the test iteration cycle number within the test case. - * - * @return The test iteration cycle number within the test case. - */ - public int getTestCycleNumber() - { - return testCaseVector.getTestCycleNumber(); - } - - /** - * Resports the client id. - * - * @return The client id. - */ - public String getClientId() - { - return clientId; - } - - /** - * Reports the circuit end number within the test client. - * - * @return The circuit end number within the test client. - */ - public int getCircuitEndId() - { - return circuitEndId; - } - - /** - * Compares this identity vector with another for equality. All fields must match. - * - * @param o The identity vector to compare with. - * - * @return true if the identity vector is identical to this one by all fields, false otherwise. - */ - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if ((o == null) || (getClass() != o.getClass())) - { - return false; - } - - MessageIdentityVector that = (MessageIdentityVector) o; - - if (circuitEndId != that.circuitEndId) - { - return false; - } - - if ((clientId != null) ? (!clientId.equals(that.clientId)) : (that.clientId != null)) - { - return false; - } - - if ((testCaseVector != null) ? (!testCaseVector.equals(that.testCaseVector)) : (that.testCaseVector != null)) - { - return false; - } - - return true; - } - - /** - * Computes a hash code for this identity vector based on all fields. - * - * @return A hash code for this identity vector based on all fields. - */ - public int hashCode() - { - int result; - result = ((testCaseVector != null) ? testCaseVector.hashCode() : 0); - result = (31 * result) + ((clientId != null) ? clientId.hashCode() : 0); - result = (31 * result) + circuitEndId; - - return result; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +/** + * MessageIdentityVector provides a message identification scheme, that matches individual messages with test cases. + * Test messages are being sent by a number of test clients, sending messages over a set of routes, and being received + * by another set of test clients. Each test is itself, being run within a test cycle, of which there could be many. It + * is the job of the test coordinator to request and receive reports from the available test clients, on what has been + * sent, what has been received, and what errors may have occurred, and to reconcile this information against the + * assertions being applied by the test case. In order to be able to figure out which messages belong to which test, + * there needs to be an identification scheme, that the coordinator can use to correlate messages in senders and + * receiver reports. Every message sent in a test can be associated with this information. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Identify a test case, a handling client id, a circuit end within the client, and a test cycle number. + *
          + */ +public class MessageIdentityVector +{ + /** Holds the test case vector component of the message identity vector. */ + private TestCaseVector testCaseVector; + + /** The unique client id. */ + private String clientId; + + /** The unique circuit end number within the client id. */ + private int circuitEndId; + + /** + * Creates a new identity vector for test messages. + * + * @param testCase The name of the test case generating the messages. + * @param clientId The unique id of the client implementing a circuit end that is handling the messages. + * @param circuitEndId The unique id number of the circuit end within the client. + * @param testCycleNumber The cycle iteration number of the test case. + */ + public MessageIdentityVector(String testCase, String clientId, int circuitEndId, int testCycleNumber) + { + this.testCaseVector = new TestCaseVector(testCase, testCycleNumber); + this.clientId = clientId; + this.circuitEndId = circuitEndId; + } + + /** + * Reports the test case vector component of the message identity vector. + * + * @return The test case vector component of the message identity vector. + */ + public TestCaseVector getTestCaseVector() + { + return testCaseVector; + } + + /** + * Reports the name of the test case. + * + * @return The name of the test case. + */ + public String getTestCase() + { + return testCaseVector.getTestCase(); + } + + /** + * Reports the test iteration cycle number within the test case. + * + * @return The test iteration cycle number within the test case. + */ + public int getTestCycleNumber() + { + return testCaseVector.getTestCycleNumber(); + } + + /** + * Resports the client id. + * + * @return The client id. + */ + public String getClientId() + { + return clientId; + } + + /** + * Reports the circuit end number within the test client. + * + * @return The circuit end number within the test client. + */ + public int getCircuitEndId() + { + return circuitEndId; + } + + /** + * Compares this identity vector with another for equality. All fields must match. + * + * @param o The identity vector to compare with. + * + * @return true if the identity vector is identical to this one by all fields, false otherwise. + */ + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if ((o == null) || (getClass() != o.getClass())) + { + return false; + } + + MessageIdentityVector that = (MessageIdentityVector) o; + + if (circuitEndId != that.circuitEndId) + { + return false; + } + + if ((clientId != null) ? (!clientId.equals(that.clientId)) : (that.clientId != null)) + { + return false; + } + + if ((testCaseVector != null) ? (!testCaseVector.equals(that.testCaseVector)) : (that.testCaseVector != null)) + { + return false; + } + + return true; + } + + /** + * Computes a hash code for this identity vector based on all fields. + * + * @return A hash code for this identity vector based on all fields. + */ + public int hashCode() + { + int result; + result = ((testCaseVector != null) ? testCaseVector.hashCode() : 0); + result = (31 * result) + ((clientId != null) ? clientId.hashCode() : 0); + result = (31 * result) + circuitEndId; + + return result; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java b/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java index 63c7fd61c3..2a20be12d6 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java @@ -1,112 +1,112 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -import org.apache.log4j.Logger; - -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -/** - * NotApplicableAssertion is a messaging assertion that can be used when an assertion requested by a test-case is not - * applicable to the testing scenario. For example an assertion may relate to AMQP functionality, but a test case may be - * being run over a non-AMQP JMS implementation, in which case the request to create the assertion may return this - * instead of the proper assertion. The test framework is configurable to quietly drop these assertions, log them - * as warnings to the console, or raise them as test failures. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Quitely pass. - *
          Log a warning. - *
          Raise a test failure. - *
          - */ -public class NotApplicableAssertion implements Assertion -{ - /** Used for logging to the console. */ - private static final Logger console = Logger.getLogger("CONSOLE." + NotApplicableAssertion.class.getName()); - - /** The possible behavioural modes of this assertion. */ - private enum Mode - { - /** Quietly ignore the assertion by passing. */ - Quiet, - - /** Ignore the assertion by passing but log a warning about it. */ - Warn, - - /** Fail the assertion. */ - Fail; - } - - /** The behavioural mode of the assertion. */ - private Mode mode; - - /** - * Creates an assertion that is driven by the value of the 'notApplicableAssertion' property of the test - * configuration. Its value should match one of 'quiet', 'warn' or 'fail' and if it does not it is automatically - * read as 'fail'. - * - * @param testProperties The test configuration properties. - */ - public NotApplicableAssertion(ParsedProperties testProperties) - { - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); - - String modeName = props.getNotApplicableAssertionMode(); - - if ("quiet".equals(modeName)) - { - mode = Mode.Quiet; - } - else if ("warn".equals(modeName)) - { - mode = Mode.Warn; - } - else - { - mode = Mode.Fail; - } - } - - /** - * Applies the assertion. - * - * @return true if the assertion passes, false if it fails. - */ - public boolean apply() - { - switch (mode) - { - case Quiet: - return true; - - case Warn: - console.warn("Warning: Not applicable assertion being ignored."); - - return true; - - case Fail: - default: - return false; - } - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +/** + * NotApplicableAssertion is a messaging assertion that can be used when an assertion requested by a test-case is not + * applicable to the testing scenario. For example an assertion may relate to AMQP functionality, but a test case may be + * being run over a non-AMQP JMS implementation, in which case the request to create the assertion may return this + * instead of the proper assertion. The test framework is configurable to quietly drop these assertions, log them + * as warnings to the console, or raise them as test failures. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Quitely pass. + *
          Log a warning. + *
          Raise a test failure. + *
          + */ +public class NotApplicableAssertion implements Assertion +{ + /** Used for logging to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE." + NotApplicableAssertion.class.getName()); + + /** The possible behavioural modes of this assertion. */ + private enum Mode + { + /** Quietly ignore the assertion by passing. */ + Quiet, + + /** Ignore the assertion by passing but log a warning about it. */ + Warn, + + /** Fail the assertion. */ + Fail; + } + + /** The behavioural mode of the assertion. */ + private Mode mode; + + /** + * Creates an assertion that is driven by the value of the 'notApplicableAssertion' property of the test + * configuration. Its value should match one of 'quiet', 'warn' or 'fail' and if it does not it is automatically + * read as 'fail'. + * + * @param testProperties The test configuration properties. + */ + public NotApplicableAssertion(ParsedProperties testProperties) + { + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); + + String modeName = props.getNotApplicableAssertionMode(); + + if ("quiet".equals(modeName)) + { + mode = Mode.Quiet; + } + else if ("warn".equals(modeName)) + { + mode = Mode.Warn; + } + else + { + mode = Mode.Fail; + } + } + + /** + * Applies the assertion. + * + * @return true if the assertion passes, false if it fails. + */ + public boolean apply() + { + switch (mode) + { + case Quiet: + return true; + + case Warn: + console.warn("Warning: Not applicable assertion being ignored."); + + return true; + + case Fail: + default: + return false; + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java b/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java index 0518a827ba..ad1e70f6f7 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java @@ -1,88 +1,88 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework; - -/** - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - *
          - */ -public class TestCaseVector -{ - /** The test case name. */ - private String testCase; - - /** The test cycle number within the test case. */ - private int testCycleNumber; - - public TestCaseVector(String testCase, int testCycleNumber) - { - this.testCase = testCase; - this.testCycleNumber = testCycleNumber; - } - - public String getTestCase() - { - return testCase; - } - - public int getTestCycleNumber() - { - return testCycleNumber; - } - - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if ((o == null) || (getClass() != o.getClass())) - { - return false; - } - - TestCaseVector that = (TestCaseVector) o; - - if (testCycleNumber != that.testCycleNumber) - { - return false; - } - - if ((testCase != null) ? (!testCase.equals(that.testCase)) : (that.testCase != null)) - { - return false; - } - - return true; - } - - public int hashCode() - { - int result; - result = ((testCase != null) ? testCase.hashCode() : 0); - result = (31 * result) + testCycleNumber; - - return result; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework; + +/** + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + *
          + */ +public class TestCaseVector +{ + /** The test case name. */ + private String testCase; + + /** The test cycle number within the test case. */ + private int testCycleNumber; + + public TestCaseVector(String testCase, int testCycleNumber) + { + this.testCase = testCase; + this.testCycleNumber = testCycleNumber; + } + + public String getTestCase() + { + return testCase; + } + + public int getTestCycleNumber() + { + return testCycleNumber; + } + + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if ((o == null) || (getClass() != o.getClass())) + { + return false; + } + + TestCaseVector that = (TestCaseVector) o; + + if (testCycleNumber != that.testCycleNumber) + { + return false; + } + + if ((testCase != null) ? (!testCase.equals(that.testCase)) : (that.testCase != null)) + { + return false; + } + + return true; + } + + public int hashCode() + { + int result; + result = ((testCase != null) ? testCase.hashCode() : 0); + result = (31 * result) + testCycleNumber; + + return result; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java index 1c138fe575..9eba36e1e9 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java @@ -1,497 +1,497 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework.distributedtesting; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.clocksynch.ClockSynchThread; -import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer; -import org.apache.qpid.util.ReflectionUtils; -import org.apache.qpid.util.ReflectionUtilsException; - -import org.apache.qpid.junit.extensions.SleepThrottle; -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -import java.util.*; - -/** - * Implements a test client as described in the interop testing spec - * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that - * reacts to control message sequences send by the test {@link Coordinator}. - * - *

          - *
          Messages Handled by TestClient
          Message Action - *
          Invite(compulsory) Reply with Enlist. - *
          Invite(test case) Reply with Enlist if test case available. - *
          AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. - *
          Start Send test messages defined by test parameters. Send report on messages sent. - *
          Status Request Send report on messages received. - *
          Terminate Terminate the test client. - *
          ClockSynch Synch clock against the supplied UDP address. - *
          - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Handle all incoming control messages. {@link TestClientControlledTest} - *
          Configure and look up test cases by name. {@link TestClientControlledTest} - *
          - */ -public class TestClient implements MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestClient.class); - - /** Used for reporting to the console. */ - private static final Logger console = Logger.getLogger("CONSOLE"); - - /** Holds the default identifying name of the test client. */ - public static final String CLIENT_NAME = "java"; - - /** Holds the URL of the broker to run the tests on. */ - public static String brokerUrl; - - /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ - public static String virtualHost; - - /** - * Holds the test context properties that provides the default test parameters, plus command line overrides. - * This is initialized with the default test parameters, to which command line overrides may be applied. - */ - public static ParsedProperties testContextProperties = - TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** Holds all the test cases loaded from the classpath. */ - Map testCases = new HashMap(); - - /** Holds the test case currently being run by this client. */ - protected TestClientControlledTest currentTestCase; - - /** Holds the connection to the broker that the test is being coordinated on. */ - protected Connection connection; - - /** Holds the message producer to hold the test coordination over. */ - protected MessageProducer producer; - - /** Holds the JMS controlSession for the test coordination. */ - protected Session session; - - /** Holds the name of this client, with a default value. */ - protected String clientName = CLIENT_NAME; - - /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ - protected boolean join; - - /** Holds the clock synchronizer for the test node. */ - ClockSynchThread clockSynchThread; - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param pBrokerUrl The url of the broker to connect to. - * @param pVirtualHost The virtual host to conect to. - * @param clientName The client name to use. - * @param join Flag to indicate that this client should attempt to join running tests. - */ - public TestClient(String pBrokerUrl, String pVirtualHost, String clientName, boolean join) - { - log.debug("public TestClient(String pBrokerUrl = " + pBrokerUrl + ", String pVirtualHost = " + pVirtualHost - + ", String clientName = " + clientName + ", boolean join = " + join + "): called"); - - // Retain the connection parameters. - brokerUrl = pBrokerUrl; - virtualHost = pVirtualHost; - this.clientName = clientName; - this.join = join; - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

          - *
          -b The broker URL. Optional. - *
          -h The virtual host. Optional. - *
          -n The test client name. Optional. - *
          name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
          - * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called"); - console.info("Qpid Distributed Test Client."); - - // Override the default broker url to be localhost:5672. - testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); - - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exist if there are errors). - // Any options and trailing name=value pairs are also injected into the test context properties object, - // to override any defaults that may have been set up. - ParsedProperties options = - new ParsedProperties(org.apache.qpid.junit.extensions.util.CommandLineParser.processCommandLine(args, - new org.apache.qpid.junit.extensions.util.CommandLineParser( - new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" }, - { "o", "The name of the directory to output test timings to.", "dir", "false" }, - { "n", "The name of the test client.", "name", "false" }, - { "j", "Join this test client to running test.", "false" } - }), testContextProperties)); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - String clientName = options.getProperty("n"); - clientName = (clientName == null) ? CLIENT_NAME : clientName; - boolean join = options.getPropertyAsBoolean("j"); - - // To distinguish logging output set up an NDC on the client name. - NDC.push(clientName); - - // Create a test client and start it running. - TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join); - - // Use a class path scanner to find all the interop test case implementations. - // Hard code the test classes till the classpath scanner is fixed. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true); - testCaseClasses.addAll(loadTestCases("org.apache.qpid.interop.clienttestcases.TestCase1DummyRun", - "org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P", - "org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub", - "org.apache.qpid.interop.clienttestcases.TestCase4P2PMessageSize", - "org.apache.qpid.interop.clienttestcases.TestCase5PubSubMessageSize", - "org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd")); - - try - { - client.start(testCaseClasses); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - console.info(e.getMessage()); - System.exit(1); - } - } - - /** - * Parses a list of class names, and loads them if they are available on the class path. - * - * @param classNames The names of the classes to load. - * - * @return A list of the loaded test case classes. - */ - public static List> loadTestCases(String... classNames) - { - List> testCases = - new LinkedList>(); - - for (String className : classNames) - { - try - { - Class cls = ReflectionUtils.forName(className); - testCases.add((Class) cls); - } - catch (ReflectionUtilsException e) - { - // Ignore, class could not be found, so test not available. - console.warn("Requested class " + className + " cannot be found, ignoring it."); - } - catch (ClassCastException e) - { - // Ignore, class was not of correct type to be a test case. - console.warn("Requested class " + className + " is not an instance of TestClientControlledTest."); - } - } - - return testCases; - } - - /** - * Starts the interop test client running. This causes it to start listening for incoming test invites. - * - * @param testCaseClasses The classes of the available test cases. The test case names from these are used to - * matchin incoming test invites against. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through. - */ - protected void start(Collection> testCaseClasses) throws JMSException - { - log.debug("protected void start(Collection> testCaseClasses = " - + testCaseClasses + "): called"); - - // Create all the test case implementations and index them by the test names. - for (Class nextClass : testCaseClasses) - { - try - { - TestClientControlledTest testCase = nextClass.newInstance(); - testCases.put(testCase.getName(), testCase); - } - catch (InstantiationException e) - { - log.warn("Could not instantiate test case class: " + nextClass.getName(), e); - // Ignored. - } - catch (IllegalAccessException e) - { - log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); - // Ignored. - } - } - - // Open a connection to communicate with the coordinator on. - connection = TestUtils.createConnection(testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Set this up to listen for control messages. - Topic privateControlTopic = session.createTopic("iop.control." + clientName); - MessageConsumer consumer = session.createConsumer(privateControlTopic); - consumer.setMessageListener(this); - - Topic controlTopic = session.createTopic("iop.control"); - MessageConsumer consumer2 = session.createConsumer(controlTopic); - consumer2.setMessageListener(this); - - // Create a producer to send replies with. - producer = session.createProducer(null); - - // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client - // is available to join the current test case, if it supports it. This message may be ignored, or it may result - // in this test client receiving a test invite. - if (join) - { - Message joinMessage = session.createMessage(); - - joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); - joinMessage.setStringProperty("CLIENT_NAME", clientName); - joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - producer.send(controlTopic, joinMessage); - } - - // Start listening for incoming control messages. - connection.start(); - } - - /** - * Handles all incoming control messages. - * - * @param message The incoming message. - */ - public void onMessage(Message message) - { - NDC.push(clientName); - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - String testName = message.getStringProperty("TEST_NAME"); - - log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'"); - - // Check if the message is a test invite. - if ("INVITE".equals(controlType)) - { - // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites - // for which test cases exist. - boolean enlist = false; - - if (testName != null) - { - log.debug("Got an invite to test: " + testName); - - // Check if the requested test case is available. - TestClientControlledTest testCase = testCases.get(testName); - - if (testCase != null) - { - log.debug("Found implementing class for test '" + testName + "', enlisting for it."); - - // Check if the test case will accept the invitation. - enlist = testCase.acceptInvite(message); - - log.debug("The test case " - + (enlist ? " accepted the invite, enlisting for it." - : " did not accept the invite, not enlisting.")); - - // Make the requested test case the current test case. - currentTestCase = testCase; - } - else - { - log.debug("Received an invite to the test '" + testName + "' but this test is not known."); - } - } - else - { - log.debug("Got a compulsory invite, enlisting for it."); - - enlist = true; - } - - if (enlist) - { - // Reply with the client name in an Enlist message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - else - { - // Reply with the client name in an Decline message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - } - else if ("ASSIGN_ROLE".equals(controlType)) - { - // Assign the role to the current test case. - String roleName = message.getStringProperty("ROLE"); - - log.debug("Got a role assignment to role: " + roleName); - - TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName); - - currentTestCase.assignRole(role, message); - - // Reply by accepting the role in an Accept Role message. - Message acceptRoleMessage = session.createMessage(); - acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName); - acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); - acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), acceptRoleMessage); - } - else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) - { - if ("START".equals(controlType)) - { - log.debug("Got a start notification."); - - // Extract the number of test messages to send from the start notification. - int numMessages; - - try - { - numMessages = message.getIntProperty("MESSAGE_COUNT"); - } - catch (NumberFormatException e) - { - // If the number of messages is not specified, use the default of one. - numMessages = 1; - } - - // Start the current test case. - currentTestCase.start(numMessages); - } - else - { - log.debug("Got a status request."); - } - - // Generate the report from the test case and reply with it as a Report message. - Message reportMessage = currentTestCase.getReport(session); - reportMessage.setStringProperty("CLIENT_NAME", clientName); - reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); - reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), reportMessage); - } - else if ("TERMINATE".equals(controlType)) - { - console.info("Received termination instruction from coordinator."); - - // Is a cleaner shutdown needed? - connection.close(); - System.exit(0); - } - else if ("CLOCK_SYNCH".equals(controlType)) - { - log.debug("Received clock synch command."); - String address = message.getStringProperty("ADDRESS"); - - log.debug("address = " + address); - - // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds. - if (clockSynchThread != null) - { - clockSynchThread.terminate(); - } - - SleepThrottle throttle = new SleepThrottle(); - throttle.setRate(0.1f); - - clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle); - clockSynchThread.start(); - } - else - { - // Log a warning about this but otherwise ignore it. - log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); - } - } - catch (JMSException e) - { - // Log a warning about this, but otherwise ignore it. - log.warn("Got JMSException whilst handling message: " + message, e); - } - // Log any runtimes that fall through this message handler. These are fatal errors for the test client. - catch (RuntimeException e) - { - log.error("The test client message handler got an unhandled exception: ", e); - console.info("The message handler got an unhandled exception, terminating the test client."); - System.exit(1); - } - finally - { - NDC.pop(); - } - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework.distributedtesting; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.clocksynch.ClockSynchThread; +import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer; +import org.apache.qpid.util.ReflectionUtils; +import org.apache.qpid.util.ReflectionUtilsException; + +import org.apache.qpid.junit.extensions.SleepThrottle; +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +import java.util.*; + +/** + * Implements a test client as described in the interop testing spec + * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that + * reacts to control message sequences send by the test {@link Coordinator}. + * + *

          + *
          Messages Handled by TestClient
          Message Action + *
          Invite(compulsory) Reply with Enlist. + *
          Invite(test case) Reply with Enlist if test case available. + *
          AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. + *
          Start Send test messages defined by test parameters. Send report on messages sent. + *
          Status Request Send report on messages received. + *
          Terminate Terminate the test client. + *
          ClockSynch Synch clock against the supplied UDP address. + *
          + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Handle all incoming control messages. {@link TestClientControlledTest} + *
          Configure and look up test cases by name. {@link TestClientControlledTest} + *
          + */ +public class TestClient implements MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestClient.class); + + /** Used for reporting to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE"); + + /** Holds the default identifying name of the test client. */ + public static final String CLIENT_NAME = "java"; + + /** Holds the URL of the broker to run the tests on. */ + public static String brokerUrl; + + /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ + public static String virtualHost; + + /** + * Holds the test context properties that provides the default test parameters, plus command line overrides. + * This is initialized with the default test parameters, to which command line overrides may be applied. + */ + public static ParsedProperties testContextProperties = + TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Holds all the test cases loaded from the classpath. */ + Map testCases = new HashMap(); + + /** Holds the test case currently being run by this client. */ + protected TestClientControlledTest currentTestCase; + + /** Holds the connection to the broker that the test is being coordinated on. */ + protected Connection connection; + + /** Holds the message producer to hold the test coordination over. */ + protected MessageProducer producer; + + /** Holds the JMS controlSession for the test coordination. */ + protected Session session; + + /** Holds the name of this client, with a default value. */ + protected String clientName = CLIENT_NAME; + + /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ + protected boolean join; + + /** Holds the clock synchronizer for the test node. */ + ClockSynchThread clockSynchThread; + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param pBrokerUrl The url of the broker to connect to. + * @param pVirtualHost The virtual host to conect to. + * @param clientName The client name to use. + * @param join Flag to indicate that this client should attempt to join running tests. + */ + public TestClient(String pBrokerUrl, String pVirtualHost, String clientName, boolean join) + { + log.debug("public TestClient(String pBrokerUrl = " + pBrokerUrl + ", String pVirtualHost = " + pVirtualHost + + ", String clientName = " + clientName + ", boolean join = " + join + "): called"); + + // Retain the connection parameters. + brokerUrl = pBrokerUrl; + virtualHost = pVirtualHost; + this.clientName = clientName; + this.join = join; + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

          + *
          -b The broker URL. Optional. + *
          -h The virtual host. Optional. + *
          -n The test client name. Optional. + *
          name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
          + * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called"); + console.info("Qpid Distributed Test Client."); + + // Override the default broker url to be localhost:5672. + testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); + + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + // Any options and trailing name=value pairs are also injected into the test context properties object, + // to override any defaults that may have been set up. + ParsedProperties options = + new ParsedProperties(org.apache.qpid.junit.extensions.util.CommandLineParser.processCommandLine(args, + new org.apache.qpid.junit.extensions.util.CommandLineParser( + new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" }, + { "n", "The name of the test client.", "name", "false" }, + { "j", "Join this test client to running test.", "false" } + }), testContextProperties)); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + String clientName = options.getProperty("n"); + clientName = (clientName == null) ? CLIENT_NAME : clientName; + boolean join = options.getPropertyAsBoolean("j"); + + // To distinguish logging output set up an NDC on the client name. + NDC.push(clientName); + + // Create a test client and start it running. + TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join); + + // Use a class path scanner to find all the interop test case implementations. + // Hard code the test classes till the classpath scanner is fixed. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true); + testCaseClasses.addAll(loadTestCases("org.apache.qpid.interop.clienttestcases.TestCase1DummyRun", + "org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P", + "org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub", + "org.apache.qpid.interop.clienttestcases.TestCase4P2PMessageSize", + "org.apache.qpid.interop.clienttestcases.TestCase5PubSubMessageSize", + "org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd")); + + try + { + client.start(testCaseClasses); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + console.info(e.getMessage()); + System.exit(1); + } + } + + /** + * Parses a list of class names, and loads them if they are available on the class path. + * + * @param classNames The names of the classes to load. + * + * @return A list of the loaded test case classes. + */ + public static List> loadTestCases(String... classNames) + { + List> testCases = + new LinkedList>(); + + for (String className : classNames) + { + try + { + Class cls = ReflectionUtils.forName(className); + testCases.add((Class) cls); + } + catch (ReflectionUtilsException e) + { + // Ignore, class could not be found, so test not available. + console.warn("Requested class " + className + " cannot be found, ignoring it."); + } + catch (ClassCastException e) + { + // Ignore, class was not of correct type to be a test case. + console.warn("Requested class " + className + " is not an instance of TestClientControlledTest."); + } + } + + return testCases; + } + + /** + * Starts the interop test client running. This causes it to start listening for incoming test invites. + * + * @param testCaseClasses The classes of the available test cases. The test case names from these are used to + * matchin incoming test invites against. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. + */ + protected void start(Collection> testCaseClasses) throws JMSException + { + log.debug("protected void start(Collection> testCaseClasses = " + + testCaseClasses + "): called"); + + // Create all the test case implementations and index them by the test names. + for (Class nextClass : testCaseClasses) + { + try + { + TestClientControlledTest testCase = nextClass.newInstance(); + testCases.put(testCase.getName(), testCase); + } + catch (InstantiationException e) + { + log.warn("Could not instantiate test case class: " + nextClass.getName(), e); + // Ignored. + } + catch (IllegalAccessException e) + { + log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); + // Ignored. + } + } + + // Open a connection to communicate with the coordinator on. + connection = TestUtils.createConnection(testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set this up to listen for control messages. + Topic privateControlTopic = session.createTopic("iop.control." + clientName); + MessageConsumer consumer = session.createConsumer(privateControlTopic); + consumer.setMessageListener(this); + + Topic controlTopic = session.createTopic("iop.control"); + MessageConsumer consumer2 = session.createConsumer(controlTopic); + consumer2.setMessageListener(this); + + // Create a producer to send replies with. + producer = session.createProducer(null); + + // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client + // is available to join the current test case, if it supports it. This message may be ignored, or it may result + // in this test client receiving a test invite. + if (join) + { + Message joinMessage = session.createMessage(); + + joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); + joinMessage.setStringProperty("CLIENT_NAME", clientName); + joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + producer.send(controlTopic, joinMessage); + } + + // Start listening for incoming control messages. + connection.start(); + } + + /** + * Handles all incoming control messages. + * + * @param message The incoming message. + */ + public void onMessage(Message message) + { + NDC.push(clientName); + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + String testName = message.getStringProperty("TEST_NAME"); + + log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'"); + + // Check if the message is a test invite. + if ("INVITE".equals(controlType)) + { + // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites + // for which test cases exist. + boolean enlist = false; + + if (testName != null) + { + log.debug("Got an invite to test: " + testName); + + // Check if the requested test case is available. + TestClientControlledTest testCase = testCases.get(testName); + + if (testCase != null) + { + log.debug("Found implementing class for test '" + testName + "', enlisting for it."); + + // Check if the test case will accept the invitation. + enlist = testCase.acceptInvite(message); + + log.debug("The test case " + + (enlist ? " accepted the invite, enlisting for it." + : " did not accept the invite, not enlisting.")); + + // Make the requested test case the current test case. + currentTestCase = testCase; + } + else + { + log.debug("Received an invite to the test '" + testName + "' but this test is not known."); + } + } + else + { + log.debug("Got a compulsory invite, enlisting for it."); + + enlist = true; + } + + if (enlist) + { + // Reply with the client name in an Enlist message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + else + { + // Reply with the client name in an Decline message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + } + else if ("ASSIGN_ROLE".equals(controlType)) + { + // Assign the role to the current test case. + String roleName = message.getStringProperty("ROLE"); + + log.debug("Got a role assignment to role: " + roleName); + + TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName); + + currentTestCase.assignRole(role, message); + + // Reply by accepting the role in an Accept Role message. + Message acceptRoleMessage = session.createMessage(); + acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName); + acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); + acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), acceptRoleMessage); + } + else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) + { + if ("START".equals(controlType)) + { + log.debug("Got a start notification."); + + // Extract the number of test messages to send from the start notification. + int numMessages; + + try + { + numMessages = message.getIntProperty("MESSAGE_COUNT"); + } + catch (NumberFormatException e) + { + // If the number of messages is not specified, use the default of one. + numMessages = 1; + } + + // Start the current test case. + currentTestCase.start(numMessages); + } + else + { + log.debug("Got a status request."); + } + + // Generate the report from the test case and reply with it as a Report message. + Message reportMessage = currentTestCase.getReport(session); + reportMessage.setStringProperty("CLIENT_NAME", clientName); + reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); + reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), reportMessage); + } + else if ("TERMINATE".equals(controlType)) + { + console.info("Received termination instruction from coordinator."); + + // Is a cleaner shutdown needed? + connection.close(); + System.exit(0); + } + else if ("CLOCK_SYNCH".equals(controlType)) + { + log.debug("Received clock synch command."); + String address = message.getStringProperty("ADDRESS"); + + log.debug("address = " + address); + + // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds. + if (clockSynchThread != null) + { + clockSynchThread.terminate(); + } + + SleepThrottle throttle = new SleepThrottle(); + throttle.setRate(0.1f); + + clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle); + clockSynchThread.start(); + } + else + { + // Log a warning about this but otherwise ignore it. + log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); + } + } + catch (JMSException e) + { + // Log a warning about this, but otherwise ignore it. + log.warn("Got JMSException whilst handling message: " + message, e); + } + // Log any runtimes that fall through this message handler. These are fatal errors for the test client. + catch (RuntimeException e) + { + log.error("The test client message handler got an unhandled exception: ", e); + console.info("The message handler got an unhandled exception, terminating the test client."); + System.exit(1); + } + finally + { + NDC.pop(); + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java index 14ae108da8..4388c7fbd8 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java @@ -1,133 +1,133 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework.localcircuit; - -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; -import org.apache.qpid.test.framework.*; - -import org.apache.qpid.junit.extensions.util.ParsedProperties; - -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Session; - -/** - * LocalAMQPPublisherImpl is an extension of {@link LocalPublisherImpl} that adds AMQP specific features. Specifically - * extra assertions for AMQP features not available through generic JMS. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - *
          - */ -public class LocalAMQPPublisherImpl extends LocalPublisherImpl implements AMQPPublisher -{ - /** - * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured - * for messages and exceptions received by the circuit end. - * - * @param producer The message producer for the circuit end point. - * @param consumer The message consumer for the circuit end point. - * @param session The controlSession for the circuit end point. - * @param messageMonitor The monitor to notify of all messages received by the circuit end. - * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end. - */ - public LocalAMQPPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session, - MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor) - { - super(producer, consumer, session, messageMonitor, exceptionMonitor); - } - - /** - * Creates a circuit end point from the producer, consumer and controlSession in a circuit end base implementation. - * - * @param end The circuit end base implementation to take producers and consumers from. - */ - public LocalAMQPPublisherImpl(CircuitEndBase end) - { - super(end); - } - - /** - * Provides an assertion that the publisher got a no consumers exception on every message. - * - * @param testProps The test configuration properties. - * - * @return An assertion that the publisher got a no consumers exception on every message. - */ - public Assertion noConsumersAssertion(ParsedProperties testProps) - { - return new AssertionBase() - { - public boolean apply() - { - boolean passed = true; - ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); - - if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoConsumersException.class)) - { - passed = false; - - addError("Was expecting linked exception type " + AMQNoConsumersException.class.getName() - + " on the connection.\n"); - addError((connectionExceptionMonitor.size() > 0) - ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) - : "Got no exceptions on the connection."); - } - - return passed; - } - }; - } - - /** - * Provides an assertion that the publisher got a no rout exception on every message. - * - * @param testProps The test configuration properties. - * - * @return An assertion that the publisher got a no rout exception on every message. - */ - public Assertion noRouteAssertion(ParsedProperties testProps) - { - return new AssertionBase() - { - public boolean apply() - { - boolean passed = true; - ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); - - if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class)) - { - passed = false; - - addError("Was expecting linked exception type " + AMQNoRouteException.class.getName() - + " on the connection.\n"); - addError((connectionExceptionMonitor.size() > 0) - ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) - : "Got no exceptions on the connection."); - } - - return passed; - } - }; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework.localcircuit; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.*; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; + +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +/** + * LocalAMQPPublisherImpl is an extension of {@link LocalPublisherImpl} that adds AMQP specific features. Specifically + * extra assertions for AMQP features not available through generic JMS. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + *
          + */ +public class LocalAMQPPublisherImpl extends LocalPublisherImpl implements AMQPPublisher +{ + /** + * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured + * for messages and exceptions received by the circuit end. + * + * @param producer The message producer for the circuit end point. + * @param consumer The message consumer for the circuit end point. + * @param session The controlSession for the circuit end point. + * @param messageMonitor The monitor to notify of all messages received by the circuit end. + * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end. + */ + public LocalAMQPPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session, + MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor) + { + super(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Creates a circuit end point from the producer, consumer and controlSession in a circuit end base implementation. + * + * @param end The circuit end base implementation to take producers and consumers from. + */ + public LocalAMQPPublisherImpl(CircuitEndBase end) + { + super(end); + } + + /** + * Provides an assertion that the publisher got a no consumers exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no consumers exception on every message. + */ + public Assertion noConsumersAssertion(ParsedProperties testProps) + { + return new AssertionBase() + { + public boolean apply() + { + boolean passed = true; + ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); + + if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoConsumersException.class)) + { + passed = false; + + addError("Was expecting linked exception type " + AMQNoConsumersException.class.getName() + + " on the connection.\n"); + addError((connectionExceptionMonitor.size() > 0) + ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) + : "Got no exceptions on the connection."); + } + + return passed; + } + }; + } + + /** + * Provides an assertion that the publisher got a no rout exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no rout exception on every message. + */ + public Assertion noRouteAssertion(ParsedProperties testProps) + { + return new AssertionBase() + { + public boolean apply() + { + boolean passed = true; + ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); + + if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class)) + { + passed = false; + + addError("Was expecting linked exception type " + AMQNoRouteException.class.getName() + + " on the connection.\n"); + addError((connectionExceptionMonitor.size() > 0) + ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) + : "Got no exceptions on the connection."); + } + + return passed; + } + }; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java index 4545e3c164..c11f75e742 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java @@ -1,96 +1,96 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework.qpid; - -import junit.framework.Test; -import junit.framework.TestResult; - -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.LocalAMQPCircuitFactory; - -import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; - -/** - * AMQPFeatureDecorator applies decorations to {@link FrameworkBaseCase} tests, so that they may use Qpid/AMQP specific - * features, not available through JMS. For example, the immediate and mandatory flags. This decorator replaces the - * standard test circuit factory on the base class with one that allows these features to be used. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Substitute the circuit factory with an AMQP/Qpid specific one. - *
          - * - * @todo This wrapper substitutes in a LocalAMQPCircuitFactory, which is fine for local tests. For distributed tests - * the Fanout or Interop factories are substituted in by their decorators instead. These actually use - * distributed circuit static create methods to build the circuits, which should actually be changed to a factory, - * so that static methods do not need to be used. The distributed circuit creater delegates the circuit - * construction to remote test nodes. This decorator should not be used with distributed tests, or should be made - * aware of them, in which case it might ensure that an AMQP feature (implied already by other properties) flag - * is passed out to the remote test nodes, and provide a mechansim for them to decorate their circuit creation - * with AMQP features too. Add factory substituion/decoration mechansim for test clients, here or in a seperate - * class. - */ -public class AMQPFeatureDecorator extends WrappedSuiteTestDecorator -{ - /** The test suite to run. */ - private Test test; - - /** - * Creates a wrapped test test decorator from another one. - * - * @param test The test test. - */ - public AMQPFeatureDecorator(WrappedSuiteTestDecorator test) - { - super(test); - this.test = test; - } - - /** - * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated. - * - * @param testResult The the results object to monitor the test results with. - */ - public void run(TestResult testResult) - { - for (Test test : getAllUnderlyingTests()) - { - if (test instanceof FrameworkBaseCase) - { - FrameworkBaseCase frameworkTest = (FrameworkBaseCase) test; - frameworkTest.setCircuitFactory(new LocalAMQPCircuitFactory()); - } - } - - // Run the test. - test.run(testResult); - } - - /** - * Prints the name of the test for debugging purposes. - * - * @return The name of the test. - */ - public String toString() - { - return "AMQPFeatureDecorator: [test = \"" + test + "\"]"; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.LocalAMQPCircuitFactory; + +import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; + +/** + * AMQPFeatureDecorator applies decorations to {@link FrameworkBaseCase} tests, so that they may use Qpid/AMQP specific + * features, not available through JMS. For example, the immediate and mandatory flags. This decorator replaces the + * standard test circuit factory on the base class with one that allows these features to be used. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Substitute the circuit factory with an AMQP/Qpid specific one. + *
          + * + * @todo This wrapper substitutes in a LocalAMQPCircuitFactory, which is fine for local tests. For distributed tests + * the Fanout or Interop factories are substituted in by their decorators instead. These actually use + * distributed circuit static create methods to build the circuits, which should actually be changed to a factory, + * so that static methods do not need to be used. The distributed circuit creater delegates the circuit + * construction to remote test nodes. This decorator should not be used with distributed tests, or should be made + * aware of them, in which case it might ensure that an AMQP feature (implied already by other properties) flag + * is passed out to the remote test nodes, and provide a mechansim for them to decorate their circuit creation + * with AMQP features too. Add factory substituion/decoration mechansim for test clients, here or in a seperate + * class. + */ +public class AMQPFeatureDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test test decorator from another one. + * + * @param test The test test. + */ + public AMQPFeatureDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + for (Test test : getAllUnderlyingTests()) + { + if (test instanceof FrameworkBaseCase) + { + FrameworkBaseCase frameworkTest = (FrameworkBaseCase) test; + frameworkTest.setCircuitFactory(new LocalAMQPCircuitFactory()); + } + } + + // Run the test. + test.run(testResult); + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "AMQPFeatureDecorator: [test = \"" + test + "\"]"; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java index 3a048ac042..2708253d86 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java @@ -1,95 +1,95 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework.qpid; - -import junit.framework.Test; -import junit.framework.TestResult; - -import org.apache.qpid.test.framework.BrokerLifecycleAware; -import org.apache.qpid.test.framework.CauseFailureUserPrompt; - -import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; - -/** - * CauseFailureDecorator applies decorations to {@link BrokerLifecycleAware} tests, so that they may use different failure - * mechanisms. It is capable of detecting when a test case uses in-vm brokers, and setting up an automatic failure - * for those tests, so that the current live broker can be shut-down by test cases. For external brokers, automatic - * failure could be implemented, for example by having a kill script. At the moment this sets up the failure to prompt - * a user interactively to cause a failure, using {@link CauseFailureUserPrompt}. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Setup automatic failures for in-vm brokers. {@link CauseFailureInVM} - *
          Setup user generated failures for external brokers. {@link CauseFailureUserPrompt}. - *
          - *
          - * - * @todo Slight problem in that CauseFailureInVM is Qpid specific, whereas CauseFailureUserPrompt is not. Would like the - * failure decorator to be non-qpid specific so that it can test failure of any JMS implementation too. Either pass - * in class name of failure mechanism, set it up in the in-vm decorator instead of here but with prompt user as the - * default for when the in-vm decorator is not used? - */ -public class CauseFailureDecorator extends WrappedSuiteTestDecorator -{ - /** The test suite to run. */ - private Test test; - - /** - * Creates a wrapped test test decorator from another one. - * - * @param test The test test. - */ - public CauseFailureDecorator(WrappedSuiteTestDecorator test) - { - super(test); - this.test = test; - } - - /** - * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated. - * - * @param testResult The the results object to monitor the test results with. - */ - public void run(TestResult testResult) - { - for (Test test : getAllUnderlyingTests()) - { - if (test instanceof BrokerLifecycleAware) - { - BrokerLifecycleAware failureTest = (BrokerLifecycleAware) test; - failureTest.setFailureMechanism(new CauseFailureUserPrompt()); - } - } - - // Run the test. - test.run(testResult); - } - - /** - * Prints the name of the test for debugging purposes. - * - * @return The name of the test. - */ - public String toString() - { - return "CauseFailureDecorator: [test = \"" + test + "\"]"; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.qpid.test.framework.BrokerLifecycleAware; +import org.apache.qpid.test.framework.CauseFailureUserPrompt; + +import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; + +/** + * CauseFailureDecorator applies decorations to {@link BrokerLifecycleAware} tests, so that they may use different failure + * mechanisms. It is capable of detecting when a test case uses in-vm brokers, and setting up an automatic failure + * for those tests, so that the current live broker can be shut-down by test cases. For external brokers, automatic + * failure could be implemented, for example by having a kill script. At the moment this sets up the failure to prompt + * a user interactively to cause a failure, using {@link CauseFailureUserPrompt}. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Setup automatic failures for in-vm brokers. {@link CauseFailureInVM} + *
          Setup user generated failures for external brokers. {@link CauseFailureUserPrompt}. + *
          + *
          + * + * @todo Slight problem in that CauseFailureInVM is Qpid specific, whereas CauseFailureUserPrompt is not. Would like the + * failure decorator to be non-qpid specific so that it can test failure of any JMS implementation too. Either pass + * in class name of failure mechanism, set it up in the in-vm decorator instead of here but with prompt user as the + * default for when the in-vm decorator is not used? + */ +public class CauseFailureDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test test decorator from another one. + * + * @param test The test test. + */ + public CauseFailureDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with a LocalAMQPCircuitFactory. Only tests that extend FrameworkBaseCase are decorated. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + for (Test test : getAllUnderlyingTests()) + { + if (test instanceof BrokerLifecycleAware) + { + BrokerLifecycleAware failureTest = (BrokerLifecycleAware) test; + failureTest.setFailureMechanism(new CauseFailureUserPrompt()); + } + } + + // Run the test. + test.run(testResult); + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "CauseFailureDecorator: [test = \"" + test + "\"]"; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java index b63ac43601..3e03ad0872 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java @@ -1,70 +1,70 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework.qpid; - -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.test.framework.CauseFailure; -import org.apache.qpid.test.framework.BrokerLifecycleAware; - -/** - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Cause messaging broker failure on the active in-vm broker. - * {@link TransportConnection}, {@link ApplicationRegistry} - *
          - */ -public class CauseFailureInVM implements CauseFailure -{ - /** Holds the in-vm broker instrumented test case to create failures for. */ - private BrokerLifecycleAware inVMTest; - - /** - * Creates an automated failure mechanism for testing against in-vm brokers. The test to create the mechanism - * for is specified, and as this failure is for in-vm brokers, the test must be {@link org.apache.qpid.test.framework.BrokerLifecycleAware}. The test - * must also report that it is currently being run against an in-vm broker, and it is a runtime error if it is not, - * as the creator of this failure mechanism should already have checked that it is. - * - * @param inVMTest The test case to create an automated failure mechanism for. - */ - public CauseFailureInVM(BrokerLifecycleAware inVMTest) - { - // Check that the test is really using in-vm brokers. - if (!inVMTest.usingInVmBroker()) - { - throw new RuntimeException( - "Cannot create in-vm broker failure mechanism for a test that is not using in-vm brokers."); - } - - this.inVMTest = inVMTest; - } - - /** - * Causes the active message broker to fail. - */ - public void causeFailure() - { - int liveBroker = inVMTest.getLiveBroker(); - - TransportConnection.killVMBroker(liveBroker); - ApplicationRegistry.remove(liveBroker); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework.qpid; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.test.framework.CauseFailure; +import org.apache.qpid.test.framework.BrokerLifecycleAware; + +/** + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Cause messaging broker failure on the active in-vm broker. + * {@link TransportConnection}, {@link ApplicationRegistry} + *
          + */ +public class CauseFailureInVM implements CauseFailure +{ + /** Holds the in-vm broker instrumented test case to create failures for. */ + private BrokerLifecycleAware inVMTest; + + /** + * Creates an automated failure mechanism for testing against in-vm brokers. The test to create the mechanism + * for is specified, and as this failure is for in-vm brokers, the test must be {@link org.apache.qpid.test.framework.BrokerLifecycleAware}. The test + * must also report that it is currently being run against an in-vm broker, and it is a runtime error if it is not, + * as the creator of this failure mechanism should already have checked that it is. + * + * @param inVMTest The test case to create an automated failure mechanism for. + */ + public CauseFailureInVM(BrokerLifecycleAware inVMTest) + { + // Check that the test is really using in-vm brokers. + if (!inVMTest.usingInVmBroker()) + { + throw new RuntimeException( + "Cannot create in-vm broker failure mechanism for a test that is not using in-vm brokers."); + } + + this.inVMTest = inVMTest; + } + + /** + * Causes the active message broker to fail. + */ + public void causeFailure() + { + int liveBroker = inVMTest.getLiveBroker(); + + TransportConnection.killVMBroker(liveBroker); + ApplicationRegistry.remove(liveBroker); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java index bcf052ea06..e0fddb10b7 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java @@ -1,135 +1,135 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.framework.qpid; - -import junit.framework.Test; -import junit.framework.TestResult; - -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.test.framework.BrokerLifecycleAware; -import org.apache.qpid.test.framework.FrameworkBaseCase; - -import org.apache.qpid.junit.extensions.SetupTaskAware; -import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; - -/** - * InVMBrokerDecorator is a test decorator, that is activated when running tests against an in-vm broker only. Its - * purpose is to automatically create, and close and delete an in-vm broker, during the set-up and tear-down of - * each test case. This decorator may only be used in conjunction with tests that extend {@link FrameworkBaseCase}. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Create/Destroy an in-vm broker on every test run. - *
          - * - * @todo May need to add a more fine grained injection point for the in-vm broker management, as this acts at the - * suite level, rather than the individual test level. - * - * @todo Management of in-vm brokers for failure testing. Failure test setups may need to set their connection url to - * use multiple broker (vm://:1;vm://:2), with fail-over between them. There is round-robin fail-over, but also - * retry? A test case using an in-vm broker needs to record which one it is using, so that it can be - * killed/restarted. - */ -public class InVMBrokerDecorator extends WrappedSuiteTestDecorator -{ - /** The test suite to run. */ - private Test test; - - /** - * Creates a wrapped test suite decorator from another one. - * - * @param test The test suite. - */ - public InVMBrokerDecorator(WrappedSuiteTestDecorator test) - { - super(test); - this.test = test; - } - - /** - * Runs the tests with in-vm broker creation and clean-up added to the tests task stack. - * - * @param testResult The the results object to monitor the test results with. - */ - public void run(TestResult testResult) - { - for (Test test : getAllUnderlyingTests()) - { - // Check that the test to have an in-vm broker setup/teardown task added to it, is actually a framework - // test that can handle setup tasks. - if ((test instanceof SetupTaskAware)) - { - SetupTaskAware frameworkTest = (SetupTaskAware) test; - - frameworkTest.chainSetupTask(new Runnable() - { - public void run() - { - // Ensure that the in-vm broker is created. - try - { - TransportConnection.createVMBroker(1); - } - catch (AMQVMBrokerCreationException e) - { - throw new RuntimeException("In-VM broker creation failed: " + e.getMessage(), e); - } - } - }); - - frameworkTest.chainTearDownTask(new Runnable() - { - public void run() - { - // Ensure that the in-vm broker is cleaned up so that the next test starts afresh. - TransportConnection.killVMBroker(1); - ApplicationRegistry.remove(1); - } - }); - - // Check if the test is aware whether or not it can control the broker life cycle, and if so provide - // additional instrumentation for it to control the in-vm broker through. - if (test instanceof BrokerLifecycleAware) - { - BrokerLifecycleAware inVMTest = (BrokerLifecycleAware) test; - inVMTest.setInVmBrokers(); - inVMTest.setLiveBroker(1); - inVMTest.setFailureMechanism(new CauseFailureInVM(inVMTest)); - } - } - } - - // Run the test. - test.run(testResult); - } - - /** - * Prints the name of the test for debugging purposes. - * - * @return The name of the test. - */ - public String toString() - { - return "InVMBrokerDecorator: [test = " + test + "]"; - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.test.framework.BrokerLifecycleAware; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import org.apache.qpid.junit.extensions.SetupTaskAware; +import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator; + +/** + * InVMBrokerDecorator is a test decorator, that is activated when running tests against an in-vm broker only. Its + * purpose is to automatically create, and close and delete an in-vm broker, during the set-up and tear-down of + * each test case. This decorator may only be used in conjunction with tests that extend {@link FrameworkBaseCase}. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Create/Destroy an in-vm broker on every test run. + *
          + * + * @todo May need to add a more fine grained injection point for the in-vm broker management, as this acts at the + * suite level, rather than the individual test level. + * + * @todo Management of in-vm brokers for failure testing. Failure test setups may need to set their connection url to + * use multiple broker (vm://:1;vm://:2), with fail-over between them. There is round-robin fail-over, but also + * retry? A test case using an in-vm broker needs to record which one it is using, so that it can be + * killed/restarted. + */ +public class InVMBrokerDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test suite decorator from another one. + * + * @param test The test suite. + */ + public InVMBrokerDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with in-vm broker creation and clean-up added to the tests task stack. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + for (Test test : getAllUnderlyingTests()) + { + // Check that the test to have an in-vm broker setup/teardown task added to it, is actually a framework + // test that can handle setup tasks. + if ((test instanceof SetupTaskAware)) + { + SetupTaskAware frameworkTest = (SetupTaskAware) test; + + frameworkTest.chainSetupTask(new Runnable() + { + public void run() + { + // Ensure that the in-vm broker is created. + try + { + TransportConnection.createVMBroker(1); + } + catch (AMQVMBrokerCreationException e) + { + throw new RuntimeException("In-VM broker creation failed: " + e.getMessage(), e); + } + } + }); + + frameworkTest.chainTearDownTask(new Runnable() + { + public void run() + { + // Ensure that the in-vm broker is cleaned up so that the next test starts afresh. + TransportConnection.killVMBroker(1); + ApplicationRegistry.remove(1); + } + }); + + // Check if the test is aware whether or not it can control the broker life cycle, and if so provide + // additional instrumentation for it to control the in-vm broker through. + if (test instanceof BrokerLifecycleAware) + { + BrokerLifecycleAware inVMTest = (BrokerLifecycleAware) test; + inVMTest.setInVmBrokers(); + inVMTest.setLiveBroker(1); + inVMTest.setFailureMechanism(new CauseFailureInVM(inVMTest)); + } + } + } + + // Run the test. + test.run(testResult); + } + + /** + * Prints the name of the test for debugging purposes. + * + * @return The name of the test. + */ + public String toString() + { + return "InVMBrokerDecorator: [test = " + test + "]"; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java index e7d874ffa9..5f41a07949 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java @@ -1,119 +1,119 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.testcases; - -import org.apache.qpid.test.framework.*; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; -import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import javax.jms.JMSException; -import javax.jms.MessageProducer; -import javax.jms.Session; - -/** - * FailoverTest provides testing of fail-over over a local-circuit implementation. The circuit being tested may be - * against an in-vm broker or against an external broker, with the failure mechanism abstracted out of the test case. - * Automatic failures can be simulated against an in-vm broker. Currently the test must interact with the user to - * simulate failures on an external broker. - * - * Things to test: - * In tx, failure duing tx causes tx to error on subsequent sends/receives or commits/rollbacks. - * Outside of tx, reconnection allows msg flow to continue but there may be loss. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - *
          - * - * @todo This test is designed to be run over a local circuit only. For in-vm using automatic failures, for external - * brokers by prompting the user (or maybe using a script). Enforce the local-circuit only nature of the tests as - * well as thinking about how other local-circuit tests might be implemented. For example, could add a method - * to the framework base case for local only tests to call, that allows them access to the local-circuit - * implementation and so on. - * - * @todo More. Need to really expand the set of fail-over tests. - */ -public class FailoverTest extends FrameworkBaseCase -{ - /* Used for debugging purposes. */ - // private static final Logger log = Logger.getLogger(FailoverTest.class); - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public FailoverTest(String name) - { - super(name); - } - - /** - * Checks that all messages sent within a transaction are receieved despite a fail-over occuring outside of - * the transaction. - * - * @throws JMSException Allowed to fall through and fail test. - */ - public void testTxP2PFailover() throws JMSException - { - // Set up the test properties to match the test cases requirements. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // MessagingTestConfigProperties props = this.getTestParameters(); - - // Create the test circuit from the test configuration parameters. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Create an assertion that all messages are received. - Assertion allMessagesReceived = testCircuit.getReceiver().allMessagesReceivedAssertion(testProps); - - // This test case assumes it is using a local circuit. - LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit; - - Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession(); - MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer(); - // MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer(); - - // Send some test messages. - for (int i = 0; i < 100; i++) - { - producer.send(TestUtils.createTestMessageOfSize(producerSession, 10)); - producerSession.commit(); - - // Cause a failover. - if (i == 50) - { - failureMechanism.causeFailure(); - } - - // Wait for the reconnection to complete. - } - - // Check that trying to send within the original transaction fails. - - // Check that all messages sent were received. - assertTrue("All messages sent were not received back again.", allMessagesReceived.apply()); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.*; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +/** + * FailoverTest provides testing of fail-over over a local-circuit implementation. The circuit being tested may be + * against an in-vm broker or against an external broker, with the failure mechanism abstracted out of the test case. + * Automatic failures can be simulated against an in-vm broker. Currently the test must interact with the user to + * simulate failures on an external broker. + * + * Things to test: + * In tx, failure duing tx causes tx to error on subsequent sends/receives or commits/rollbacks. + * Outside of tx, reconnection allows msg flow to continue but there may be loss. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + *
          + * + * @todo This test is designed to be run over a local circuit only. For in-vm using automatic failures, for external + * brokers by prompting the user (or maybe using a script). Enforce the local-circuit only nature of the tests as + * well as thinking about how other local-circuit tests might be implemented. For example, could add a method + * to the framework base case for local only tests to call, that allows them access to the local-circuit + * implementation and so on. + * + * @todo More. Need to really expand the set of fail-over tests. + */ +public class FailoverTest extends FrameworkBaseCase +{ + /* Used for debugging purposes. */ + // private static final Logger log = Logger.getLogger(FailoverTest.class); + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public FailoverTest(String name) + { + super(name); + } + + /** + * Checks that all messages sent within a transaction are receieved despite a fail-over occuring outside of + * the transaction. + * + * @throws JMSException Allowed to fall through and fail test. + */ + public void testTxP2PFailover() throws JMSException + { + // Set up the test properties to match the test cases requirements. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // MessagingTestConfigProperties props = this.getTestParameters(); + + // Create the test circuit from the test configuration parameters. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Create an assertion that all messages are received. + Assertion allMessagesReceived = testCircuit.getReceiver().allMessagesReceivedAssertion(testProps); + + // This test case assumes it is using a local circuit. + LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit; + + Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession(); + MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer(); + // MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer(); + + // Send some test messages. + for (int i = 0; i < 100; i++) + { + producer.send(TestUtils.createTestMessageOfSize(producerSession, 10)); + producerSession.commit(); + + // Cause a failover. + if (i == 50) + { + failureMechanism.causeFailure(); + } + + // Wait for the reconnection to complete. + } + + // Check that trying to send within the original transaction fails. + + // Check that all messages sent were received. + assertTrue("All messages sent were not received back again.", allMessagesReceived.apply()); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java index 845c3ed9c8..767871e25a 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java @@ -1,303 +1,303 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.testcases; - -import org.apache.qpid.test.framework.AMQPPublisher; -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -/** - * ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS - * feature. A message may be marked with an immediate delivery flag, which means that a consumer must be connected - * to receive the message, through a valid route, when it is sent, or when its transaction is committed in the case - * of transactional messaging. If this is not the case, the broker should return the message with a NO_CONSUMERS code. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Check that an immediate message is sent succesfully not using transactions when a consumer is connected. - *
          Check that an immediate message is committed succesfully in a transaction when a consumer is connected. - *
          Check that an immediate message results in no consumers code, not using transactions, when a consumer is - * disconnected. - *
          Check that an immediate message results in no consumers code, in a transaction, when a consumer is - * disconnected. - *
          Check that an immediate message results in no route code, not using transactions, when no outgoing route is - * connected. - *
          Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is - * connected. - *
          Check that an immediate message is sent succesfully not using transactions when a consumer is connected. - *
          Check that an immediate message is committed succesfully in a transaction when a consumer is connected. - *
          Check that an immediate message results in no consumers code, not using transactions, when a consumer is - * disconnected. - *
          Check that an immediate message results in no consumers code, in a transaction, when a consumer is - * disconnected. - *
          Check that an immediate message results in no route code, not using transactions, when no outgoing route is - * connected. - *
          Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is - * connected. - *
          - * - * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test - * circuits. - */ -public class ImmediateMessageTest extends FrameworkBaseCase -{ - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public ImmediateMessageTest(String name) - { - super(name); - } - - /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_517_ImmediateOkNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); - } - - /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_517_ImmediateOkTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Send one message with no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); - } - - /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); - } - - /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); - } - - /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_517_ImmediateOkNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Send one message with no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_517_ImmediateOkTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Send one message with no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); - } - - /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); - } - - /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - protected void setUp() throws Exception - { - super.setUp(); - - testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** All these tests should have the immediate flag on. */ - testProps.setProperty(IMMEDIATE_PROPNAME, true); - testProps.setProperty(MANDATORY_PROPNAME, false); - - /** Bind the receivers consumer by default. */ - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.AMQPPublisher; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +/** + * ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS + * feature. A message may be marked with an immediate delivery flag, which means that a consumer must be connected + * to receive the message, through a valid route, when it is sent, or when its transaction is committed in the case + * of transactional messaging. If this is not the case, the broker should return the message with a NO_CONSUMERS code. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Check that an immediate message is sent succesfully not using transactions when a consumer is connected. + *
          Check that an immediate message is committed succesfully in a transaction when a consumer is connected. + *
          Check that an immediate message results in no consumers code, not using transactions, when a consumer is + * disconnected. + *
          Check that an immediate message results in no consumers code, in a transaction, when a consumer is + * disconnected. + *
          Check that an immediate message results in no route code, not using transactions, when no outgoing route is + * connected. + *
          Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is + * connected. + *
          Check that an immediate message is sent succesfully not using transactions when a consumer is connected. + *
          Check that an immediate message is committed succesfully in a transaction when a consumer is connected. + *
          Check that an immediate message results in no consumers code, not using transactions, when a consumer is + * disconnected. + *
          Check that an immediate message results in no consumers code, in a transaction, when a consumer is + * disconnected. + *
          Check that an immediate message results in no route code, not using transactions, when no outgoing route is + * connected. + *
          Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is + * connected. + *
          + * + * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test + * circuits. + */ +public class ImmediateMessageTest extends FrameworkBaseCase +{ + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public ImmediateMessageTest(String name) + { + super(name); + } + + /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_517_ImmediateOkNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_517_ImmediateOkTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_517_ImmediateOkNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_517_ImmediateOkTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** All these tests should have the immediate flag on. */ + testProps.setProperty(IMMEDIATE_PROPNAME, true); + testProps.setProperty(MANDATORY_PROPNAME, false); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java index 066b4e24ba..d46a866b93 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java @@ -1,321 +1,321 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.testcases; - -import org.apache.qpid.test.framework.AMQPPublisher; -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -/** - * MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS - * feature. A message may be marked with a mandatory delivery flag, which means that a valid route for the message - * must exist, when it is sent, or when its transaction is committed in the case of transactional messaging. If this - * is not the case, the broker should return the message with a NO_CONSUMERS code. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. - *
          Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. - *
          Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected - * but the route exists. - *
          Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - *
          Check that an mandatory message results in no route code, not using transactions, when no consumer is - * connected. - *
          Check that an mandatory message results in no route code, upon transaction commit, when a consumer is - * connected. - *
          Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. - *
          Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. - *
          Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected - * but the route exists. - *
          Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - *
          Check that an mandatory message results in no route code, not using transactions, when no consumer is - * connected. - *
          Check that an mandatory message results in no route code, upon transaction commit, when a consumer is - * connected. - *
          - * - * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test - * circuits. - */ -public class MandatoryMessageTest extends FrameworkBaseCase -{ - /** Used to read the tests configurable properties through. */ - ParsedProperties testProps; - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public MandatoryMessageTest(String name) - { - super(name); - } - - /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_508_MandatoryOkNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_508_MandatoryOkTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** - * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** - * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_508_MandatoryOkNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_508_MandatoryOkTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** - * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** - * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); - } - - /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); - } - - protected void setUp() throws Exception - { - super.setUp(); - - testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** All these tests should have the mandatory flag on. */ - testProps.setProperty(IMMEDIATE_PROPNAME, false); - testProps.setProperty(MANDATORY_PROPNAME, true); - - /** Bind the receivers consumer by default. */ - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.AMQPPublisher; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +/** + * MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS + * feature. A message may be marked with a mandatory delivery flag, which means that a valid route for the message + * must exist, when it is sent, or when its transaction is committed in the case of transactional messaging. If this + * is not the case, the broker should return the message with a NO_CONSUMERS code. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. + *
          Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. + *
          Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected + * but the route exists. + *
          Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + *
          Check that an mandatory message results in no route code, not using transactions, when no consumer is + * connected. + *
          Check that an mandatory message results in no route code, upon transaction commit, when a consumer is + * connected. + *
          Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. + *
          Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. + *
          Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected + * but the route exists. + *
          Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + *
          Check that an mandatory message results in no route code, not using transactions, when no consumer is + * connected. + *
          Check that an mandatory message results in no route code, upon transaction commit, when a consumer is + * connected. + *
          + * + * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test + * circuits. + */ +public class MandatoryMessageTest extends FrameworkBaseCase +{ + /** Used to read the tests configurable properties through. */ + ParsedProperties testProps; + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public MandatoryMessageTest(String name) + { + super(name); + } + + /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_508_MandatoryOkNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_508_MandatoryOkTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_508_MandatoryOkNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_508_MandatoryOkTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** All these tests should have the mandatory flag on. */ + testProps.setProperty(IMMEDIATE_PROPNAME, false); + testProps.setProperty(MANDATORY_PROPNAME, true); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java index f39d22bc67..c1a484b2aa 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java @@ -1,132 +1,132 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.testcases; - -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import org.apache.qpid.junit.extensions.util.ParsedProperties; -import org.apache.qpid.junit.extensions.util.TestContextProperties; - -/** - * RollbackTest tests the rollback ability of transactional messaging. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          Check messages sent but rolled back are never received. - *
          Check messages received but rolled back are redelivered on subsequent receives. - *
          Attempting to rollback outside of a transaction results in an IllegalStateException. - *
          - */ -public class RollbackTest extends FrameworkBaseCase -{ - /** Used to read the tests configurable properties through. */ - ParsedProperties testProps; - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public RollbackTest(String name) - { - super(name); - } - - /** Check messages sent but rolled back are never received. */ - public void testRolledbackMessageNotDelivered() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); - testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), - testCircuit.getReceiver().noMessagesReceivedAssertion(testProps)))); - } - - /** Check messages received but rolled back are redelivered on subsequent receives. */ - public void testRolledbackMessagesSubsequentlyReceived() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, true); - testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, - assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), - testCircuit.getReceiver().allMessagesReceivedAssertion(testProps)))); - } - - /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ - public void testRollbackUnavailableOutsideTransactionPublisher() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); - testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().channelClosedAssertion(testProps)))); - } - - /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ - public void testRollbackUnavailableOutsideTransactionReceiver() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, false); - testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getReceiver().channelClosedAssertion(testProps)))); - } - - /** - * Sets up all tests to have an active outward route and consumer by default. - * - * @throws Exception Any exceptions are allowed to fall through and fail the test. - */ - protected void setUp() throws Exception - { - super.setUp(); - - testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** Bind the receivers consumer by default. */ - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import org.apache.qpid.junit.extensions.util.ParsedProperties; +import org.apache.qpid.junit.extensions.util.TestContextProperties; + +/** + * RollbackTest tests the rollback ability of transactional messaging. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          Check messages sent but rolled back are never received. + *
          Check messages received but rolled back are redelivered on subsequent receives. + *
          Attempting to rollback outside of a transaction results in an IllegalStateException. + *
          + */ +public class RollbackTest extends FrameworkBaseCase +{ + /** Used to read the tests configurable properties through. */ + ParsedProperties testProps; + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public RollbackTest(String name) + { + super(name); + } + + /** Check messages sent but rolled back are never received. */ + public void testRolledbackMessageNotDelivered() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), + testCircuit.getReceiver().noMessagesReceivedAssertion(testProps)))); + } + + /** Check messages received but rolled back are redelivered on subsequent receives. */ + public void testRolledbackMessagesSubsequentlyReceived() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, true); + testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), + testCircuit.getReceiver().allMessagesReceivedAssertion(testProps)))); + } + + /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ + public void testRollbackUnavailableOutsideTransactionPublisher() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().channelClosedAssertion(testProps)))); + } + + /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ + public void testRollbackUnavailableOutsideTransactionReceiver() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, false); + testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getReceiver().channelClosedAssertion(testProps)))); + } + + /** + * Sets up all tests to have an active outward route and consumer by default. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java index f752ccba00..9004cb72df 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java @@ -1,151 +1,151 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.test.testcases; - -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.ACK_MODE_PROPNAME; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.PUBSUB_PROPNAME; -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import javax.jms.*; - -import java.util.LinkedList; -import java.util.List; -import java.util.Random; - -/** - * TTLTest checks that time-to-live is applied to messages. The test sends messages with a variety of TTL stamps on them - * then after a pause attempts to receive those messages. Only messages with a large enough TTL to have survived the pause - * should be receiveable. This test case also applies an additional assertion against the broker, that the message store - * is empty at the end of the test. - * - *

          This test is designed to run over local circuits only, as it must control a timed pause between sending and receiving - * messages to that TTL can be applied to purge some of the messages. - * - *

          - *
          CRC Card
          Responsibilities Collaborations - *
          - *
          - * - * @todo Use an interface or other method to mark this test as local only. - * - * @todo Implement the message store assertion for in-vm broker. Could also be done for external broker, for example - * by using diagnostic exchange. - * - * @todo Implement and add a queue depth assertion too. This might already be in another test to copy from. - * - * @todo Create variations on test theme, for different ack mode and tx and message sizes etc. - * - * @todo Add an allowable margin of error to the test, as ttl may not be precise. - */ -public class TTLTest extends FrameworkBaseCase -{ - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public TTLTest(String name) - { - super(name); - } - - /** - * Checks that all messages sent with a TTL shorter than a pause between sending them and attempting to receive them - * will fail to arrive. Once all messages have been purged by TTL or received, check that they no longer exist on - * the broker. - * - * @throws javax.jms.JMSException Allowed to fall through and fail test. - */ - public void testTTLP2P() throws JMSException - { - String errorMessages = ""; - Random r = new Random(); - - // Used to accumulate correctly received messages in. - List receivedMessages = new LinkedList(); - - // Set up the test properties to match the test case requirements. - testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Create the test circuit from the test configuration parameters. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // This test case assumes it is using a local circuit. - LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit; - - Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession(); - MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer(); - MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer(); - - // Send some tests messages, with random TTLs, some shorter and some longer than the pause time. - for (int i = 0; i < 100; i++) - { - Message testMessage = TestUtils.createTestMessageOfSize(producerSession, 10); - - // Set the TTL on the message and record its value in the message headers. - long ttl = 500 + r.nextInt(1500); - producer.setTimeToLive(ttl); - testMessage.setLongProperty("testTTL", ttl); - - producer.send(testMessage); - // producerSession.commit(); - } - - // Inject a pause to allow some messages to be purged by TTL. - TestUtils.pause(1000); - - // Attempt to receive back all of the messages, confirming by the message time stamps and TTLs that only - // those received should have avoided being purged by the TTL. - boolean timedOut = false; - - while (!timedOut) - { - Message testMessage = consumer.receive(1000); - - long ttl = testMessage.getLongProperty("testTTL"); - long timeStamp = testMessage.getJMSTimestamp(); - long now = System.currentTimeMillis(); - - if ((timeStamp + ttl) < now) - { - errorMessages += - "Received message [sent: " + timeStamp + ", ttl: " + ttl + ", received: " + now - + "] which should have been purged by its TTL.\n"; - } - /*else - { - receivedMessages.add(testMessage); - }*/ - } - - // Check that the queue and message store on the broker are empty. - // assertTrue("Message store is not empty.", messageStoreEmpty.apply()); - // assertTrue("Queue is not empty.", queueEmpty.apply()); - - assertTrue(errorMessages, "".equals(errorMessages)); - } -} +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.ACK_MODE_PROPNAME; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.PUBSUB_PROPNAME; +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import javax.jms.*; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +/** + * TTLTest checks that time-to-live is applied to messages. The test sends messages with a variety of TTL stamps on them + * then after a pause attempts to receive those messages. Only messages with a large enough TTL to have survived the pause + * should be receiveable. This test case also applies an additional assertion against the broker, that the message store + * is empty at the end of the test. + * + *

          This test is designed to run over local circuits only, as it must control a timed pause between sending and receiving + * messages to that TTL can be applied to purge some of the messages. + * + *

          + *
          CRC Card
          Responsibilities Collaborations + *
          + *
          + * + * @todo Use an interface or other method to mark this test as local only. + * + * @todo Implement the message store assertion for in-vm broker. Could also be done for external broker, for example + * by using diagnostic exchange. + * + * @todo Implement and add a queue depth assertion too. This might already be in another test to copy from. + * + * @todo Create variations on test theme, for different ack mode and tx and message sizes etc. + * + * @todo Add an allowable margin of error to the test, as ttl may not be precise. + */ +public class TTLTest extends FrameworkBaseCase +{ + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public TTLTest(String name) + { + super(name); + } + + /** + * Checks that all messages sent with a TTL shorter than a pause between sending them and attempting to receive them + * will fail to arrive. Once all messages have been purged by TTL or received, check that they no longer exist on + * the broker. + * + * @throws javax.jms.JMSException Allowed to fall through and fail test. + */ + public void testTTLP2P() throws JMSException + { + String errorMessages = ""; + Random r = new Random(); + + // Used to accumulate correctly received messages in. + List receivedMessages = new LinkedList(); + + // Set up the test properties to match the test case requirements. + testProps.setProperty(ACK_MODE_PROPNAME, Session.AUTO_ACKNOWLEDGE); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Create the test circuit from the test configuration parameters. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // This test case assumes it is using a local circuit. + LocalCircuitImpl localCircuit = (LocalCircuitImpl) testCircuit; + + Session producerSession = localCircuit.getLocalPublisherCircuitEnd().getSession(); + MessageProducer producer = localCircuit.getLocalPublisherCircuitEnd().getProducer(); + MessageConsumer consumer = localCircuit.getLocalReceiverCircuitEnd().getConsumer(); + + // Send some tests messages, with random TTLs, some shorter and some longer than the pause time. + for (int i = 0; i < 100; i++) + { + Message testMessage = TestUtils.createTestMessageOfSize(producerSession, 10); + + // Set the TTL on the message and record its value in the message headers. + long ttl = 500 + r.nextInt(1500); + producer.setTimeToLive(ttl); + testMessage.setLongProperty("testTTL", ttl); + + producer.send(testMessage); + // producerSession.commit(); + } + + // Inject a pause to allow some messages to be purged by TTL. + TestUtils.pause(1000); + + // Attempt to receive back all of the messages, confirming by the message time stamps and TTLs that only + // those received should have avoided being purged by the TTL. + boolean timedOut = false; + + while (!timedOut) + { + Message testMessage = consumer.receive(1000); + + long ttl = testMessage.getLongProperty("testTTL"); + long timeStamp = testMessage.getJMSTimestamp(); + long now = System.currentTimeMillis(); + + if ((timeStamp + ttl) < now) + { + errorMessages += + "Received message [sent: " + timeStamp + ", ttl: " + ttl + ", received: " + now + + "] which should have been purged by its TTL.\n"; + } + /*else + { + receivedMessages.add(testMessage); + }*/ + } + + // Check that the queue and message store on the broker are empty. + // assertTrue("Message store is not empty.", messageStoreEmpty.apply()); + // assertTrue("Queue is not empty.", queueEmpty.apply()); + + assertTrue(errorMessages, "".equals(errorMessages)); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java b/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java index 398e3e7800..c43b65a805 100644 --- a/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java +++ b/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java @@ -1,296 +1,296 @@ -/* - * - * 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.testutil; - -import org.apache.log4j.Logger; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQConnectionFactory; -import org.apache.qpid.client.AMQConnectionURL; -import org.apache.qpid.client.JMSAMQException; -import org.apache.qpid.url.URLSyntaxException; - -import javax.jms.Connection; -import javax.jms.DeliveryMode; -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Queue; -import javax.jms.Session; -import javax.jms.TextMessage; - -/** - * @todo This was originally cut and paste from the client module leading to a duplicate class, then altered very - * slightly. To avoid the duplicate class the name was altered slightly to have 'Helper' on the end in order - * to distinguish it from the original. Delete this class and use the original instead, just upgrade it to - * provide the new features needed. - */ -public class QpidClientConnectionHelper implements ExceptionListener -{ - - private static final Logger _logger = Logger.getLogger(QpidClientConnectionHelper.class); - - private boolean transacted = true; - private int ackMode = Session.CLIENT_ACKNOWLEDGE; - private Connection connection; - - private String virtualHost; - private String brokerlist; - private int prefetch; - protected Session session; - protected boolean connected; - - public QpidClientConnectionHelper(String broker) - { - super(); - setVirtualHost("/test"); - setBrokerList(broker); - setPrefetch(5000); - } - - public void connect() throws JMSException - { - if (!connected) - { - /* - * amqp://[user:pass@][clientid]/virtualhost? - * brokerlist='[transport://]host[:port][?option='value'[&option='value']];' - * [&failover='method[?option='value'[&option='value']]'] - * [&option='value']" - */ - String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'"; - try - { - AMQConnectionFactory factory = new AMQConnectionFactory(new AMQConnectionURL(brokerUrl)); - _logger.info("connecting to Qpid :" + brokerUrl); - connection = factory.createConnection(); - - // register exception listener - connection.setExceptionListener(this); - - session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch); - - _logger.info("starting connection"); - connection.start(); - - connected = true; - } - catch (URLSyntaxException e) - { - throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e); - } - } - } - - public void disconnect() throws JMSException - { - if (connected) - { - session.commit(); - session.close(); - connection.close(); - connected = false; - _logger.info("disconnected"); - } - } - - public void disconnectWithoutCommit() throws JMSException - { - if (connected) - { - session.close(); - connection.close(); - connected = false; - _logger.info("disconnected without commit"); - } - } - - public String getBrokerList() - { - return brokerlist; - } - - public void setBrokerList(String brokerlist) - { - this.brokerlist = brokerlist; - } - - public String getVirtualHost() - { - return virtualHost; - } - - public void setVirtualHost(String virtualHost) - { - this.virtualHost = virtualHost; - } - - public void setPrefetch(int prefetch) - { - this.prefetch = prefetch; - } - - /** override as necessary */ - public void onException(JMSException exception) - { - _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage()); - } - - public boolean isConnected() - { - return connected; - } - - public Session getSession() - { - return session; - } - - /** - * Put a String as a text messages, repeat n times. A null payload will result in a null message. - * - * @param queueName The queue name to put to - * @param payload the content of the payload - * @param copies the number of messages to put - * - * @throws javax.jms.JMSException any exception that occurs - */ - public void put(String queueName, String payload, int copies, int deliveryMode) throws JMSException - { - if (!connected) - { - connect(); - } - - _logger.info("putting to queue " + queueName); - Queue queue = session.createQueue(queueName); - - final MessageProducer sender = session.createProducer(queue); - - sender.setDeliveryMode(deliveryMode); - - for (int i = 0; i < copies; i++) - { - Message m = session.createTextMessage(payload + i); - m.setIntProperty("index", i + 1); - sender.send(m); - } - - session.commit(); - sender.close(); - _logger.info("put " + copies + " copies"); - } - - /** - * GET the top message on a queue. Consumes the message. Accepts timeout value. - * - * @param queueName The quename to get from - * @param readTimeout The timeout to use - * - * @return the content of the text message if any - * - * @throws javax.jms.JMSException any exception that occured - */ - public Message getNextMessage(String queueName, long readTimeout) throws JMSException - { - if (!connected) - { - connect(); - } - - Queue queue = session.createQueue(queueName); - - final MessageConsumer consumer = session.createConsumer(queue); - - Message message = consumer.receive(readTimeout); - session.commit(); - consumer.close(); - - Message result; - - // all messages we consume should be TextMessages - if (message instanceof TextMessage) - { - result = ((TextMessage) message); - } - else if (null == message) - { - result = null; - } - else - { - _logger.info("warning: received non-text message"); - result = message; - } - - return result; - } - - /** - * GET the top message on a queue. Consumes the message. - * - * @param queueName The Queuename to get from - * - * @return The string content of the text message, if any received - * - * @throws javax.jms.JMSException any exception that occurs - */ - public Message getNextMessage(String queueName) throws JMSException - { - return getNextMessage(queueName, 0); - } - - /** - * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer. - * - * @param queueName The Queue name to consume from - * @param readTimeout The timeout for each consume - * - * @throws javax.jms.JMSException Any exception that occurs during the consume - * @throws InterruptedException If the consume thread was interrupted during a consume. - */ - public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException - { - if (!connected) - { - connect(); - } - - _logger.info("consuming queue " + queueName); - Queue queue = session.createQueue(queueName); - - final MessageConsumer consumer = session.createConsumer(queue); - int messagesReceived = 0; - - _logger.info("consuming..."); - while ((consumer.receive(readTimeout)) != null) - { - messagesReceived++; - } - - session.commit(); - consumer.close(); - _logger.info("consumed: " + messagesReceived); - } -} +/* + * + * 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.testutil; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.client.JMSAMQException; +import org.apache.qpid.url.URLSyntaxException; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * @todo This was originally cut and paste from the client module leading to a duplicate class, then altered very + * slightly. To avoid the duplicate class the name was altered slightly to have 'Helper' on the end in order + * to distinguish it from the original. Delete this class and use the original instead, just upgrade it to + * provide the new features needed. + */ +public class QpidClientConnectionHelper implements ExceptionListener +{ + + private static final Logger _logger = Logger.getLogger(QpidClientConnectionHelper.class); + + private boolean transacted = true; + private int ackMode = Session.CLIENT_ACKNOWLEDGE; + private Connection connection; + + private String virtualHost; + private String brokerlist; + private int prefetch; + protected Session session; + protected boolean connected; + + public QpidClientConnectionHelper(String broker) + { + super(); + setVirtualHost("/test"); + setBrokerList(broker); + setPrefetch(5000); + } + + public void connect() throws JMSException + { + if (!connected) + { + /* + * amqp://[user:pass@][clientid]/virtualhost? + * brokerlist='[transport://]host[:port][?option='value'[&option='value']];' + * [&failover='method[?option='value'[&option='value']]'] + * [&option='value']" + */ + String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'"; + try + { + AMQConnectionFactory factory = new AMQConnectionFactory(new AMQConnectionURL(brokerUrl)); + _logger.info("connecting to Qpid :" + brokerUrl); + connection = factory.createConnection(); + + // register exception listener + connection.setExceptionListener(this); + + session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch); + + _logger.info("starting connection"); + connection.start(); + + connected = true; + } + catch (URLSyntaxException e) + { + throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e); + } + } + } + + public void disconnect() throws JMSException + { + if (connected) + { + session.commit(); + session.close(); + connection.close(); + connected = false; + _logger.info("disconnected"); + } + } + + public void disconnectWithoutCommit() throws JMSException + { + if (connected) + { + session.close(); + connection.close(); + connected = false; + _logger.info("disconnected without commit"); + } + } + + public String getBrokerList() + { + return brokerlist; + } + + public void setBrokerList(String brokerlist) + { + this.brokerlist = brokerlist; + } + + public String getVirtualHost() + { + return virtualHost; + } + + public void setVirtualHost(String virtualHost) + { + this.virtualHost = virtualHost; + } + + public void setPrefetch(int prefetch) + { + this.prefetch = prefetch; + } + + /** override as necessary */ + public void onException(JMSException exception) + { + _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage()); + } + + public boolean isConnected() + { + return connected; + } + + public Session getSession() + { + return session; + } + + /** + * Put a String as a text messages, repeat n times. A null payload will result in a null message. + * + * @param queueName The queue name to put to + * @param payload the content of the payload + * @param copies the number of messages to put + * + * @throws javax.jms.JMSException any exception that occurs + */ + public void put(String queueName, String payload, int copies, int deliveryMode) throws JMSException + { + if (!connected) + { + connect(); + } + + _logger.info("putting to queue " + queueName); + Queue queue = session.createQueue(queueName); + + final MessageProducer sender = session.createProducer(queue); + + sender.setDeliveryMode(deliveryMode); + + for (int i = 0; i < copies; i++) + { + Message m = session.createTextMessage(payload + i); + m.setIntProperty("index", i + 1); + sender.send(m); + } + + session.commit(); + sender.close(); + _logger.info("put " + copies + " copies"); + } + + /** + * GET the top message on a queue. Consumes the message. Accepts timeout value. + * + * @param queueName The quename to get from + * @param readTimeout The timeout to use + * + * @return the content of the text message if any + * + * @throws javax.jms.JMSException any exception that occured + */ + public Message getNextMessage(String queueName, long readTimeout) throws JMSException + { + if (!connected) + { + connect(); + } + + Queue queue = session.createQueue(queueName); + + final MessageConsumer consumer = session.createConsumer(queue); + + Message message = consumer.receive(readTimeout); + session.commit(); + consumer.close(); + + Message result; + + // all messages we consume should be TextMessages + if (message instanceof TextMessage) + { + result = ((TextMessage) message); + } + else if (null == message) + { + result = null; + } + else + { + _logger.info("warning: received non-text message"); + result = message; + } + + return result; + } + + /** + * GET the top message on a queue. Consumes the message. + * + * @param queueName The Queuename to get from + * + * @return The string content of the text message, if any received + * + * @throws javax.jms.JMSException any exception that occurs + */ + public Message getNextMessage(String queueName) throws JMSException + { + return getNextMessage(queueName, 0); + } + + /** + * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer. + * + * @param queueName The Queue name to consume from + * @param readTimeout The timeout for each consume + * + * @throws javax.jms.JMSException Any exception that occurs during the consume + * @throws InterruptedException If the consume thread was interrupted during a consume. + */ + public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException + { + if (!connected) + { + connect(); + } + + _logger.info("consuming queue " + queueName); + Queue queue = session.createQueue(queueName); + + final MessageConsumer consumer = session.createConsumer(queue); + int messagesReceived = 0; + + _logger.info("consuming..."); + while ((consumer.receive(readTimeout)) != null) + { + messagesReceived++; + } + + session.commit(); + consumer.close(); + _logger.info("consumed: " + messagesReceived); + } +} -- cgit v1.2.1