summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorAidan Skinner <aidan@apache.org>2008-04-24 01:54:20 +0000
committerAidan Skinner <aidan@apache.org>2008-04-24 01:54:20 +0000
commit559e9702d5a7c26dddee708e267f2f685d4de89e (patch)
treeb3114d58b39092b4699186533a50553715e42b96 /java
parent04fe7be0efbc3687a5a302fea6fbec82adbfedae (diff)
downloadqpid-python-559e9702d5a7c26dddee708e267f2f685d4de89e.tar.gz
QPID-832 merge M2.x
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@651133 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java')
-rw-r--r--java/broker/pom.xml4
-rw-r--r--java/broker/src/main/grammar/SelectorParser.jj2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java4
-rw-r--r--java/client/build.xml8
-rw-r--r--java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java171
-rw-r--r--java/client/pom.xml24
-rw-r--r--java/client/src/main/grammar/SelectorParser.jj17
-rw-r--r--java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java478
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java5
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java19
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQConnection.java519
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java24
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java14
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java1
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQDestination.java23
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java2
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java48
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQSession.java580
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java42
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java16
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java323
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java3
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQTopic.java16
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java440
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java22
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java27
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java80
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java8
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java77
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java7
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java8
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java4
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java9
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java7
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java36
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java13
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java22
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java29
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java27
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java8
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java54
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java12
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java528
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java155
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java85
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java31
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java7
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java13
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java25
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java47
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java55
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java12
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java13
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java65
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java13
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java8
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java2
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java27
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java2
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java18
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java272
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java165
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java32
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/state/AMQState.java26
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java193
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java8
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java34
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java92
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java9
-rw-r--r--java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java2
-rw-r--r--java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java4
-rw-r--r--java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java12
-rw-r--r--java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java4
-rw-r--r--java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java52
-rw-r--r--java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java48
-rw-r--r--java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java2
-rw-r--r--java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java2
-rw-r--r--java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java4
-rw-r--r--java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java2
-rw-r--r--java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java6
-rw-r--r--java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java7
-rw-r--r--java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java24
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java12
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java187
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java (renamed from java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java)4
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java15
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java13
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java123
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java44
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java104
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java19
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java7
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java4
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java (renamed from java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTests.java)2
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java31
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java132
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java12
-rw-r--r--java/common/build.xml6
-rw-r--r--java/common/pom.xml12
-rw-r--r--java/common/protocol-version.xml22
-rw-r--r--java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java528
-rw-r--r--java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java228
-rw-r--r--java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java48
-rw-r--r--java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java272
-rw-r--r--java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java197
-rw-r--r--java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java440
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java547
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java486
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java67
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java1026
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java240
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java488
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java151
-rw-r--r--java/common/src/main/java/org/apache/qpid/AMQChannelException.java8
-rw-r--r--java/common/src/main/java/org/apache/qpid/AMQConnectionException.java13
-rw-r--r--java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java87
-rw-r--r--java/common/src/main/java/org/apache/qpid/AMQException.java31
-rw-r--r--java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java6
-rw-r--r--java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java6
-rw-r--r--java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java114
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQBody.java79
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java58
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java6
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java215
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java3
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java96
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java3
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java1165
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java31
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQType.java262
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java32
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java27
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/ContentBody.java21
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java10
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java4
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java50
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java19
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/FieldTable.java108
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java14
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java3
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java22
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java2
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java2
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java209
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java172
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java209
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java151
-rw-r--r--java/common/src/main/java/org/apache/qpid/pool/Job.java22
-rw-r--r--java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java105
-rw-r--r--java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java3
-rw-r--r--java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java15
-rw-r--r--java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java6
-rw-r--r--java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java8
-rw-r--r--java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java396
-rw-r--r--java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java157
-rw-r--r--java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java109
-rw-r--r--java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java82
-rw-r--r--java/common/templates/method/MethodBodyClass.tmpl183
-rw-r--r--java/common/templates/method/MethodBodyInterface.vm62
-rw-r--r--java/common/templates/method/version/MethodBodyClass.vm209
-rw-r--r--java/common/templates/model/ClientMethodDispatcherInterface.vm56
-rw-r--r--java/common/templates/model/MethodDispatcherInterface.vm39
-rw-r--r--java/common/templates/model/MethodRegistryClass.tmpl159
-rw-r--r--java/common/templates/model/MethodRegistryClass.vm104
-rw-r--r--java/common/templates/model/ProtocolVersionListClass.vm (renamed from java/common/templates/model/ProtocolVersionListClass.tmpl)69
-rw-r--r--java/common/templates/model/ServerMethodDispatcherInterface.vm56
-rw-r--r--java/common/templates/model/version/AmqpConstantsClass.vm (renamed from java/common/templates/model/AmqpConstantsClass.tmpl)0
-rw-r--r--java/common/templates/model/version/ClientMethodDispatcherInterface.vm55
-rw-r--r--java/common/templates/model/version/MethodDispatcherInterface.vm43
-rw-r--r--java/common/templates/model/version/MethodRegistryClass.vm193
-rw-r--r--java/common/templates/model/version/ServerMethodDispatcherInterface.vm55
-rw-r--r--java/pom.xml122
-rw-r--r--java/systests/build.xml2
-rw-r--r--java/systests/etc/bin/fail.py88
-rw-r--r--java/systests/pom.xml133
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java15
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java9
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java3
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/failover/FailoverMethodTest.java136
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/plugins/PluginTest.java54
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java36
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java25
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java8
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java8
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java18
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java46
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java35
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java135
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java607
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java15
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java11
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java20
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/FailoverBaseCase.java76
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java70
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/CancelTest.java111
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java160
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java510
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java49
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java49
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java50
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java53
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java152
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java51
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java257
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java54
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java70
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java42
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java65
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java11
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java79
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java204
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java20
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java48
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java168
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java316
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java167
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java268
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java112
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java40
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java48
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java88
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java11
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java5
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java7
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java7
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java8
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java36
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java48
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java21
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java87
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java3
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java5
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java497
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java133
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java192
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java60
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java54
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java96
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java95
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java70
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java135
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java10
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java14
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java6
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java14
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java119
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java303
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java321
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java132
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java151
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java174
-rw-r--r--java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java2
259 files changed, 20360 insertions, 3620 deletions
diff --git a/java/broker/pom.xml b/java/broker/pom.xml
index 153e186a26..53913f161f 100644
--- a/java/broker/pom.xml
+++ b/java/broker/pom.xml
@@ -22,14 +22,14 @@
<groupId>org.apache.qpid</groupId>
<artifactId>qpid-broker</artifactId>
<packaging>jar</packaging>
- <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <version>1.0-incubating-M3-SNAPSHOT</version>
<name>Qpid Broker</name>
<url>http://cwiki.apache.org/confluence/display/qpid</url>
<parent>
<groupId>org.apache.qpid</groupId>
<artifactId>qpid</artifactId>
- <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <version>1.0-incubating-M3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
diff --git a/java/broker/src/main/grammar/SelectorParser.jj b/java/broker/src/main/grammar/SelectorParser.jj
index f6a843e080..eb4467f1cc 100644
--- a/java/broker/src/main/grammar/SelectorParser.jj
+++ b/java/broker/src/main/grammar/SelectorParser.jj
@@ -94,7 +94,7 @@ public class SelectorParser {
return this.JmsSelector();
}
catch (Throwable e) {
- throw (AMQInvalidArgumentException)new AMQInvalidArgumentException(sql).initCause(e);
+ throw (AMQInvalidArgumentException)new AMQInvalidArgumentException(sql, e);
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
index 1a9dc6673a..636aa7eb03 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
@@ -67,7 +67,7 @@ public class DefaultExchangeFactory implements ExchangeFactory
if (exchType == null)
{
- throw new AMQUnknownExchangeType("Unknown exchange type: " + type);
+ throw new AMQUnknownExchangeType("Unknown exchange type: " + type, null);
}
Exchange e = exchType.newInstance(_host, exchange, durable, ticket, autoDelete);
return e;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java
index 9a98bc9659..97d25f650d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java
@@ -101,7 +101,7 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange
else if (!exchange.getType().equals(body.getType()))
{
- throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.getExchange() + " of type " + exchange.getType() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor());
+ throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.getExchange() + " of type " + exchange.getType() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor(), null);
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java
index dee1676632..126ff22d69 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java
@@ -37,9 +37,9 @@ public class AllowAll implements ACLPlugin
public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters)
{
- if (_logger.isDebugEnabled())
+ if (ACLManager.getLogger().isDebugEnabled())
{
- _logger.debug("Allowing user:" + session.getAuthorizedID() + " for :" + permission.toString()
+ ACLManager.getLogger().debug("Allowing user:" + session.getAuthorizedID() + " for :" + permission.toString()
+ " on " + body.getClass().getSimpleName()
+ (parameters == null || parameters.length == 0 ? "" : "-" + accessablesToString(parameters)));
}
diff --git a/java/client/build.xml b/java/client/build.xml
index 1fb980b908..aeadfa2f0f 100644
--- a/java/client/build.xml
+++ b/java/client/build.xml
@@ -21,7 +21,7 @@
<project name="AMQ Client" default="build">
<property name="module.depends" value="common"/>
- <property name="module.test.depends" value="broker"/>
+ <property name="module.test.depends" value="broker junit-toolkit"/>
<import file="../module.xml"/>
@@ -29,9 +29,9 @@
<target name="precompile">
<mkdir dir="${output.dir}"/>
- <exec executable="javacc">
- <arg line="-OUTPUT_DIRECTORY=${output.dir} src/main/grammar/SelectorParser.jj"/>
- </exec>
+ <javacc target="src/main/grammar/SelectorParser.jj"
+ outputdirectory="${output.dir}"
+ javacchome="${project.root}/lib"/>
</target>
</project>
diff --git a/java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java b/java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java
new file mode 100644
index 0000000000..d7eb138523
--- /dev/null
+++ b/java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java
@@ -0,0 +1,171 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.example.transport;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.SocketChannel;
+import java.util.UUID;
+
+/**
+ * This is a simple application that demonstrates how you can use the Qpid AMQP interfaces to use existing sockets as
+ * the transport for the Client API.
+ *
+ * The Demo here runs twice:
+ * 1. Just to show a simple publish and receive.
+ * 2. To demonstrate how to use existing sockets and utilise the underlying client failover mechnaism.
+ */
+public class ExistingSocketConnectorDemo implements ConnectionListener
+{
+ private static boolean DEMO_FAILOVER = false;
+
+ public static void main(String[] args) throws IOException, URLSyntaxException, AMQException, JMSException
+ {
+ System.out.println("Testing socket connection to localhost:5672.");
+
+ new ExistingSocketConnectorDemo();
+
+ System.out.println("Testing socket connection failover between localhost:5672 and localhost:5673.");
+
+ DEMO_FAILOVER = true;
+
+ new ExistingSocketConnectorDemo();
+ }
+
+ Connection _connection;
+ MessageProducer _producer;
+ Session _session;
+
+ String Socket1_ID = UUID.randomUUID().toString();
+ String Socket2_ID = UUID.randomUUID().toString();
+
+
+
+ /** Here we can see the broker we are connecting to is set to be 'socket:///' signifying we will provide the socket. */
+ public final String CONNECTION = "amqp://guest:guest@id/test?brokerlist='socket://" + Socket1_ID + ";socket://" + Socket2_ID + "'";
+
+
+ public ExistingSocketConnectorDemo() throws IOException, URLSyntaxException, AMQException, JMSException
+ {
+
+ Socket socket = SocketChannel.open().socket();
+ socket.connect(new InetSocketAddress("localhost", 5672));
+
+ TransportConnection.registerOpenSocket(Socket1_ID, socket);
+
+
+ _connection = new AMQConnection(CONNECTION);
+
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ MessageConsumer consumer = _session.createConsumer(_session.createQueue("Queue"));
+
+ _producer = _session.createProducer(_session.createQueue("Queue"));
+
+ _connection.start();
+
+ if (!DEMO_FAILOVER)
+ {
+ _producer.send(_session.createTextMessage("Simple Test"));
+ }
+ else
+ {
+ // Using the Qpid interfaces we can set a listener that allows us to demonstrate failover
+ ((AMQConnection) _connection).setConnectionListener(this);
+
+ System.out.println("Testing failover: Please ensure second broker running on localhost:5673 and shutdown broker on 5672.");
+ }
+
+ //We do a blocking receive here so that we can demonstrate failover.
+ Message message = consumer.receive();
+
+ System.out.println("Recevied :" + message);
+
+ _connection.close();
+ }
+
+ // ConnectionListener Interface
+
+ public void bytesSent(long count)
+ {
+ //not used in this example
+ }
+ public void bytesReceived(long count)
+ {
+ //not used in this example
+ }
+
+ public boolean preFailover(boolean redirect)
+ {
+ /**
+ * This method is called before the underlying client library starts to reconnect. This gives us the opportunity
+ * to set a new socket for the failover to occur on.
+ */
+ try
+ {
+ Socket socket = SocketChannel.open().socket();
+
+ socket.connect(new InetSocketAddress("localhost", 5673));
+
+ // This is the new method to pass in an open socket for the connection to use.
+ TransportConnection.registerOpenSocket(Socket2_ID, socket);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ public boolean preResubscribe()
+ {
+ //not used in this example - but must return true to allow the resubscription of existing clients.
+ return true;
+ }
+
+ public void failoverComplete()
+ {
+ // Now that failover has completed we can send a message that the receiving thread will pick up
+ try
+ {
+ _producer.send(_session.createTextMessage("Simple Failover Test"));
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/java/client/pom.xml b/java/client/pom.xml
index 07a3520f58..bcce3e1d3b 100644
--- a/java/client/pom.xml
+++ b/java/client/pom.xml
@@ -69,18 +69,8 @@
<artifactId>commons-lang</artifactId>
</dependency>
- <dependency>
- <groupId>org.apache.mina</groupId>
- <artifactId>mina-filter-ssl</artifactId>
- </dependency>
<!-- Test Dependencies -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.4.0</version>
- <scope>test</scope>
- </dependency>
<dependency> <!-- for inVm Broker -->
<groupId>org.apache.qpid</groupId>
@@ -89,19 +79,7 @@
</dependency>
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.easymock</groupId>
- <artifactId>easymockclassextension</artifactId>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit</artifactId>
<scope>test</scope>
</dependency>
diff --git a/java/client/src/main/grammar/SelectorParser.jj b/java/client/src/main/grammar/SelectorParser.jj
index 2dca11748e..a72da526ae 100644
--- a/java/client/src/main/grammar/SelectorParser.jj
+++ b/java/client/src/main/grammar/SelectorParser.jj
@@ -170,6 +170,8 @@ TOKEN [IGNORE_CASE] :
TOKEN [IGNORE_CASE] :
{
< ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* >
+ | < QUOTED_ID : "\"" ( ("\"\"") | ~["\""] )* "\"" >
+
}
// ----------------------------------------------------------------------------
@@ -577,6 +579,7 @@ String stringLitteral() :
PropertyExpression variable() :
{
Token t;
+ StringBuffer rc = new StringBuffer();
PropertyExpression left=null;
}
{
@@ -585,6 +588,20 @@ PropertyExpression variable() :
{
left = new PropertyExpression(t.image);
}
+ |
+ t = <QUOTED_ID>
+ {
+ // Decode the sting value.
+ String image = t.image;
+ for( int i=1; i < image.length()-1; i++ ) {
+ char c = image.charAt(i);
+ if( c == '"' )
+ i++;
+ rc.append(c);
+ }
+ return new PropertyExpression(rc.toString());
+ }
+
)
{
return left;
diff --git a/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java b/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java
new file mode 100644
index 0000000000..98716c0c3c
--- /dev/null
+++ b/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java
@@ -0,0 +1,478 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.socket.nio;
+
+import edu.emory.mathcs.backport.java.util.concurrent.Executor;
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.ExceptionMonitor;
+import org.apache.mina.common.IoConnector;
+import org.apache.mina.common.IoConnectorConfig;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.support.BaseIoConnector;
+import org.apache.mina.common.support.DefaultConnectFuture;
+import org.apache.mina.util.NamePreservingRunnable;
+import org.apache.mina.util.NewThreadExecutor;
+import org.apache.mina.util.Queue;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * {@link IoConnector} for socket transport (TCP/IP).
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 627427 $, $Date: 2008-02-13 14:39:10 +0000 (Wed, 13 Feb 2008) $
+ */
+public class ExistingSocketConnector extends BaseIoConnector
+{
+ /** @noinspection StaticNonFinalField */
+ private static volatile int nextId = 0;
+
+ private final Object lock = new Object();
+ private final int id = nextId++;
+ private final String threadName = "SocketConnector-" + id;
+ private SocketConnectorConfig defaultConfig = new SocketConnectorConfig();
+ private final Queue connectQueue = new Queue();
+ private final SocketIoProcessor[] ioProcessors;
+ private final int processorCount;
+ private final Executor executor;
+
+ /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */
+ private Selector selector;
+ private Worker worker;
+ private int processorDistributor = 0;
+ private int workerTimeout = 60; // 1 min.
+ private Socket _openSocket = null;
+
+ /** Create a connector with a single processing thread using a NewThreadExecutor */
+ public ExistingSocketConnector()
+ {
+ this(1, new NewThreadExecutor());
+ }
+
+ /**
+ * Create a connector with the desired number of processing threads
+ *
+ * @param processorCount Number of processing threads
+ * @param executor Executor to use for launching threads
+ */
+ public ExistingSocketConnector(int processorCount, Executor executor)
+ {
+ if (processorCount < 1)
+ {
+ throw new IllegalArgumentException("Must have at least one processor");
+ }
+
+ this.executor = executor;
+ this.processorCount = processorCount;
+ ioProcessors = new SocketIoProcessor[processorCount];
+
+ for (int i = 0; i < processorCount; i++)
+ {
+ ioProcessors[i] = new SocketIoProcessor("SocketConnectorIoProcessor-" + id + "." + i, executor);
+ }
+ }
+
+ /**
+ * How many seconds to keep the connection thread alive between connection requests
+ *
+ * @return Number of seconds to keep connection thread alive
+ */
+ public int getWorkerTimeout()
+ {
+ return workerTimeout;
+ }
+
+ /**
+ * Set how many seconds the connection worker thread should remain alive once idle before terminating itself.
+ *
+ * @param workerTimeout Number of seconds to keep thread alive. Must be >=0
+ */
+ public void setWorkerTimeout(int workerTimeout)
+ {
+ if (workerTimeout < 0)
+ {
+ throw new IllegalArgumentException("Must be >= 0");
+ }
+ this.workerTimeout = workerTimeout;
+ }
+
+ public ConnectFuture connect(SocketAddress address, IoHandler handler, IoServiceConfig config)
+ {
+ return connect(address, null, handler, config);
+ }
+
+ public ConnectFuture connect(SocketAddress address, SocketAddress localAddress,
+ IoHandler handler, IoServiceConfig config)
+ {
+ /** Changes here from the Mina OpenSocketConnector.
+ * Ignoreing all address as they are not needed */
+
+ if (handler == null)
+ {
+ throw new NullPointerException("handler");
+ }
+
+
+ if (config == null)
+ {
+ config = getDefaultConfig();
+ }
+
+ if (_openSocket == null)
+ {
+ throw new IllegalArgumentException("Specifed Socket not active");
+ }
+
+ boolean success = false;
+
+ try
+ {
+ DefaultConnectFuture future = new DefaultConnectFuture();
+ newSession(_openSocket, handler, config, future);
+ success = true;
+ return future;
+ }
+ catch (IOException e)
+ {
+ return DefaultConnectFuture.newFailedFuture(e);
+ }
+ finally
+ {
+ if (!success && _openSocket != null)
+ {
+ try
+ {
+ _openSocket.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ }
+ }
+ }
+
+ public IoServiceConfig getDefaultConfig()
+ {
+ return defaultConfig;
+ }
+
+ /**
+ * Sets the config this connector will use by default.
+ *
+ * @param defaultConfig the default config.
+ *
+ * @throws NullPointerException if the specified value is <code>null</code>.
+ */
+ public void setDefaultConfig(SocketConnectorConfig defaultConfig)
+ {
+ if (defaultConfig == null)
+ {
+ throw new NullPointerException("defaultConfig");
+ }
+ this.defaultConfig = defaultConfig;
+ }
+
+ private synchronized void startupWorker() throws IOException
+ {
+ if (worker == null)
+ {
+ selector = Selector.open();
+ worker = new Worker();
+ executor.execute(new NamePreservingRunnable(worker));
+ }
+ }
+
+ private void registerNew()
+ {
+ if (connectQueue.isEmpty())
+ {
+ return;
+ }
+
+ for (; ;)
+ {
+ ConnectionRequest req;
+ synchronized (connectQueue)
+ {
+ req = (ConnectionRequest) connectQueue.pop();
+ }
+
+ if (req == null)
+ {
+ break;
+ }
+
+ SocketChannel ch = req.channel;
+ try
+ {
+ ch.register(selector, SelectionKey.OP_CONNECT, req);
+ }
+ catch (IOException e)
+ {
+ req.setException(e);
+ }
+ }
+ }
+
+ private void processSessions(Set keys)
+ {
+ Iterator it = keys.iterator();
+
+ while (it.hasNext())
+ {
+ SelectionKey key = (SelectionKey) it.next();
+
+ if (!key.isConnectable())
+ {
+ continue;
+ }
+
+ SocketChannel ch = (SocketChannel) key.channel();
+ ConnectionRequest entry = (ConnectionRequest) key.attachment();
+
+ boolean success = false;
+ try
+ {
+ ch.finishConnect();
+ newSession(ch, entry.handler, entry.config, entry);
+ success = true;
+ }
+ catch (Throwable e)
+ {
+ entry.setException(e);
+ }
+ finally
+ {
+ key.cancel();
+ if (!success)
+ {
+ try
+ {
+ ch.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ }
+ }
+ }
+
+ keys.clear();
+ }
+
+ private void processTimedOutSessions(Set keys)
+ {
+ long currentTime = System.currentTimeMillis();
+ Iterator it = keys.iterator();
+
+ while (it.hasNext())
+ {
+ SelectionKey key = (SelectionKey) it.next();
+
+ if (!key.isValid())
+ {
+ continue;
+ }
+
+ ConnectionRequest entry = (ConnectionRequest) key.attachment();
+
+ if (currentTime >= entry.deadline)
+ {
+ entry.setException(new ConnectException());
+ try
+ {
+ key.channel().close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ finally
+ {
+ key.cancel();
+ }
+ }
+ }
+ }
+
+ private void newSession(Socket socket, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture)
+ throws IOException
+ {
+ SocketSessionImpl session = new SocketSessionImpl(this,
+ nextProcessor(),
+ getListeners(),
+ config,
+ socket.getChannel(),
+ handler,
+ socket.getRemoteSocketAddress());
+
+ newSession(session, config, connectFuture);
+ }
+
+ private void newSession(SocketChannel ch, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture)
+ throws IOException
+
+ {
+ SocketSessionImpl session = new SocketSessionImpl(this,
+ nextProcessor(),
+ getListeners(),
+ config,
+ ch,
+ handler,
+ ch.socket().getRemoteSocketAddress());
+
+ newSession(session, config, connectFuture);
+ }
+
+ private void newSession(SocketSessionImpl session, IoServiceConfig config, ConnectFuture connectFuture)
+ throws IOException
+ {
+ try
+ {
+ getFilterChainBuilder().buildFilterChain(session.getFilterChain());
+ config.getFilterChainBuilder().buildFilterChain(session.getFilterChain());
+ config.getThreadModel().buildFilterChain(session.getFilterChain());
+ }
+ catch (Throwable e)
+ {
+ throw (IOException) new IOException("Failed to create a session.").initCause(e);
+ }
+ session.getIoProcessor().addNew(session);
+ connectFuture.setSession(session);
+ }
+
+ private SocketIoProcessor nextProcessor()
+ {
+ return ioProcessors[processorDistributor++ % processorCount];
+ }
+
+ public void setOpenSocket(Socket openSocket)
+ {
+ _openSocket = openSocket;
+ }
+
+ private class Worker implements Runnable
+ {
+ private long lastActive = System.currentTimeMillis();
+
+ public void run()
+ {
+ Thread.currentThread().setName(ExistingSocketConnector.this.threadName);
+
+ for (; ;)
+ {
+ try
+ {
+ int nKeys = selector.select(1000);
+
+ registerNew();
+
+ if (nKeys > 0)
+ {
+ processSessions(selector.selectedKeys());
+ }
+
+ processTimedOutSessions(selector.keys());
+
+ if (selector.keys().isEmpty())
+ {
+ if (System.currentTimeMillis() - lastActive > workerTimeout * 1000L)
+ {
+ synchronized (lock)
+ {
+ if (selector.keys().isEmpty() &&
+ connectQueue.isEmpty())
+ {
+ worker = null;
+ try
+ {
+ selector.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ finally
+ {
+ selector = null;
+ }
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ lastActive = System.currentTimeMillis();
+ }
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e1)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e1);
+ }
+ }
+ }
+ }
+ }
+
+ private class ConnectionRequest extends DefaultConnectFuture
+ {
+ private final SocketChannel channel;
+ private final long deadline;
+ private final IoHandler handler;
+ private final IoServiceConfig config;
+
+ private ConnectionRequest(SocketChannel channel, IoHandler handler, IoServiceConfig config)
+ {
+ this.channel = channel;
+ long timeout;
+ if (config instanceof IoConnectorConfig)
+ {
+ timeout = ((IoConnectorConfig) config).getConnectTimeoutMillis();
+ }
+ else
+ {
+ timeout = ((IoConnectorConfig) getDefaultConfig()).getConnectTimeoutMillis();
+ }
+ this.deadline = System.currentTimeMillis() + timeout;
+ this.handler = handler;
+ this.config = config;
+ }
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java b/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java
index 6bae0166d1..05ac3dca9e 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java
@@ -39,4 +39,9 @@ public class AMQAuthenticationException extends AMQException
{
super(error, msg, cause);
}
+ public boolean isHardError()
+ {
+ return true;
+ }
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java b/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
index b35f73d84a..f051450260 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
@@ -58,8 +58,9 @@ public class AMQBrokerDetails implements BrokerDetails
if (transport != null)
{
//todo this list of valid transports should be enumerated somewhere
- if ((!(transport.equalsIgnoreCase("vm") ||
- transport.equalsIgnoreCase("tcp"))))
+ if ((!(transport.equalsIgnoreCase(BrokerDetails.VM) ||
+ transport.equalsIgnoreCase(BrokerDetails.TCP) ||
+ transport.equalsIgnoreCase(BrokerDetails.SOCKET))))
{
if (transport.equalsIgnoreCase("localhost"))
{
@@ -164,7 +165,10 @@ public class AMQBrokerDetails implements BrokerDetails
}
else
{
- setPort(port);
+ if (!_transport.equalsIgnoreCase(SOCKET))
+ {
+ setPort(port);
+ }
}
String queryString = connection.getQuery();
@@ -271,13 +275,16 @@ public class AMQBrokerDetails implements BrokerDetails
sb.append(_transport);
sb.append("://");
- if (!(_transport.equalsIgnoreCase("vm")))
+ if (!(_transport.equalsIgnoreCase(VM)))
{
sb.append(_host);
}
- sb.append(':');
- sb.append(_port);
+ if (!(_transport.equalsIgnoreCase(SOCKET)))
+ {
+ sb.append(':');
+ sb.append(_port);
+ }
sb.append(printOptionsURL());
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
index 8ab16c65c0..3969eef8a9 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
@@ -1,5 +1,5 @@
/*
- *
+*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -20,17 +20,31 @@
*/
package org.apache.qpid.client;
-import java.io.IOException;
-import java.net.ConnectException;
-import java.nio.channels.UnresolvedAddressException;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.qpid.AMQConnectionFailureException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQProtocolException;
+import org.apache.qpid.AMQUndeliveredException;
+import org.apache.qpid.AMQUnresolvedAddressException;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.client.failover.FailoverProtectedOperation;
+import org.apache.qpid.client.failover.FailoverRetrySupport;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.jms.ChannelLimitReachedException;
+import org.apache.qpid.jms.Connection;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.jms.FailoverPolicy;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpidity.transport.TransportConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.jms.ConnectionConsumer;
import javax.jms.ConnectionMetaData;
@@ -49,25 +63,117 @@ import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
-
-import org.apache.qpid.*;
-import org.apache.qpid.client.failover.FailoverException;
-import org.apache.qpid.client.protocol.AMQProtocolHandler;
-import org.apache.qpid.exchange.ExchangeDefaults;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.jms.BrokerDetails;
-import org.apache.qpid.jms.Connection;
-import org.apache.qpid.jms.ConnectionListener;
-import org.apache.qpid.jms.ConnectionURL;
-import org.apache.qpid.jms.FailoverPolicy;
-import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.url.URLSyntaxException;
-import org.apache.qpidity.transport.TransportConstants;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.nio.channels.UnresolvedAddressException;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
public class AMQConnection extends Closeable implements Connection, QueueConnection, TopicConnection, Referenceable
{
+ public static final class ChannelToSessionMap
+ {
+ private final AMQSession[] _fastAccessSessions = new AMQSession[16];
+ private final LinkedHashMap<Integer, AMQSession> _slowAccessSessions = new LinkedHashMap<Integer, AMQSession>();
+ private int _size = 0;
+ private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0;
+
+ public AMQSession get(int channelId)
+ {
+ if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0)
+ {
+ return _fastAccessSessions[channelId];
+ }
+ else
+ {
+ return _slowAccessSessions.get(channelId);
+ }
+ }
+
+ public AMQSession put(int channelId, AMQSession session)
+ {
+ AMQSession oldVal;
+ if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0)
+ {
+ oldVal = _fastAccessSessions[channelId];
+ _fastAccessSessions[channelId] = session;
+ }
+ else
+ {
+ oldVal = _slowAccessSessions.put(channelId, session);
+ }
+ if((oldVal != null) && (session == null))
+ {
+ _size--;
+ }
+ else if((oldVal == null) && (session != null))
+ {
+ _size++;
+ }
+
+ return session;
+
+ }
+
+
+ public AMQSession remove(int channelId)
+ {
+ AMQSession session;
+ if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0)
+ {
+ session = _fastAccessSessions[channelId];
+ _fastAccessSessions[channelId] = null;
+ }
+ else
+ {
+ session = _slowAccessSessions.remove(channelId);
+ }
+
+ if(session != null)
+ {
+ _size--;
+ }
+ return session;
+
+ }
+
+ public Collection<AMQSession> values()
+ {
+ ArrayList<AMQSession> values = new ArrayList<AMQSession>(size());
+
+ for(int i = 0; i < 16; i++)
+ {
+ if(_fastAccessSessions[i] != null)
+ {
+ values.add(_fastAccessSessions[i]);
+ }
+ }
+ values.addAll(_slowAccessSessions.values());
+
+ return values;
+ }
+
+ public int size()
+ {
+ return _size;
+ }
+
+ public void clear()
+ {
+ _size = 0;
+ _slowAccessSessions.clear();
+ for(int i = 0; i<16; i++)
+ {
+ _fastAccessSessions[i] = null;
+ }
+ }
+ }
+
+
private static final Logger _logger = LoggerFactory.getLogger(AMQConnection.class);
protected AtomicInteger _idFactory = new AtomicInteger(0);
@@ -76,7 +182,9 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
* This is the "root" mutex that must be held when doing anything that could be impacted by failover. This must be
* held by any child objects of this connection such as the session, producers and consumers.
*/
- protected final Object _failoverMutex = new Object();
+ private final Object _failoverMutex = new Object();
+
+ private final Object _sessionCreationLock = new Object();
/**
* A channel is roughly analogous to a session. The server can negotiate the maximum number of channels per session
@@ -85,7 +193,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
protected long _maximumChannelCount;
/** The maximum size of frame supported by the server */
- protected long _maximumFrameSize;
+ private long _maximumFrameSize;
/**
* The protocol handler dispatches protocol events for this connection. For example, when the connection is dropped
@@ -95,30 +203,31 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
protected AMQProtocolHandler _protocolHandler;
/** Maps from session id (Integer) to AMQSession instance */
- protected final Map<Integer, AMQSession> _sessions = new LinkedHashMap<Integer, AMQSession>();
+ private final ChannelToSessionMap _sessions = new ChannelToSessionMap();
- protected String _clientName;
+ private String _clientName;
/** The user name to use for authentication */
- protected String _username;
+ private String _username;
/** The password to use for authentication */
- protected String _password;
+ private String _password;
/** The virtual path to connect to on the AMQ server */
- protected String _virtualHost;
+ private String _virtualHost;
+
protected ExceptionListener _exceptionListener;
- protected ConnectionListener _connectionListener;
+ private ConnectionListener _connectionListener;
- protected ConnectionURL _connectionURL;
+ private ConnectionURL _connectionURL;
/**
* Whether this connection is started, i.e. whether messages are flowing to consumers. It has no meaning for message
* publication.
*/
- protected boolean _started;
+ protected volatile boolean _started;
/** Policy dictating how to failover */
protected FailoverPolicy _failoverPolicy;
@@ -136,22 +245,23 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
/*
* The connection meta data
*/
- protected QpidConnectionMetaData _connectionMetaData;
+ private QpidConnectionMetaData _connectionMetaData;
/** Configuration info for SSL */
- protected SSLConfiguration _sslConfiguration;
+ private SSLConfiguration _sslConfiguration;
- protected AMQShortString _defaultTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
- protected AMQShortString _defaultQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
- protected AMQShortString _temporaryTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
- protected AMQShortString _temporaryQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
+ private AMQShortString _defaultTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
+ private AMQShortString _defaultQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
+ private AMQShortString _temporaryTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
+ private AMQShortString _temporaryQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
/** Thread Pool for executing connection level processes. Such as returning bounced messages. */
- protected final ExecutorService _taskPool = Executors.newCachedThreadPool();
- protected static final long DEFAULT_TIMEOUT = 1000 * 30;
+ private final ExecutorService _taskPool = Executors.newCachedThreadPool();
+ private static final long DEFAULT_TIMEOUT = 1000 * 30;
+ private ProtocolVersion _protocolVersion = ProtocolVersion.v0_9; // FIXME TGM, shouldn't need this
protected AMQConnectionDelegate _delegate;
-
+
// this connection maximum number of prefetched messages
private long _maxPrefetch;
@@ -259,7 +369,8 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
}
_failoverPolicy = new FailoverPolicy(connectionURL);
- if (_failoverPolicy.getCurrentBrokerDetails().getTransport().equals(BrokerDetails.VM))
+ BrokerDetails brokerDetails = _failoverPolicy.getNextBrokerDetails();
+ if (brokerDetails.getTransport().equals(BrokerDetails.VM))
{
_delegate = new AMQConnectionDelegate_0_8(this);
}
@@ -272,6 +383,26 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
_delegate = new AMQConnectionDelegate_0_10(this);
}
+ final ArrayList<JMSException> exceptions = new ArrayList<JMSException>();
+
+ class Listener implements ExceptionListener
+ {
+ public void onException(JMSException e)
+ {
+ exceptions.add(e);
+ }
+ }
+
+ try
+ {
+ setExceptionListener(new Listener());
+ }
+ catch (JMSException e)
+ {
+ // Shouldn't happen
+ throw new AMQException(null, null, e);
+ }
+
if (_logger.isInfoEnabled())
{
_logger.info("Connection:" + connectionURL);
@@ -288,6 +419,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
_clientName = connectionURL.getClientName();
_username = connectionURL.getUsername();
_password = connectionURL.getPassword();
+
setVirtualHost(connectionURL.getVirtualHost());
if (connectionURL.getDefaultQueueExchangeName() != null)
@@ -311,7 +443,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
}
- _protocolHandler = new AMQProtocolHandler(this);
+ _protocolHandler = new AMQProtocolHandler(this);
// We are not currently connected
_connected = false;
@@ -319,11 +451,13 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
Exception lastException = new Exception();
lastException.initCause(new ConnectException());
- while (!_connected && _failoverPolicy.failoverAllowed())
+ // TMG FIXME this seems... wrong...
+ boolean retryAllowed = true;
+ while (!_connected && retryAllowed )
{
try
{
- makeBrokerConnection(_failoverPolicy.getNextBrokerDetails());
+ makeBrokerConnection(brokerDetails);
lastException = null;
_connected = true;
}
@@ -346,8 +480,10 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
if (_logger.isInfoEnabled())
{
_logger.info("Unable to connect to broker at " + _failoverPolicy.getCurrentBrokerDetails(),
- e.getCause());
+ e.getCause());
}
+ retryAllowed = _failoverPolicy.failoverAllowed();
+ brokerDetails = _failoverPolicy.getNextBrokerDetails();
}
}
@@ -359,8 +495,31 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
if (!_connected)
{
String message = null;
+ try
+ {
+ Thread.sleep(150);
+ }
+ catch (InterruptedException e)
+ {
+ // Eat it, we've hopefully got all the exceptions if this happened
+ }
+ if (exceptions.size() > 0)
+ {
+ JMSException e = exceptions.get(0);
+ int code = -1;
+ try
+ {
+ code = new Integer(e.getErrorCode()).intValue();
+ }
+ catch (NumberFormatException nfe)
+ {
+ // Ignore this, we have some error codes and messages swapped around
+ }
- if (lastException != null)
+ throw new AMQConnectionFailureException(AMQConstant.getConstant(code),
+ e.getMessage(), e);
+ }
+ else if (lastException != null)
{
if (lastException.getCause() != null)
{
@@ -397,7 +556,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
if (e.getCause() != null)
{
e.initCause(lastException);
- }
+ }
}
throw e;
@@ -406,6 +565,18 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
_connectionMetaData = new QpidConnectionMetaData(this);
}
+ protected boolean checkException(Throwable thrown)
+ {
+ Throwable cause = thrown.getCause();
+
+ if (cause == null)
+ {
+ cause = thrown;
+ }
+
+ return ((cause instanceof ConnectException) || (cause instanceof UnresolvedAddressException));
+ }
+
private void getDelegate() throws AMQProtocolException
{
try
@@ -444,20 +615,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
_virtualHost = virtualHost;
}
- protected boolean checkException(Throwable thrown)
- {
- Throwable cause = thrown.getCause();
-
- if (cause == null)
- {
- cause = thrown;
- }
-
- return ((cause instanceof ConnectException) || (cause instanceof UnresolvedAddressException));
- }
-
-
-
public boolean attemptReconnection(String host, int port)
{
BrokerDetails bd = new AMQBrokerDetails(host, port, _sslConfiguration);
@@ -559,8 +716,55 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode,
final int prefetchHigh, final int prefetchLow) throws JMSException
{
+ synchronized (_sessionCreationLock)
+ {
+ checkNotClosed();
+ return _delegate.createSession(transacted, acknowledgeMode, prefetchHigh, prefetchLow);
+ }
+ }
+
+ private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted)
+ throws AMQException, FailoverException
+ {
+
+ ChannelOpenBody channelOpenBody = getProtocolHandler().getMethodRegistry().createChannelOpenBody(null);
+
+ // TODO: Be aware of possible changes to parameter order as versions change.
+
+ _protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class);
+
+ BasicQosBody basicQosBody = getProtocolHandler().getMethodRegistry().createBasicQosBody(0, prefetchHigh, false);
+
+ // todo send low water mark when protocol allows.
+ // todo Be aware of possible changes to parameter order as versions change.
+ _protocolHandler.syncWrite(basicQosBody.generateFrame(channelId), BasicQosOkBody.class);
+
+ if (transacted)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Issuing TxSelect for " + channelId);
+ }
- return _delegate.createSession(transacted, acknowledgeMode, prefetchHigh, prefetchLow);
+ TxSelectBody body = getProtocolHandler().getMethodRegistry().createTxSelectBody();
+
+ // TODO: Be aware of possible changes to parameter order as versions change.
+ _protocolHandler.syncWrite(body.generateFrame(channelId), TxSelectOkBody.class);
+ }
+ }
+
+ private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted)
+ throws AMQException, FailoverException
+ {
+ try
+ {
+ createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted);
+ }
+ catch (AMQException e)
+ {
+ deregisterSession(channelId);
+ throw new AMQException(null, "Error reopening channel " + channelId + " after failover: " + e, e);
+ }
}
public void setFailoverPolicy(FailoverPolicy policy)
@@ -659,10 +863,10 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
if (!_started)
{
_started = true;
- final Iterator it = _sessions.entrySet().iterator();
+ final Iterator it = _sessions.values().iterator();
while (it.hasNext())
{
- final AMQSession s = (AMQSession) ((Map.Entry) it.next()).getValue();
+ final AMQSession s = (AMQSession) (it.next());
try
{
s.start();
@@ -673,6 +877,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
}
}
+
}
}
@@ -704,49 +909,89 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
public void close(long timeout) throws JMSException
{
- synchronized (getFailoverMutex())
+ close(new ArrayList<AMQSession>(_sessions.values()),timeout);
+ }
+
+ public void close(List<AMQSession> sessions, long timeout) throws JMSException
+ {
+ synchronized(_sessionCreationLock)
{
- if (!_closed.getAndSet(true))
+ if(!sessions.isEmpty())
{
- try
+ AMQSession session = sessions.remove(0);
+ synchronized(session.getMessageDeliveryLock())
{
- long startCloseTime = System.currentTimeMillis();
-
- _taskPool.shutdown();
- closeAllSessions(null, timeout, startCloseTime);
-
- if (!_taskPool.isTerminated())
+ close(sessions, timeout);
+ }
+ }
+ else
+ {
+ if (!_closed.getAndSet(true))
+ {
+ synchronized (getFailoverMutex())
{
try
{
- // adjust timeout
- long taskPoolTimeout = adjustTimeout(timeout, startCloseTime);
+ long startCloseTime = System.currentTimeMillis();
+
+ closeAllSessions(null, timeout, startCloseTime);
+
+ //This MUST occur after we have successfully closed all Channels/Sessions
+ _taskPool.shutdown();
- _taskPool.awaitTermination(taskPoolTimeout, TimeUnit.MILLISECONDS);
+ if (!_taskPool.isTerminated())
+ {
+ try
+ {
+ // adjust timeout
+ long taskPoolTimeout = adjustTimeout(timeout, startCloseTime);
+
+ _taskPool.awaitTermination(taskPoolTimeout, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ _logger.info("Interrupted while shutting down connection thread pool.");
+ }
+ }
+
+ // adjust timeout
+ timeout = adjustTimeout(timeout, startCloseTime);
+ _delegate.closeConneciton(timeout);
+
+ //If the taskpool hasn't shutdown by now then give it shutdownNow.
+ // This will interupt any running tasks.
+ if (!_taskPool.isTerminated())
+ {
+ List<Runnable> tasks = _taskPool.shutdownNow();
+ for (Runnable r : tasks)
+ {
+ _logger.warn("Connection close forced taskpool to prevent execution:" + r);
+ }
+ }
}
- catch (InterruptedException e)
+ catch (AMQException e)
{
- _logger.info("Interrupted while shutting down connection thread pool.");
+ JMSException jmse = new JMSException("Error closing connection: " + e);
+ jmse.setLinkedException(e);
+ throw jmse;
}
}
-
- // adjust timeout
- timeout = adjustTimeout(timeout, startCloseTime);
- _delegate.closeConneciton(timeout);
- //_protocolHandler.closeConnection(timeout);
-
- }
- catch (AMQException e)
- {
- JMSException jmse = new JMSException("Error closing connection: " + e);
- jmse.setLinkedException(e);
- throw jmse;
}
}
}
}
+ private long adjustTimeout(long timeout, long startTime)
+ {
+ long now = System.currentTimeMillis();
+ timeout -= now - startTime;
+ if (timeout < 0)
+ {
+ timeout = 0;
+ }
+ return timeout;
+ }
/**
* Marks all sessions and their children as closed without sending any protocol messages. Useful when you need to
@@ -811,19 +1056,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
}
}
-
- private long adjustTimeout(long timeout, long startTime)
- {
- long now = System.currentTimeMillis();
- timeout -= now - startTime;
- if (timeout < 0)
- {
- timeout = 0;
- }
-
- return timeout;
- }
-
public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector,
ServerSessionPool sessionPool, int maxMessages) throws JMSException
{
@@ -889,11 +1121,11 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
return _maximumFrameSize;
}
- public Map getSessions()
+ public ChannelToSessionMap getSessions()
{
return _sessions;
}
-
+
public String getUsername()
{
return _username;
@@ -1017,18 +1249,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
/**
* Invoked by the AMQProtocolSession when a protocol session exception has occurred. This method sends the exception
- * to a JMS exception liste
- {
- ArrayList sessions = new ArrayList(_sessions.values());
- _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey?
- for (Iterator it = sessions.iterator(); it.hasNext();)
- {
- AMQSession s = (AMQSession) it.next();
- // _protocolHandler.addSessionByChannel(s.getChannelId(), s);
- reopenChannel(s.getChannelId(), s.getDefaultPrefetchHigh(), s.getDefaultPrefetchLow(), s.getTransacted());
- s.resubscribe();
- }
- }ner, if configured, and propagates the exception to sessions, which in turn will
+ * to a JMS exception listener, if configured, and propagates the exception to sessions, which in turn will
* propagate to consumers. This allows synchronous consumers to have exceptions thrown to them.
*
* @param cause the exception
@@ -1085,8 +1306,12 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
{
_exceptionListener.onException(je);
}
+ else
+ {
+ _logger.error("Throwable Received but no listener set: " + cause.getMessage());
+ }
- if (!(cause instanceof AMQUndeliveredException) && !(cause instanceof AMQAuthenticationException))
+ if (hardError(cause))
{
try
{
@@ -1110,6 +1335,16 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
}
}
+ private boolean hardError(Throwable cause)
+ {
+ if (cause instanceof AMQException)
+ {
+ return ((AMQException)cause).isHardError();
+ }
+
+ return true;
+ }
+
void registerSession(int channelId, AMQSession session)
{
_sessions.put(channelId, session);
@@ -1120,6 +1355,24 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
_sessions.remove(channelId);
}
+ /**
+ * For all sessions, and for all consumers in those sessions, resubscribe. This is called during failover handling.
+ * The caller must hold the failover mutex before calling this method.
+ */
+ public void resubscribeSesssions() throws JMSException, AMQException, FailoverException
+ {
+ ArrayList sessions = new ArrayList(_sessions.values());
+ _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey?
+ for (Iterator it = sessions.iterator(); it.hasNext();)
+ {
+ AMQSession s = (AMQSession) it.next();
+ // _protocolHandler.addSessionByChannel(s.getChannelId(), s);
+ reopenChannel(s.getChannelId(), s.getDefaultPrefetchHigh(), s.getDefaultPrefetchLow(), s.getTransacted());
+ s.resubscribe();
+ s.setFlowControl(true);
+ }
+ }
+
public String toString()
{
StringBuffer buf = new StringBuffer("AMQConnection:\n");
@@ -1207,6 +1460,22 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
return _sessions.get(channelId);
}
+ public ProtocolVersion getProtocolVersion()
+ {
+ return _protocolVersion;
+ }
+
+ public void setProtocolVersion(ProtocolVersion protocolVersion)
+ {
+ _protocolVersion = protocolVersion;
+ _protocolHandler.getProtocolSession().setProtocolVersion(protocolVersion);
+ }
+
+ public boolean isFailingOver()
+ {
+ return (_protocolHandler.getFailoverLatch() != null);
+ }
+
/**
* Get the maximum number of messages that this connection can pre-fetch.
*
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java
index c22fcc3c7a..637cff5b7e 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_8.java
@@ -178,31 +178,25 @@ public class AMQConnectionDelegate_0_8 implements AMQConnectionDelegate
private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted)
throws AMQException, FailoverException
{
-
+ ChannelOpenBody channelOpenBody = _conn.getProtocolHandler().getMethodRegistry().createChannelOpenBody(null);
// TODO: Be aware of possible changes to parameter order as versions change.
-
- _conn._protocolHandler.syncWrite(ChannelOpenBody.createAMQFrame(channelId, _conn._protocolHandler.getProtocolMajorVersion(),
- _conn._protocolHandler.getProtocolMinorVersion(), null), // outOfBand
- ChannelOpenOkBody.class);
+ _conn._protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class);
// todo send low water mark when protocol allows.
// todo Be aware of possible changes to parameter order as versions change.
- _conn._protocolHandler.syncWrite(BasicQosBody.createAMQFrame(channelId, _conn._protocolHandler.getProtocolMajorVersion(),
- _conn._protocolHandler.getProtocolMinorVersion(), false, // global
- prefetchHigh, // prefetchCount
- 0), // prefetchSize
- BasicQosOkBody.class);
-
+ BasicQosBody basicQosBody = _conn.getProtocolHandler().getMethodRegistry().createBasicQosBody(0,prefetchHigh,false);
+ _conn._protocolHandler.syncWrite(basicQosBody.generateFrame(channelId),BasicQosOkBody.class);
+
if (transacted)
{
if (_logger.isDebugEnabled())
{
_logger.debug("Issuing TxSelect for " + channelId);
}
-
+ TxSelectBody body = _conn.getProtocolHandler().getMethodRegistry().createTxSelectBody();
+
// TODO: Be aware of possible changes to parameter order as versions change.
- _conn._protocolHandler.syncWrite(TxSelectBody.createAMQFrame(channelId, _conn._protocolHandler.getProtocolMajorVersion(),
- _conn._protocolHandler.getProtocolMinorVersion()), TxSelectOkBody.class);
+ _conn._protocolHandler.syncWrite(body.generateFrame(channelId), TxSelectOkBody.class);
}
}
@@ -212,7 +206,7 @@ public class AMQConnectionDelegate_0_8 implements AMQConnectionDelegate
*/
public void resubscribeSessions() throws JMSException, AMQException, FailoverException
{
- ArrayList sessions = new ArrayList(_conn._sessions.values());
+ ArrayList sessions = new ArrayList(_conn.getSessions().values());
_logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey?
for (Iterator it = sessions.iterator(); it.hasNext();)
{
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java
index 3fcec67fe1..fd8063e99b 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -23,6 +23,7 @@ package org.apache.qpid.client;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Hashtable;
+import java.util.UUID;
import javax.jms.*;
import javax.naming.Context;
@@ -259,7 +260,7 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF
}
catch (UnknownHostException e)
{
- return null;
+ return "UnknownHost" + UUID.randomUUID();
}
}
@@ -352,7 +353,9 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF
* @param name
* @param ctx
* @param env
+ *
* @return AMQConnection,AMQTopic,AMQQueue, or AMQConnectionFactory.
+ *
* @throws Exception
*/
public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable env) throws Exception
@@ -408,8 +411,9 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF
public Reference getReference() throws NamingException
{
- return new Reference(AMQConnectionFactory.class.getName(),
- new StringRefAddr(AMQConnectionFactory.class.getName(), _connectionDetails.getURL()),
+ return new Reference(
+ AMQConnectionFactory.class.getName(),
+ new StringRefAddr(AMQConnectionFactory.class.getName(), _connectionDetails.getURL()),
AMQConnectionFactory.class.getName(), null); // factory location
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java
index 770fab7a81..02959aff3b 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java
@@ -27,6 +27,7 @@ import java.util.Map;
import org.apache.qpid.client.url.URLParser;
import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ProtocolVersion;
import org.apache.qpid.jms.BrokerDetails;
import org.apache.qpid.jms.ConnectionURL;
import org.apache.qpid.url.URLHelper;
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
index e02b3d6643..52080112c9 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
@@ -56,7 +56,9 @@ public abstract class AMQDestination implements Destination, Referenceable
private String _url;
private AMQShortString _urlAsShortString;
- private boolean _validated;
+ private boolean _checkedForQueueBinding;
+
+ private boolean _exchangeExistsChecked;
private byte[] _byteEncoding;
private static final int IS_DURABLE_MASK = 0x1;
@@ -234,14 +236,25 @@ public abstract class AMQDestination implements Destination, Referenceable
}
- public boolean isValidated()
+ public boolean isCheckedForQueueBinding()
+ {
+ return _checkedForQueueBinding;
+ }
+
+ public void setCheckedForQueueBinding(boolean checkedForQueueBinding)
+ {
+ _checkedForQueueBinding = checkedForQueueBinding;
+ }
+
+
+ public boolean isExchangeExistsChecked()
{
- return _validated;
+ return _exchangeExistsChecked;
}
- public void setValidated(boolean validated)
+ public void setExchangeExistsChecked(final boolean exchangeExistsChecked)
{
- _validated = validated;
+ _exchangeExistsChecked = exchangeExistsChecked;
}
public String toURL()
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java b/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java
index b27dfc6dcb..b9e9a33cd6 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java
@@ -49,6 +49,6 @@ public class AMQHeadersExchange extends AMQDestination
//Not sure what the best approach is here, probably to treat this like a topic
//and allow server to generate names. As it is AMQ specific it doesn't need to
//fit the JMS API expectations so this is not as yet critical.
- return false;
+ return getAMQQueueName() == null;
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java b/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java
index f56477c0fc..08fd49286b 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java
@@ -22,6 +22,7 @@ package org.apache.qpid.client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.qpid.AMQException;
import javax.jms.IllegalStateException;
import javax.jms.JMSException;
@@ -50,7 +51,9 @@ public class AMQQueueBrowser implements QueueBrowser
_messageSelector = ((messageSelector == null) || (messageSelector.trim().length() == 0)) ? null : messageSelector;
// Create Consumer to verify message selector.
BasicMessageConsumer consumer =
- (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false);
+ (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false);
+ // Close this consumer as we are not looking to consume only to establish that, at least for now,
+ // the QB can be created
consumer.close();
}
@@ -88,36 +91,37 @@ public class AMQQueueBrowser implements QueueBrowser
checkState();
final BasicMessageConsumer consumer =
(BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false);
+
_consumers.add(consumer);
return new Enumeration()
+ {
+
+ Message _nextMessage = consumer == null ? null : consumer.receive(1000);
+
+ public boolean hasMoreElements()
{
- Message _nextMessage = consumer.receive(1000);
+ _logger.info("QB:hasMoreElements:" + (_nextMessage != null));
+ return (_nextMessage != null);
+ }
- public boolean hasMoreElements()
+ public Object nextElement()
+ {
+ Message msg = _nextMessage;
+ try
{
- _logger.info("QB:hasMoreElements:" + (_nextMessage != null));
- return (_nextMessage != null);
+ _logger.info("QB:nextElement about to receive");
+ _nextMessage = consumer.receive(1000);
+ _logger.info("QB:nextElement received:" + _nextMessage);
}
-
- public Object nextElement()
+ catch (JMSException e)
{
- Message msg = _nextMessage;
- try
- {
- _logger.info("QB:nextElement about to receive");
- _nextMessage = consumer.receive(1000);
- _logger.info("QB:nextElement received:" + _nextMessage);
- }
- catch (JMSException e)
- {
- _logger.warn("Exception caught while queue browsing", e);
- _nextMessage = null;
- }
-
- return msg;
+ _logger.warn("Exception caught while queue browsing", e);
+ _nextMessage = null;
}
- };
+ return msg;
+ }
+ };
}
public void close() throws JMSException
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
index ee55743d0e..404c0cd381 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
@@ -20,14 +20,15 @@
*/
package org.apache.qpid.client;
-
import java.io.Serializable;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -57,7 +58,9 @@ import javax.jms.Topic;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
+import javax.jms.TransactionRolledBackException;
+import org.apache.qpid.AMQDisconnectedException;
import org.apache.qpid.AMQException;
import org.apache.qpid.AMQInvalidArgumentException;
import org.apache.qpid.AMQInvalidRoutingKeyException;
@@ -77,11 +80,12 @@ import org.apache.qpid.client.message.ReturnMessage;
import org.apache.qpid.client.message.UnprocessedMessage;
import org.apache.qpid.client.protocol.AMQProtocolHandler;
import org.apache.qpid.client.util.FlowControllingBlockingQueue;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.FieldTableFactory;
+import org.apache.qpid.common.AMQPFilterTypes;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9;
import org.apache.qpid.jms.Session;
import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.url.AMQBindingURL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -106,6 +110,86 @@ import org.slf4j.LoggerFactory;
*/
public abstract class AMQSession extends Closeable implements Session, QueueSession, TopicSession
{
+ public static final class IdToConsumerMap
+ {
+ private final BasicMessageConsumer[] _fastAccessConsumers = new BasicMessageConsumer[16];
+ private final ConcurrentHashMap<Integer, BasicMessageConsumer> _slowAccessConsumers = new ConcurrentHashMap<Integer, BasicMessageConsumer>();
+
+
+ public BasicMessageConsumer get(int id)
+ {
+ if((id & 0xFFFFFFF0) == 0)
+ {
+ return _fastAccessConsumers[id];
+ }
+ else
+ {
+ return _slowAccessConsumers.get(id);
+ }
+ }
+
+ public BasicMessageConsumer put(int id, BasicMessageConsumer consumer)
+ {
+ BasicMessageConsumer oldVal;
+ if((id & 0xFFFFFFF0) == 0)
+ {
+ oldVal = _fastAccessConsumers[id];
+ _fastAccessConsumers[id] = consumer;
+ }
+ else
+ {
+ oldVal = _slowAccessConsumers.put(id, consumer);
+ }
+
+ return consumer;
+
+ }
+
+
+ public BasicMessageConsumer remove(int id)
+ {
+ BasicMessageConsumer consumer;
+ if((id & 0xFFFFFFF0) == 0)
+ {
+ consumer = _fastAccessConsumers[id];
+ _fastAccessConsumers[id] = null;
+ }
+ else
+ {
+ consumer = _slowAccessConsumers.remove(id);
+ }
+
+ return consumer;
+
+ }
+
+ public Collection<BasicMessageConsumer> values()
+ {
+ ArrayList<BasicMessageConsumer> values = new ArrayList<BasicMessageConsumer>();
+
+ for(int i = 0; i < 16; i++)
+ {
+ if(_fastAccessConsumers[i] != null)
+ {
+ values.add(_fastAccessConsumers[i]);
+ }
+ }
+ values.addAll(_slowAccessConsumers.values());
+
+ return values;
+ }
+
+
+ public void clear()
+ {
+ _slowAccessConsumers.clear();
+ for(int i = 0; i<16; i++)
+ {
+ _fastAccessConsumers[i] = null;
+ }
+ }
+ }
+
/** Used for debugging. */
private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class);
@@ -155,7 +239,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
protected boolean _transacted;
/** Holds the sessions acknowledgement mode. */
- protected int _acknowledgeMode;
+ protected final int _acknowledgeMode;
/** Holds this session unique identifier, used to distinguish it from other sessions. */
protected int _channelId;
@@ -231,8 +315,16 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
* Maps from identifying tags to message consumers, in order to pass dispatch incoming messages to the right
* consumer.
*/
- protected Map<AMQShortString, BasicMessageConsumer> _consumers =
- new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>();
+ protected final IdToConsumerMap _consumers = new IdToConsumerMap();
+
+ //Map<AMQShortString, BasicMessageConsumer> _consumers =
+ //new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>();
+
+ /**
+ * Contains a list of consumers which have been removed but which might still have
+ * messages to acknowledge, eg in client ack or transacted modes
+ */
+ private CopyOnWriteArrayList<BasicMessageConsumer> _removedConsumers = new CopyOnWriteArrayList<BasicMessageConsumer>();
/** Provides a count of consumers on destinations, in order to be able to know if a destination has consumers. */
private ConcurrentHashMap<Destination, AtomicInteger> _destinationConsumerCount =
@@ -284,6 +376,32 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
protected final boolean _strictAMQPFATAL;
private final Object _messageDeliveryLock = new Object();
+ /** Session state : used to detect if commit is a) required b) allowed , i.e. does the tx span failover. */
+ private boolean _dirty;
+ /** Has failover occured on this session */
+ private boolean _failedOver;
+
+
+
+ private static final class FlowControlIndicator
+ {
+ private volatile boolean _flowControl = true;
+
+ public synchronized void setFlowControl(boolean flowControl)
+ {
+ _flowControl= flowControl;
+ notify();
+ }
+
+ public boolean getFlowControl()
+ {
+ return _flowControl;
+ }
+ }
+
+ /** Flow control */
+ private FlowControlIndicator _flowControl = new FlowControlIndicator();
+
/**
* Creates a new session on a connection.
*
@@ -330,24 +448,20 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
public void aboveThreshold(int currentValue)
{
- if (_acknowledgeMode == NO_ACKNOWLEDGE)
- {
_logger.debug(
"Above threshold(" + _defaultPrefetchHighMark
+ ") so suspending channel. Current value is " + currentValue);
new Thread(new SuspenderRunner(true)).start();
- }
+
}
public void underThreshold(int currentValue)
{
- if (_acknowledgeMode == NO_ACKNOWLEDGE)
- {
_logger.debug(
"Below threshold(" + _defaultPrefetchLowMark
+ ") so unsuspending channel. Current value is " + currentValue);
new Thread(new SuspenderRunner(false)).start();
- }
+
}
});
}
@@ -357,7 +471,22 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
}
-
+ /**
+ * Creates a new session on a connection with the default message factory factory.
+ *
+ * @param con The connection on which to create the session.
+ * @param channelId The unique identifier for the session.
+ * @param transacted Indicates whether or not the session is transactional.
+ * @param acknowledgeMode The acknoledgement mode for the session.
+ * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session.
+ * @param defaultPrefetchLow The number of prefetched messages at which to resume the session.
+ */
+ AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh,
+ int defaultPrefetchLow)
+ {
+ this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh,
+ defaultPrefetchLow);
+ }
// ===== JMS Session methods.
@@ -371,6 +500,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
close(-1);
}
+ public BytesMessage createBytesMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new JMSBytesMessage();
+ }
+
/**
* Acknowledges all unacknowledged messages on the session, for all message consumers on the session.
*
@@ -381,6 +516,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
if (isClosed())
{
throw new IllegalStateException("Session is already closed");
+ }
+ else if (hasFailedOver())
+ {
+ throw new IllegalStateException("has failed over");
}
while (true)
@@ -405,6 +544,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
*/
public abstract void acknowledgeMessage(long deliveryTag, boolean multiple);
+ public MethodRegistry getMethodRegistry()
+ {
+ MethodRegistry methodRegistry = getProtocolHandler().getMethodRegistry();
+ return methodRegistry;
+ }
+
/**
* Binds the named queue, with the specified routing key, to the named exchange.
*
@@ -471,30 +616,26 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
if (_logger.isInfoEnabled())
{
- _logger.info("Closing session: " + this );//+ ":"
- // + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6));
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ _logger.info("Closing session: " + this); // + ":"
+ // + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
}
- if( _dispatcher != null )
- {
- _dispatcher.setConnectionStopped(true);
- }
- synchronized (_messageDeliveryLock)
+ // Ensure we only try and close an open session.
+ if (!_closed.getAndSet(true))
{
-
- // We must close down all producers and consumers in an orderly fashion. This is the only method
- // that can be called from a different thread of control from the one controlling the session.
synchronized (_connection.getFailoverMutex())
{
- // Ensure we only try and close an open session.
- if (!_closed.getAndSet(true))
+ // We must close down all producers and consumers in an orderly fashion. This is the only method
+ // that can be called from a different thread of control from the one controlling the session.
+ synchronized (_messageDeliveryLock)
{
// we pass null since this is not an error case
closeProducersAndConsumers(null);
try
{
- sendClose(timeout);
+ sendClose(timeout);
}
catch (AMQException e)
{
@@ -527,25 +668,44 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
*/
public void closed(Throwable e) throws JMSException
{
- synchronized (_messageDeliveryLock)
+ // This method needs to be improved. Throwables only arrive here from the mina : exceptionRecived
+ // calls through connection.closeAllSessions which is also called by the public connection.close()
+ // with a null cause
+ // When we are closing the Session due to a protocol session error we simply create a new AMQException
+ // with the correct error code and text this is cleary WRONG as the instanceof check below will fail.
+ // We need to determin here if the connection should be
+
+ if (e instanceof AMQDisconnectedException)
+ {
+ if (_dispatcher != null)
+ {
+ // Failover failed and ain't coming back. Knife the dispatcher.
+ _dispatcher.interrupt();
+ }
+ }
+
+ if (!_closed.getAndSet(true))
{
synchronized (_connection.getFailoverMutex())
{
- // An AMQException has an error code and message already and will be passed in when closure occurs as a
- // result of a channel close request
- _closed.set(true);
- AMQException amqe;
- if (e instanceof AMQException)
- {
- amqe = (AMQException) e;
- }
- else
+ synchronized (_messageDeliveryLock)
{
- amqe = new AMQException(null, "Closing session forcibly", e);
- }
+ // An AMQException has an error code and message already and will be passed in when closure occurs as a
+ // result of a channel close request
+ AMQException amqe;
+ if (e instanceof AMQException)
+ {
+ amqe = (AMQException) e;
+ }
+ else
+ {
+ amqe = new AMQException("Closing session forcibly", e);
+ }
+
- _connection.deregisterSession(_channelId);
- closeProducersAndConsumers(amqe);
+ _connection.deregisterSession(_channelId);
+ closeProducersAndConsumers(amqe);
+ }
}
}
}
@@ -565,10 +725,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
*/
public void commit() throws JMSException
{
- checkTransacted();
+ checkTransacted();
try
{
+
+ // TGM FIXME: what about failover?
// Acknowledge all delivered messages
while (true)
{
@@ -580,7 +742,6 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
acknowledgeMessage(tag, false);
}
-
// Commits outstanding messages and acknowledgments
sendCommit();
}
@@ -600,16 +761,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
// Remove the consumer from the map
- BasicMessageConsumer consumer = (BasicMessageConsumer) _consumers.get(consumerTag);
+ BasicMessageConsumer consumer = _consumers.get(consumerTag.toIntValue());
if (consumer != null)
{
- // fixme this isn't right.. needs to check if _queue contains data for this consumer
- if (consumer.isAutoClose()) // && _queue.isEmpty())
- {
- consumer.closeWhenNoMessages(true);
- }
-
- if (!consumer.isNoConsume())
+ if (!consumer.isNoConsume()) // Normal Consumer
{
// Clean the Maps up first
// Flush any pending messages for this consumerTag
@@ -620,13 +775,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
else
{
_logger.info("Dispatcher is null so created stopped dispatcher");
-
- startDistpatcherIfNecessary(true);
+ startDispatcherIfNecessary(true);
}
_dispatcher.rejectPending(consumer);
}
- else
+ else // Queue Browser
{
// Just close the consumer
// fixme the CancelOK is being processed before the arriving messages..
@@ -634,13 +788,24 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
// has yet to receive before the close comes in.
// consumer.markClosed();
+
+
+
+ if (consumer.isAutoClose())
+ {
+ // There is a small window where the message is between the two queues in the dispatcher.
+ if (consumer.isClosed())
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Closing consumer:" + consumer.debugIdentity());
+ }
+
+ deregisterConsumer(consumer);
+ }
+ }
}
}
- else
- {
- _logger.warn("Unable to confirm cancellation of consumer (" + consumerTag + "). Not found in consumer map.");
- }
-
}
public QueueBrowser createBrowser(Queue queue) throws JMSException
@@ -675,21 +840,11 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
messageSelector, null, true, true);
}
- public BytesMessage createBytesMessage() throws JMSException
- {
- synchronized (_connection.getFailoverMutex())
- {
- checkNotClosed();
-
- return new JMSBytesMessage();
- }
- }
-
public MessageConsumer createConsumer(Destination destination) throws JMSException
{
checkValidDestination(destination);
- return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, null, null,
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic), null, null,
false, false);
}
@@ -701,11 +856,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
false, false);
}
+
public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException
{
checkValidDestination(destination);
- return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false,
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic),
messageSelector, null, false, false);
}
@@ -714,16 +870,27 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
checkValidDestination(destination);
- return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false,
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, (destination instanceof Topic),
messageSelector, null, false, false);
}
+
+ public MessageConsumer createExclusiveConsumer(Destination destination, String messageSelector, boolean noLocal)
+ throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, true,
+ messageSelector, null, false, false);
+ }
+
+
public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive,
String selector) throws JMSException
{
checkValidDestination(destination);
- return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, null, false, false);
+ return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, null, false, false);
}
public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal,
@@ -739,7 +906,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
checkValidDestination(destination);
- return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, rawSelector, false, false);
+ return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, rawSelector, false, false);
}
public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal,
@@ -770,12 +937,8 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
public MapMessage createMapMessage() throws JMSException
{
- synchronized (_connection.getFailoverMutex())
- {
- checkNotClosed();
-
- return new JMSMapMessage();
- }
+ checkNotClosed();
+ return new JMSMapMessage();
}
public javax.jms.Message createMessage() throws JMSException
@@ -785,12 +948,8 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
public ObjectMessage createObjectMessage() throws JMSException
{
- synchronized (_connection.getFailoverMutex())
- {
- checkNotClosed();
-
- return (ObjectMessage) new JMSObjectMessage();
- }
+ checkNotClosed();
+ return (ObjectMessage) new JMSObjectMessage();
}
public ObjectMessage createObjectMessage(Serializable object) throws JMSException
@@ -827,7 +986,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
checkNotClosed();
- return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic), topic);
+ return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic,false,false), topic);
}
public Queue createQueue(String queueName) throws JMSException
@@ -874,7 +1033,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
public Object execute() throws AMQException, FailoverException
{
- sendCreateQueue(name, autoDelete,durable,exclusive);
+ sendCreateQueue(name, autoDelete, durable, exclusive);
return null;
}
}, _connection).execute();
@@ -989,9 +1148,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
AMQTopic dest = checkValidTopic(topic);
// AMQTopic dest = new AMQTopic(topic.getTopicName());
- return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest));
+ return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createExclusiveConsumer(dest));
}
+
/**
* Creates a non-durable subscriber with a message selector
*
@@ -1009,7 +1169,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
AMQTopic dest = checkValidTopic(topic);
// AMQTopic dest = new AMQTopic(topic.getTopicName());
- return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal));
+ return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createExclusiveConsumer(dest, messageSelector, noLocal));
}
public abstract TemporaryQueue createTemporaryQueue() throws JMSException;
@@ -1267,14 +1427,14 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
}
- public abstract void sendRecover() throws AMQException, FailoverException;
+ abstract void sendRecover() throws AMQException, FailoverException;
public void rejectMessage(UnprocessedMessage message, boolean requeue)
{
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Rejecting Unacked message:" + message.getDeliveryTag());
+ _logger.debug("Rejecting Unacked message:" + message.getDeliveryTag());
}
rejectMessage(message.getDeliveryTag(), requeue);
@@ -1282,9 +1442,9 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
public void rejectMessage(AbstractJMSMessage message, boolean requeue)
{
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Rejecting Abstract message:" + message.getDeliveryTag());
+ _logger.debug("Rejecting Abstract message:" + message.getDeliveryTag());
}
rejectMessage(message.getDeliveryTag(), requeue);
@@ -1325,6 +1485,8 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
sendRollback();
+ markClean();
+
if (!isSuspended)
{
suspendChannel(false);
@@ -1460,6 +1622,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
AMQDestination amqd = (AMQDestination) destination;
+ final AMQProtocolHandler protocolHandler = getProtocolHandler();
// TODO: Define selectors in AMQP
// TODO: construct the rawSelector from the selector string if rawSelector == null
final FieldTable ft = FieldTableFactory.newFieldTable();
@@ -1499,11 +1662,6 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
{
JMSException ex = new JMSException("Error registering consumer: " + e);
- if (_logger.isDebugEnabled())
- {
- e.printStackTrace();
- }
-
ex.setLinkedException(e);
throw ex;
}
@@ -1531,7 +1689,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
*/
void deregisterConsumer(BasicMessageConsumer consumer)
{
- if (_consumers.remove(consumer.getConsumerTag()) != null)
+ if (_consumers.remove(consumer.getConsumerTag().toIntValue()) != null)
{
String subscriptionName = _reverseSubscriptionMap.remove(consumer);
if (subscriptionName != null)
@@ -1547,6 +1705,13 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
_destinationConsumerCount.remove(dest);
}
}
+
+ // Consumers that are closed in a transaction must be stored
+ // so that messages they have received can be acknowledged on commit
+ if (_transacted)
+ {
+ _removedConsumers.add(consumer);
+ }
}
}
@@ -1582,8 +1747,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
*/
public abstract boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey)
throws JMSException;
-
-
+
public abstract boolean isQueueBound(final AMQDestination destination) throws JMSException;
/**
@@ -1605,6 +1769,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
*/
void resubscribe() throws AMQException
{
+ _failedOver = true;
resubscribeProducers();
resubscribeConsumers();
}
@@ -1639,13 +1804,20 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
// If the event dispatcher is not running then start it too.
if (hasMessageListeners())
{
- startDistpatcherIfNecessary();
+ startDispatcherIfNecessary();
}
}
- synchronized void startDistpatcherIfNecessary()
+ void startDispatcherIfNecessary()
{
+ //If we are the dispatcher then we don't need to check we are started
+ if (Thread.currentThread() == _dispatcher)
+ {
+ return;
+ }
+
// If IMMEDIATE_PREFETCH is not set then we need to start fetching
+ // This is final per session so will be multi-thread safe.
if (!_immediatePrefetch)
{
// We do this now if this is the first call on a started connection
@@ -1662,10 +1834,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
}
- startDistpatcherIfNecessary(false);
+ startDispatcherIfNecessary(false);
}
- synchronized void startDistpatcherIfNecessary(boolean initiallyStopped)
+ synchronized void startDispatcherIfNecessary(boolean initiallyStopped)
{
if (_dispatcher == null)
{
@@ -1816,7 +1988,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
else
{
- con.close();
+ con.close(false);
}
}
// at this point the _consumers map will be empty
@@ -1891,20 +2063,22 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
private void consumeFromQueue(BasicMessageConsumer consumer, AMQShortString queueName,
AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector) throws AMQException, FailoverException
{
- //need to generate a consumer tag on the client so we can exploit the nowait flag
- AMQShortString tag = new AMQShortString(Integer.toString(_nextTag++));
+ int tagId = _nextTag++;
+ // need to generate a consumer tag on the client so we can exploit the nowait flag
+ AMQShortString tag = new AMQShortString(Integer.toString(tagId));
+
consumer.setConsumerTag(tag);
// we must register the consumer in the map before we actually start listening
- _consumers.put(tag, consumer);
+ _consumers.put(tagId, consumer);
try
{
- sendConsume(consumer,queueName,protocolHandler,nowait,messageSelector,tag);
+ sendConsume(consumer, queueName, protocolHandler, nowait, messageSelector, tag);
}
catch (AMQException e)
{
// clean-up the map in the event of an error
- _consumers.remove(tag);
+ _consumers.remove(tagId);
throw e;
}
}
@@ -1945,6 +2119,34 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
declareExchange(amqd.getExchangeName(), amqd.getExchangeClass(), protocolHandler, nowait);
}
+
+ /**
+ * Returns the number of messages currently queued for the given destination.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param amqd The destination to be checked
+ *
+ * @return the number of queued messages.
+ *
+ * @throws AMQException If the queue cannot be declared for any reason.
+ */
+ public long getQueueDepth(final AMQDestination amqd)
+ throws AMQException
+ {
+ return new FailoverNoopSupport<Long, AMQException>(
+ new FailoverProtectedOperation<Long, AMQException>()
+ {
+ public Long execute() throws AMQException, FailoverException
+ {
+ return requestQueueDepth(amqd);
+ }
+ }, _connection).execute();
+
+ }
+
+ abstract Long requestQueueDepth(AMQDestination amqd) throws AMQException, FailoverException;
+
/**
* Declares the named exchange and type of exchange.
*
@@ -1960,7 +2162,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
* @todo Be aware of possible changes to parameter order as versions change.
*/
private void declareExchange(final AMQShortString name, final AMQShortString type,
- final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException
+ final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException
{
new FailoverNoopSupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()
{
@@ -2013,7 +2215,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
amqd.setQueueName(protocolHandler.generateQueueName());
}
- sendQueueDeclare(amqd,protocolHandler);
+ sendQueueDeclare(amqd, protocolHandler);
return amqd.getAMQQueueName();
}
@@ -2064,12 +2266,12 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
return _connection.getProtocolHandler();
}
- protected byte getProtocolMajorVersion()
+ public byte getProtocolMajorVersion()
{
return getProtocolHandler().getProtocolMajorVersion();
}
- protected byte getProtocolMinorVersion()
+ public byte getProtocolMinorVersion()
{
return getProtocolHandler().getProtocolMinorVersion();
}
@@ -2227,7 +2429,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
if (_logger.isDebugEnabled())
{
_logger.debug("Removing message(" + System.identityHashCode(message) + ") from _queue DT:"
- + message.getDeliveryTag());
+ + message.getDeliveryTag());
}
messages.remove();
@@ -2250,6 +2452,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
for (Iterator it = consumers.iterator(); it.hasNext();)
{
BasicMessageConsumer consumer = (BasicMessageConsumer) it.next();
+ consumer.failedOver();
registerConsumer(consumer, true);
}
}
@@ -2276,11 +2479,10 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
// Bounced message is processed here, away from the mina thread
AbstractJMSMessage bouncedMessage =
_messageFactoryRegistry.createMessage(0, false, msg.getExchange(),
- msg.getExchange(), msg.getContentHeader(), msg.getBodies());
-
- AMQConstant errorCode = AMQConstant.getConstant(msg.getReplyCode());
- AMQShortString reason = msg.getReplyText();
- _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")");
+ msg.getRoutingKey(), msg.getContentHeader(), msg.getBodies());
+ AMQConstant errorCode = AMQConstant.getConstant(msg.getReplyCode());
+ AMQShortString reason = msg.getReplyText();
+ _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")");
// @TODO should this be moved to an exception handler of sorts. Somewhere errors are converted to correct execeptions.
if (errorCode == AMQConstant.NO_CONSUMERS)
@@ -2318,7 +2520,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
*
* @todo Be aware of possible changes to parameter order as versions change.
*/
- protected void suspendChannel(boolean suspend) throws AMQException // , FailoverException
+ protected void suspendChannel(boolean suspend) throws AMQException // , FailoverException
{
synchronized (_suspensionLock)
{
@@ -2339,6 +2541,13 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
}
+ public abstract void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException;
+
+ Object getMessageDeliveryLock()
+ {
+ return _messageDeliveryLock;
+ }
+
/**
* Indicates whether this session consumers pre-fetche messages
*
@@ -2350,7 +2559,62 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
- public abstract void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException;
+ /** Signifies that the session has pending sends to commit. */
+ public void markDirty()
+ {
+ _dirty = true;
+ }
+
+ /** Signifies that the session has no pending sends to commit. */
+ public void markClean()
+ {
+ _dirty = false;
+ _failedOver = false;
+ }
+
+ /**
+ * Check to see if failover has occured since the last call to markClean(commit or rollback).
+ *
+ * @return boolean true if failover has occured.
+ */
+ public boolean hasFailedOver()
+ {
+ return _failedOver;
+ }
+
+ /**
+ * Check to see if any message have been sent in this transaction and have not been commited.
+ *
+ * @return boolean true if a message has been sent but not commited
+ */
+ public boolean isDirty()
+ {
+ return _dirty;
+ }
+
+ public void setTicket(int ticket)
+ {
+ _ticket = ticket;
+ }
+
+ public void setFlowControl(final boolean active)
+ {
+ _flowControl.setFlowControl(active);
+ }
+
+
+ public void checkFlowControl() throws InterruptedException
+ {
+ synchronized(_flowControl)
+ {
+ while(!_flowControl.getFlowControl())
+ {
+ _flowControl.wait();
+ }
+ }
+
+ }
+
/** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */
class Dispatcher extends Thread
@@ -2361,6 +2625,7 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
private final Object _lock = new Object();
private final AtomicLong _rollbackMark = new AtomicLong(-1);
+ private String dispatcherID = "" + System.identityHashCode(this);
public Dispatcher()
{
@@ -2392,10 +2657,11 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
// Reject messages on pre-receive queue
- consumer.rollback();
+ consumer.rollbackPendingMessages();
// Reject messages on pre-dispatch queue
rejectMessagesForConsumerTag(consumer.getConsumerTag(), true);
+ //Let the dispatcher deal with this when it gets to them.
// closeConsumer
consumer.markClosed();
@@ -2436,6 +2702,13 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
+ for (int i = 0; i < _removedConsumers.size(); i++)
+ {
+ // Sends acknowledgement to server
+ _removedConsumers.get(i).rollback();
+ _removedConsumers.remove(i);
+ }
+
setConnectionStopped(isStopped);
}
@@ -2484,9 +2757,16 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
}
else
{
- synchronized (_messageDeliveryLock)
+ if (message.getDeliveryTag() <= _rollbackMark.get())
{
- dispatchMessage(message);
+ rejectMessage(message, true);
+ }
+ else
+ {
+ synchronized (_messageDeliveryLock)
+ {
+ dispatchMessage(message);
+ }
}
}
@@ -2535,38 +2815,38 @@ public abstract class AMQSession extends Closeable implements Session, QueueSess
//This if block is not needed anymore as bounce messages are handled separately
//if (message.getDeliverBody() != null)
//{
- final BasicMessageConsumer consumer =
- (BasicMessageConsumer) _consumers.get(new AMQShortString(message.getConsumerTag()));
+ final BasicMessageConsumer consumer =
+ _consumers.get(message.getConsumerTag().toIntValue());
- if ((consumer == null) || consumer.isClosed())
+ if ((consumer == null) || consumer.isClosed())
+ {
+ if (_dispatcherLogger.isInfoEnabled())
{
- if (_dispatcherLogger.isInfoEnabled())
+ if (consumer == null)
{
- if (consumer == null)
- {
- _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "["
- + message.getDeliveryTag() + "] from queue "
- + message.getConsumerTag() + " )without a handler - rejecting(requeue)...");
- }
- else
- {
- _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "["
- + message.getDeliveryTag() + "] from queue " + " consumer("
- + consumer.debugIdentity() + ") is closed rejecting(requeue)...");
- }
+ _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ")" + "["
+ + message.getDeliveryTag() + "] from queue "
+ + message.getConsumerTag() + " )without a handler - rejecting(requeue)...");
}
- // Don't reject if we're already closing
- if (!_closed.get())
+ else
{
- rejectMessage(message, true);
+ _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "["
+ + message.getDeliveryTag() + "] from queue " + " consumer("
+ + message.getConsumerTag() + ") is closed rejecting(requeue)...");
}
}
- else
+ // Don't reject if we're already closing
+ if (!_closed.get())
{
- consumer.notifyMessage(message, _channelId);
+ rejectMessage(message, true);
}
}
- //}
+ else
+ {
+ consumer.notifyMessage(message);
+ }
+
+ }
}
/*public void requestAccess(AMQShortString realm, boolean exclusive, boolean passive, boolean active, boolean write,
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java b/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java
new file mode 100644
index 0000000000..a1b240ed54
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java
@@ -0,0 +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.client;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQSessionDirtyException represents all failures to send data on a transacted session that is
+ * no longer in a state that the client expects. i.e. failover has occured so previously sent messages
+ * will not be part of the transaction.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent attempt to perform additional sends on a dirty session.
+ * </table>
+ */
+public class AMQSessionDirtyException extends AMQException
+{
+ public AMQSessionDirtyException(String msg)
+ {
+ super(AMQConstant.RESOURCE_ERROR, msg, null);
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
index d72668bb53..6089c54314 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java
@@ -395,16 +395,16 @@ public class AMQSession_0_10 extends AMQSession
try
{
preAcquire = ( ! consumer.isNoConsume() && consumer.getMessageSelector() == null) || !(consumer.getDestination() instanceof AMQQueue);
+ getQpidSession().messageSubscribe(queueName.toString(), tag.toString(),
+ getAcknowledgeMode() == NO_ACKNOWLEDGE ? Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED:Session.TRANSFER_CONFIRM_MODE_REQUIRED,
+ preAcquire ? Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE : Session.TRANSFER_ACQUIRE_MODE_NO_ACQUIRE,
+ new MessagePartListenerAdapter((BasicMessageConsumer_0_10) consumer), null,
+ consumer.isExclusive() ? Option.EXCLUSIVE : Option.NO_OPTION);
}
catch (JMSException e)
{
throw new AMQException(AMQConstant.INTERNAL_ERROR, "problem when registering consumer", e);
}
- getQpidSession().messageSubscribe(queueName.toString(), tag.toString(),
- (Boolean.getBoolean("noAck") ?Session.TRANSFER_CONFIRM_MODE_NOT_REQUIRED:Session.TRANSFER_CONFIRM_MODE_REQUIRED),
- preAcquire ? Session.TRANSFER_ACQUIRE_MODE_PRE_ACQUIRE : Session.TRANSFER_ACQUIRE_MODE_NO_ACQUIRE,
- new MessagePartListenerAdapter((BasicMessageConsumer_0_10) consumer), null,
- consumer.isExclusive() ? Option.EXCLUSIVE : Option.NO_OPTION);
if (! prefetch())
{
@@ -746,4 +746,10 @@ public class AMQSession_0_10 extends AMQSession
return subscriber;
}
+
+ Long requestQueueDepth(AMQDestination amqd)
+ {
+ return getQpidSession().queueQuery(amqd.getQueueName()).get().getMessageCount();
+ }
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java
index dd0fd9c457..740bd5dace 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java
@@ -30,8 +30,10 @@ import org.apache.qpid.client.failover.FailoverProtectedOperation;
import org.apache.qpid.client.failover.FailoverRetrySupport;
import org.apache.qpid.client.message.MessageFactoryRegistry;
import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.client.state.listener.SpecificMethodFrameListener;
import org.apache.qpid.common.AMQPFilterTypes;
import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9;
import org.apache.qpid.jms.Session;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.protocol.AMQMethodEvent;
@@ -79,11 +81,16 @@ public class AMQSession_0_8 extends AMQSession
defaultPrefetchLow);
}
+ private ProtocolVersion getProtocolVersion()
+ {
+ return getProtocolHandler().getProtocolVersion();
+ }
+
public void acknowledgeMessage(long deliveryTag, boolean multiple)
{
- final AMQFrame ackFrame =
- BasicAckBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag,
- multiple);
+ BasicAckBody body = getMethodRegistry().createBasicAckBody(deliveryTag, multiple);
+
+ final AMQFrame ackFrame = body.generateFrame(_channelId);
if (_logger.isDebugEnabled())
{
@@ -96,28 +103,17 @@ public class AMQSession_0_8 extends AMQSession
public void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments,
final AMQShortString exchangeName, final AMQDestination dest) throws AMQException, FailoverException
{
- AMQFrame queueBind = QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), arguments, // arguments
- exchangeName, // exchange
- false, // nowait
- queueName, // queue
- routingKey, // routingKey
- getTicket()); // ticket
-
- getProtocolHandler().syncWrite(queueBind, QueueBindOkBody.class);
+ getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createQueueBindBody
+ (getTicket(),queueName,exchangeName,routingKey,false,arguments).
+ generateFrame(_channelId), QueueBindOkBody.class);
}
public void sendClose(long timeout) throws AMQException, FailoverException
{
getProtocolHandler().closeSession(this);
-
- final AMQFrame frame = ChannelCloseBody.createAMQFrame
- (getChannelId(), getProtocolMajorVersion(), getProtocolMinorVersion(),
- 0, // classId
- 0, // methodId
- AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
- new AMQShortString("JMS client closing channel")); // replyText
-
- getProtocolHandler().syncWrite(frame, ChannelCloseOkBody.class, timeout);
+ getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createChannelCloseBody(AMQConstant.REPLY_SUCCESS.getCode(),
+ new AMQShortString("JMS client closing channel"), 0, 0).generateFrame(_channelId),
+ ChannelCloseOkBody.class, timeout);
// When control resumes at this point, a reply will have been received that
// indicates the broker has closed the channel successfully.
}
@@ -126,21 +122,14 @@ public class AMQSession_0_8 extends AMQSession
{
final AMQProtocolHandler handler = getProtocolHandler();
- handler.syncWrite(TxCommitBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion()), TxCommitOkBody.class);
+ handler.syncWrite(getProtocolHandler().getMethodRegistry().createTxCommitBody().generateFrame(_channelId), TxCommitOkBody.class);
}
public void sendCreateQueue(AMQShortString name, final boolean autoDelete, final boolean durable, final boolean exclusive) throws AMQException,
FailoverException
{
- AMQFrame queueDeclare = QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), null, // arguments
- autoDelete, // autoDelete
- durable, // durable
- exclusive, // exclusive
- false, // nowait
- false, // passive
- name, // queue
- getTicket()); // ticket
-
+ QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),name,false,durable,exclusive,autoDelete,false,null);
+ AMQFrame queueDeclare = body.generateFrame(_channelId);
getProtocolHandler().syncWrite(queueDeclare, QueueDeclareOkBody.class);
}
@@ -151,16 +140,29 @@ public class AMQSession_0_8 extends AMQSession
if (isStrictAMQP())
{
// We can't use the BasicRecoverBody-OK method as it isn't part of the spec.
- _connection.getProtocolHandler().writeFrame(
- BasicRecoverBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false)); // requeue
+
+ BasicRecoverBody body = getMethodRegistry().createBasicRecoverBody(false);
+ _connection.getProtocolHandler().writeFrame(body.generateFrame(_channelId));
_logger.warn("Session Recover cannot be guaranteed with STRICT_AMQP. Messages may arrive out of order.");
}
else
{
-
- _connection.getProtocolHandler().syncWrite(
- BasicRecoverBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false) // requeue
- , BasicRecoverOkBody.class);
+ // in Qpid the 0-8 spec was hacked to have a recover-ok method... this is bad
+ // in 0-9 we used the cleaner addition of a new sync recover method with its own ok
+ if(getProtocolHandler().getProtocolVersion().equals(ProtocolVersion.v8_0))
+ {
+ BasicRecoverBody body = getMethodRegistry().createBasicRecoverBody(false);
+ _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverOkBody.class);
+ }
+ else if(getProtocolVersion().equals(ProtocolVersion.v0_9))
+ {
+ BasicRecoverSyncBody body = ((MethodRegistry_0_9)getMethodRegistry()).createBasicRecoverSyncBody(false);
+ _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverSyncOkBody.class);
+ }
+ else
+ {
+ throw new RuntimeException("Unsupported version of the AMQP Protocol: " + getProtocolVersion());
+ }
}
}
@@ -189,13 +191,13 @@ public class AMQSession_0_8 extends AMQSession
{
if (_logger.isDebugEnabled())
{
- _logger.debug("Rejecting delivery tag:" + deliveryTag);
+ _logger.debug("Rejecting delivery tag:" + deliveryTag + ":SessionHC:" + this.hashCode());
}
- AMQFrame basicRejectBody = BasicRejectBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag,
- requeue);
+ BasicRejectBody body = getMethodRegistry().createBasicRejectBody(deliveryTag, requeue);
+ AMQFrame frame = body.generateFrame(_channelId);
- _connection.getProtocolHandler().writeFrame(basicRejectBody);
+ _connection.getProtocolHandler().writeFrame(frame);
}
}
@@ -214,10 +216,8 @@ public class AMQSession_0_8 extends AMQSession
{
public AMQMethodEvent execute() throws AMQException, FailoverException
{
- AMQFrame boundFrame = ExchangeBoundBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(),
- exchangeName, // exchange
- queueName, // queue
- routingKey); // routingKey
+ AMQFrame boundFrame = getProtocolHandler().getMethodRegistry().createExchangeBoundBody
+ (exchangeName, routingKey, queueName).generateFrame(_channelId);
return getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class);
@@ -227,7 +227,7 @@ public class AMQSession_0_8 extends AMQSession
// Extract and return the response code from the query.
ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod();
- return (responseBody.replyCode == 0);
+ return (responseBody.getReplyCode() == 0);
}
catch (AMQException e)
{
@@ -238,7 +238,6 @@ public class AMQSession_0_8 extends AMQSession
public void sendConsume(BasicMessageConsumer consumer, AMQShortString queueName, AMQProtocolHandler protocolHandler, boolean nowait,
String messageSelector, AMQShortString tag) throws AMQException, FailoverException
{
-
FieldTable arguments = FieldTableFactory.newFieldTable();
if ((messageSelector != null) && !messageSelector.equals(""))
{
@@ -255,18 +254,17 @@ public class AMQSession_0_8 extends AMQSession
arguments.put(AMQPFilterTypes.NO_CONSUME.getValue(), Boolean.TRUE);
}
- consumer.setConsumerTag(tag);
- // we must register the consumer in the map before we actually start listening
- _consumers.put(tag, consumer);
- // TODO: Be aware of possible changes to parameter order as versions change.
- AMQFrame jmsConsume = BasicConsumeBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), arguments, // arguments
- tag, // consumerTag
- consumer.isExclusive(), // exclusive
- consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, // noAck
- consumer.isNoLocal(), // noLocal
- nowait, // nowait
- queueName, // queue
- getTicket()); // ticket
+ BasicConsumeBody body = getMethodRegistry().createBasicConsumeBody(getTicket(),
+ queueName,
+ tag,
+ consumer.isNoLocal(),
+ consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE,
+ consumer.isExclusive(),
+ nowait,
+ arguments);
+
+
+ AMQFrame jmsConsume = body.generateFrame(_channelId);
if (nowait)
{
@@ -281,48 +279,37 @@ public class AMQSession_0_8 extends AMQSession
public void sendExchangeDeclare(final AMQShortString name, final AMQShortString type, final AMQProtocolHandler protocolHandler,
final boolean nowait) throws AMQException, FailoverException
{
- AMQFrame exchangeDeclare = ExchangeDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), null, // arguments
- false, // autoDelete
- false, // durable
- name, // exchange
- false, // internal
- nowait, // nowait
- false, // passive
- getTicket(), // ticket
- type); // type
+ ExchangeDeclareBody body = getMethodRegistry().createExchangeDeclareBody(getTicket(),name,type,false,false,false,false,nowait,null);
+ AMQFrame exchangeDeclare = body.generateFrame(_channelId);
protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class);
}
public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) throws AMQException, FailoverException
{
- AMQFrame queueDeclare = QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), null, // arguments
- amqd.isAutoDelete(), // autoDelete
- amqd.isDurable(), // durable
- amqd.isExclusive(), // exclusive
- false, // nowait
- false, // passive
- amqd.getAMQQueueName(), // queue
- getTicket()); // ticket
+ QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),amqd.getAMQQueueName(),false,amqd.isDurable(),amqd.isExclusive(),amqd.isAutoDelete(),false,null);
+
+ AMQFrame queueDeclare = body.generateFrame(_channelId);
protocolHandler.syncWrite(queueDeclare, QueueDeclareOkBody.class);
}
public void sendQueueDelete(final AMQShortString queueName) throws AMQException, FailoverException
{
- AMQFrame queueDeleteFrame = QueueDeleteBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false, // ifEmpty
- false, // ifUnused
- true, // nowait
- queueName, // queue
- getTicket()); // ticket
+ QueueDeleteBody body = getMethodRegistry().createQueueDeleteBody(getTicket(),
+ queueName,
+ false,
+ false,
+ true);
+ AMQFrame queueDeleteFrame = body.generateFrame(_channelId);
getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class);
}
public void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException
{
- AMQFrame channelFlowFrame = ChannelFlowBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), !suspend);
-
+ ChannelFlowBody body = getMethodRegistry().createChannelFlowBody(!suspend);
+ AMQFrame channelFlowFrame = body.generateFrame(_channelId);
_connection.getProtocolHandler().syncWrite(channelFlowFrame, ChannelFlowOkBody.class);
}
@@ -348,8 +335,9 @@ public class AMQSession_0_8 extends AMQSession
public void sendRollback() throws AMQException, FailoverException
{
- _connection.getProtocolHandler().syncWrite(TxRollbackBody.createAMQFrame(_channelId,
- getProtocolMajorVersion(), getProtocolMinorVersion()), TxRollbackOkBody.class);
+ TxRollbackBody body = getMethodRegistry().createTxRollbackBody();
+ AMQFrame frame = body.generateFrame(getChannelId());
+ getProtocolHandler().syncWrite(frame, TxRollbackOkBody.class);
}
public TemporaryQueue createTemporaryQueue() throws JMSException
@@ -360,68 +348,109 @@ public class AMQSession_0_8 extends AMQSession
}
public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException
- {
-
- checkNotClosed();
- AMQTopic origTopic = checkValidTopic(topic);
- AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection);
- TopicSubscriberAdaptor subscriber = _subscriptions.get(name);
- if (subscriber != null)
- {
- if (subscriber.getTopic().equals(topic))
- {
- throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange "
- + name);
- }
- else
- {
- unsubscribe(name);
- }
- }
- else
- {
- AMQShortString topicName;
- if (topic instanceof AMQTopic)
- {
- topicName = ((AMQTopic) topic).getRoutingKey();
- }
- else
- {
- topicName = new AMQShortString(topic.getTopicName());
- }
-
- if (_strictAMQP)
- {
- if (_strictAMQPFATAL)
- {
- throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP.");
- }
- else
- {
- _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' "
- + "for creation durableSubscriber. Requesting queue deletion regardless.");
- }
-
- deleteQueue(dest.getAMQQueueName());
- }
- else
- {
- // if the queue is bound to the exchange but NOT for this topic, then the JMS spec
- // says we must trash the subscription.
- if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName())
- && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName))
- {
- deleteQueue(dest.getAMQQueueName());
- }
- }
- }
-
- subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest));
-
- _subscriptions.put(name, subscriber);
- _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name);
-
- return subscriber;
- }
+ {
+
+ checkNotClosed();
+ AMQTopic origTopic = checkValidTopic(topic);
+ AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection);
+ TopicSubscriberAdaptor subscriber = _subscriptions.get(name);
+ if (subscriber != null)
+ {
+ if (subscriber.getTopic().equals(topic))
+ {
+ throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange "
+ + name);
+ }
+ else
+ {
+ unsubscribe(name);
+ }
+ }
+ else
+ {
+ AMQShortString topicName;
+ if (topic instanceof AMQTopic)
+ {
+ topicName = ((AMQTopic) topic).getRoutingKey();
+ }
+ else
+ {
+ topicName = new AMQShortString(topic.getTopicName());
+ }
+
+ if (_strictAMQP)
+ {
+ if (_strictAMQPFATAL)
+ {
+ throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP.");
+ }
+ else
+ {
+ _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' "
+ + "for creation durableSubscriber. Requesting queue deletion regardless.");
+ }
+
+ deleteQueue(dest.getAMQQueueName());
+ }
+ else
+ {
+ // if the queue is bound to the exchange but NOT for this topic, then the JMS spec
+ // says we must trash the subscription.
+ if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName())
+ && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName))
+ {
+ deleteQueue(dest.getAMQQueueName());
+ }
+ }
+ }
+
+ subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest));
+
+ _subscriptions.put(name, subscriber);
+ _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name);
+
+ return subscriber;
+ }
+
+ class QueueDeclareOkHandler extends SpecificMethodFrameListener
+ {
+
+ private long _messageCount;
+ private long _consumerCount;
+
+ public QueueDeclareOkHandler()
+ {
+ super(getChannelId(), QueueDeclareOkBody.class);
+ }
+
+ public boolean processMethod(int channelId, AMQMethodBody frame) //throws AMQException
+ {
+ boolean matches = super.processMethod(channelId, frame);
+ if (matches)
+ {
+ QueueDeclareOkBody declareOk = (QueueDeclareOkBody) frame;
+ _messageCount = declareOk.getMessageCount();
+ _consumerCount = declareOk.getConsumerCount();
+ }
+ return matches;
+ }
+
+ }
+
+ Long requestQueueDepth(AMQDestination amqd) throws AMQException, FailoverException
+ {
+ AMQFrame queueDeclare =
+ getMethodRegistry().createQueueDeclareBody(getTicket(),
+ amqd.getAMQQueueName(),
+ true,
+ amqd.isDurable(),
+ amqd.isExclusive(),
+ amqd.isAutoDelete(),
+ false,
+ null).generateFrame(_channelId);
+ QueueDeclareOkHandler okHandler = new QueueDeclareOkHandler();
+ getProtocolHandler().writeCommandFrameAndWaitForReply(queueDeclare, okHandler);
+ return okHandler._messageCount;
+ }
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java
index 6c954ec3df..7b5781530b 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java
@@ -24,6 +24,7 @@ import org.apache.qpid.framing.AMQShortString;
import javax.jms.JMSException;
import javax.jms.TemporaryTopic;
+import java.util.UUID;
/**
* AMQ implementation of TemporaryTopic.
@@ -38,7 +39,7 @@ class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic, TemporaryDes
*/
public AMQTemporaryTopic(AMQSession session)
{
- super(session.getTemporaryTopicExchangeName(),new AMQShortString("TempQueue" + Long.toString(System.currentTimeMillis())));
+ super(session.getTemporaryTopicExchangeName(),new AMQShortString("tmp_" + UUID.randomUUID()));
_session = session;
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java
index fcc36cd3fd..40041afdc6 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java
@@ -120,8 +120,7 @@ public class AMQTopic extends AMQDestination implements Topic
public boolean isNameRequired()
{
- // Topics always rely on a server generated queue name.
- return false;
+ return !isDurable();
}
/**
@@ -136,4 +135,17 @@ public class AMQTopic extends AMQDestination implements Topic
public void setQueueName(String queueName)
{
}
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof AMQTopic)
+ && ((AMQTopic)o).getExchangeName().equals(getExchangeName())
+ && ((AMQTopic)o).getRoutingKey().equals(getRoutingKey());
+
+ }
+
+ public int hashCode()
+ {
+ return getExchangeName().hashCode() + getRoutingKey().hashCode();
+ }
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
index 015a2ccc57..1741903bb8 100644
--- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
+++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
@@ -20,45 +20,47 @@
*/
package org.apache.qpid.client;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageListener;
-
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.failover.FailoverException;
import org.apache.qpid.client.message.AbstractJMSMessage;
import org.apache.qpid.client.message.MessageFactoryRegistry;
import org.apache.qpid.client.message.UnprocessedMessage;
import org.apache.qpid.client.protocol.AMQProtocolHandler;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.*;
import org.apache.qpid.jms.MessageConsumer;
import org.apache.qpid.jms.Session;
-import org.apache.qpid.AMQException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.TreeSet;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
public abstract class BasicMessageConsumer<H, B> extends Closeable implements MessageConsumer
{
private static final Logger _logger = LoggerFactory.getLogger(BasicMessageConsumer.class);
- /**
- * The connection being used by this consumer
- */
- protected AMQConnection _connection;
+ /** The connection being used by this consumer */
+ protected final AMQConnection _connection;
- protected String _messageSelector;
+ protected final String _messageSelector;
- private boolean _noLocal;
+ private final boolean _noLocal;
- private AMQDestination _destination;
+ private final AMQDestination _destination;
/**
* When true indicates that a blocking receive call is in progress
@@ -69,15 +71,11 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
*/
private final AtomicReference<MessageListener> _messageListener = new AtomicReference<MessageListener>();
- /**
- * The consumer tag allows us to close the consumer by sending a jmsCancel method to the broker
- */
+ /** The consumer tag allows us to close the consumer by sending a jmsCancel method to the broker */
protected AMQShortString _consumerTag;
- /**
- * We need to know the channel id when constructing frames
- */
- protected int _channelId;
+ /** We need to know the channel id when constructing frames */
+ protected final int _channelId;
/**
* Used in the blocking receive methods to receive a message from the Session thread. <p/> Or to notify of errors
@@ -85,40 +83,40 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
*/
protected final ArrayBlockingQueue _synchronousQueue;
- protected MessageFactoryRegistry _messageFactory;
+ protected final MessageFactoryRegistry _messageFactory;
protected final AMQSession _session;
- protected AMQProtocolHandler _protocolHandler;
+ protected final AMQProtocolHandler _protocolHandler;
/**
* We need to store the "raw" field table so that we can resubscribe in the event of failover being required
*/
- private FieldTable _rawSelectorFieldTable;
+ private final FieldTable _rawSelectorFieldTable;
/**
* We store the high water prefetch field in order to be able to reuse it when resubscribing in the event of
* failover
*/
- private int _prefetchHigh;
+ private final int _prefetchHigh;
/**
* We store the low water prefetch field in order to be able to reuse it when resubscribing in the event of
* failover
*/
- private int _prefetchLow;
+ private final int _prefetchLow;
/**
* We store the exclusive field in order to be able to reuse it when resubscribing in the event of failover
*/
- private boolean _exclusive;
+ private final boolean _exclusive;
/**
* The acknowledge mode in force for this consumer. Note that the AMQP protocol allows different ack modes per
* consumer whereas JMS defines this at the session level, hence why we associate it with the consumer in our
* implementation.
*/
- private int _acknowledgeMode;
+ private final int _acknowledgeMode;
/**
* Number of messages unacknowledged in DUPS_OK_ACKNOWLEDGE mode
@@ -132,6 +130,19 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
private boolean _dups_ok_acknowledge_send;
/**
+ * List of tags delievered, The last of which which should be acknowledged on commit in transaction mode.
+ */
+ private ConcurrentLinkedQueue<Long> _receivedDeliveryTags = new ConcurrentLinkedQueue<Long>();
+
+ /** The last tag that was "multiple" acknowledged on this session (if transacted) */
+ private long _lastAcked;
+
+ /** set of tags which have previously been acked; but not part of the multiple ack (transacted mode only) */
+ private final SortedSet<Long> _previouslyAcked = new TreeSet<Long>();
+
+ private final Object _commitLock = new Object();
+
+ /**
* The thread that was used to call receive(). This is important for being able to interrupt that thread if a
* receive() is in progress.
*/
@@ -148,12 +159,13 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
* autoClose denotes that the consumer will automatically cancel itself when there are no more messages to receive
* on the queue. This is used for queue browsing.
*/
- private boolean _autoClose;
- private boolean _closeWhenNoMessages;
+ private final boolean _autoClose;
- private boolean _noConsume;
+ private final boolean _noConsume;
private List<StackTraceElement> _closedStack = null;
+
+
protected BasicMessageConsumer(int channelId, AMQConnection connection, AMQDestination destination,
String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory,
AMQSession session, AMQProtocolHandler protocolHandler,
@@ -172,7 +184,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
_prefetchHigh = prefetchHigh;
_prefetchLow = prefetchLow;
_exclusive = exclusive;
- _acknowledgeMode = acknowledgeMode;
+
_synchronousQueue = new ArrayBlockingQueue(prefetchHigh, true);
_autoClose = autoClose;
_noConsume = noConsume;
@@ -182,6 +194,10 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
{
_acknowledgeMode = Session.NO_ACKNOWLEDGE;
}
+ else
+ {
+ _acknowledgeMode = acknowledgeMode;
+ }
}
public AMQDestination getDestination()
@@ -254,14 +270,14 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
if (messageListener != null)
{
- // handle case where connection has already been started, and the dispatcher has alreaded started
+ //todo: handle case where connection has already been started, and the dispatcher has alreaded started
// putting values on the _synchronousQueue
synchronized (_session)
{
_messageListener.set(messageListener);
_session.setHasMessageListeners();
- _session.startDistpatcherIfNecessary();
+ _session.startDispatcherIfNecessary();
}
}
}
@@ -273,12 +289,32 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
{
_session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag());
}
-
+
_session.setInRecovery(false);
}
- private void acquireReceiving() throws JMSException
+ /**
+ * @param immediate if true then return immediately if the connection is failing over
+ *
+ * @return boolean if the acquisition was successful
+ *
+ * @throws JMSException if a listener has already been set or another thread is receiving
+ * @throws InterruptedException if interrupted
+ */
+ private boolean acquireReceiving(boolean immediate) throws JMSException, InterruptedException
{
+ if (_connection.isFailingOver())
+ {
+ if (immediate)
+ {
+ return false;
+ }
+ else
+ {
+ _connection.blockUntilNotFailingOver();
+ }
+ }
+
if (!_receiving.compareAndSet(false, true))
{
throw new javax.jms.IllegalStateException("Another thread is already receiving.");
@@ -290,6 +326,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
}
_receivingThread = Thread.currentThread();
+ return true;
}
private void releaseReceiving()
@@ -343,26 +380,30 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
checkPreConditions();
- acquireReceiving();
-
- _session.startDistpatcherIfNecessary();
-
try
{
- if (closeOnAutoClose())
+ acquireReceiving(false);
+ }
+ catch (InterruptedException e)
+ {
+ _logger.warn("Interrupted acquire: " + e);
+ if (isClosed())
{
return null;
}
+ }
- Object o = getMessageFromQueue(l);
+ _session.startDispatcherIfNecessary();
+ try
+ {
+ Object o = getMessageFromQueue(l);
final AbstractJMSMessage m = returnMessageOrThrow(o);
if (m != null)
{
preApplicationProcessing(m);
postDeliver(m);
}
-
return m;
}
catch (InterruptedException e)
@@ -395,35 +436,34 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
return o;
}
- private boolean closeOnAutoClose() throws JMSException
- {
- if (isAutoClose() && _closeWhenNoMessages && _synchronousQueue.isEmpty())
- {
- close(false);
-
- return true;
- }
- else
- {
- return false;
- }
- }
-
public Message receiveNoWait() throws JMSException
{
checkPreConditions();
- acquireReceiving();
-
- _session.startDistpatcherIfNecessary();
-
try
{
- if (closeOnAutoClose())
+ if (!acquireReceiving(true))
{
+ //If we couldn't acquire the receiving thread then return null.
+ // This will occur if failing over.
return null;
}
+ }
+ catch (InterruptedException e)
+ {
+ /*
+ * This seems slightly shoddy but should never actually be executed
+ * since we told acquireReceiving to return immediately and it shouldn't
+ * block on anything.
+ */
+
+ return null;
+ }
+
+ _session.startDispatcherIfNecessary();
+ try
+ {
Object o = getMessageFromQueue(-1);
final AbstractJMSMessage m = returnMessageOrThrow(o);
if (m != null)
@@ -450,7 +490,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
* We can get back either a Message or an exception from the queue. This method examines the argument and deals with
* it by throwing it (if an exception) or returning it (in any other case).
*
- * @param o
+ * @param o the object to return or throw
* @return a message only if o is a Message
* @throws JMSException if the argument is a throwable. If it is a JMSException it is rethrown as is, but if not a
* JMSException is created with the linked exception set appropriately
@@ -481,61 +521,69 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
public void close(boolean sendClose) throws JMSException
{
- // synchronized (_closed)
-
if (_logger.isInfoEnabled())
{
_logger.info("Closing consumer:" + debugIdentity());
}
- synchronized (_connection.getFailoverMutex())
+ if (!_closed.getAndSet(true))
{
- if (!_closed.getAndSet(true))
+ if (_logger.isDebugEnabled())
{
- if (_logger.isTraceEnabled())
- {
- if (_closedStack != null)
- {
- _logger.trace(_consumerTag + " close():" + Arrays.asList(Thread.currentThread().getStackTrace())
- .subList(3, 6));
- _logger.trace(_consumerTag + " previously:" + _closedStack.toString());
- }
- else
- {
- _closedStack = Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6);
- }
- }
-
- if (sendClose)
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ if (_closedStack != null)
{
- // TODO: Be aware of possible changes to parameter order as versions change.
- sendCancel();
+ _logger.debug(_consumerTag + " previously:" + _closedStack.toString());
}
else
{
- // //fixme this probably is not right
- // if (!isNoConsume())
- //{ // done in BasicCancelOK Handler but not sending one so just deregister.
- // deregisterConsumer();
- //}
+ _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1);
}
+ }
- deregisterConsumer();
-
- if (_messageListener != null && _receiving.get() && _receivingThread != null)
+ if (sendClose)
+ {
+ // The Synchronized block only needs to protect network traffic.
+ synchronized (_connection.getFailoverMutex())
{
- if (_logger.isInfoEnabled())
+ try
{
- _logger.info("Interrupting thread: " + _receivingThread);
+ sendCancel();
}
+ catch (AMQException e)
+ {
+ throw new JMSAMQException("Error closing consumer: " + e, e);
+ }
+ catch (FailoverException e)
+ {
+ throw new JMSAMQException("FailoverException interrupted basic cancel.", e);
+ }
+ }
+ }
+ else
+ {
+ // //fixme this probably is not right
+ // if (!isNoConsume())
+ { // done in BasicCancelOK Handler but not sending one so just deregister.
+ deregisterConsumer();
+ }
+ }
- _receivingThread.interrupt();
+ // This will occur if session.close is called closing all consumers we may be blocked waiting for a receive
+ // so we need to let it know it is time to close.
+ if ((_messageListener != null) && _receiving.get())
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Interrupting thread: " + _receivingThread);
}
+
+ _receivingThread.interrupt();
}
}
}
- public abstract void sendCancel() throws JMSAMQException;
+ abstract void sendCancel() throws AMQException, FailoverException;
/**
* Called when you need to invalidate a consumer. Used for example when failover has occurred and the client has
@@ -547,13 +595,14 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
{
_closed.set(true);
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
if (_closedStack != null)
{
- _logger.trace(_consumerTag + " markClosed():" + Arrays
- .asList(Thread.currentThread().getStackTrace()).subList(3, 8));
- _logger.trace(_consumerTag + " previously:" + _closedStack.toString());
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ _logger.debug(_consumerTag + " markClosed():"
+ + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
+ _logger.debug(_consumerTag + " previously:" + _closedStack.toString());
}
else
{
@@ -570,9 +619,8 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
* message listener or a synchronous receive() caller.
*
* @param messageFrame the raw unprocessed mesage
- * @param channelId channel on which this message was sent
*/
- void notifyMessage(UnprocessedMessage messageFrame, int channelId)
+ void notifyMessage(UnprocessedMessage messageFrame)
{
final boolean debug = _logger.isDebugEnabled();
@@ -584,6 +632,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
try
{
AbstractJMSMessage jmsMessage = createJMSMessageFromUnprocessedMessage(messageFrame);
+
if (debug)
{
_logger.debug("Message is of type: " + jmsMessage.getClass().getName());
@@ -594,11 +643,9 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
// if (!_closed.get())
{
- jmsMessage.setConsumer(this);
-
preDeliver(jmsMessage);
- notifyMessage(jmsMessage, channelId);
+ notifyMessage(jmsMessage);
}
// else
// {
@@ -624,12 +671,8 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
public abstract AbstractJMSMessage createJMSMessageFromUnprocessedMessage(UnprocessedMessage<H, B> messageFrame)
throws Exception;
-
- /**
- * @param jmsMessage this message has already been processed so can't redo preDeliver
- * @param channelId
- */
- public void notifyMessage(AbstractJMSMessage jmsMessage, int channelId)
+ /** @param jmsMessage this message has already been processed so can't redo preDeliver */
+ public void notifyMessage(AbstractJMSMessage jmsMessage)
{
try
{
@@ -700,26 +743,6 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
break;
case Session.DUPS_OK_ACKNOWLEDGE:
- if (++_outstanding >= _prefetchHigh)
- {
- _dups_ok_acknowledge_send = true;
- }
-
- if (_outstanding <= _prefetchLow)
- {
- _dups_ok_acknowledge_send = false;
- }
-
- if (_dups_ok_acknowledge_send)
- {
- if (!_session.isInRecovery())
- {
- _session.acknowledgeMessage(msg.getDeliveryTag(), true);
- }
- }
-
- break;
-
case Session.AUTO_ACKNOWLEDGE:
// we do not auto ack a message if the application code called recover()
if (!_session.isInRecovery())
@@ -728,7 +751,6 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
}
break;
-
case Session.SESSION_TRANSACTED:
if (isNoConsume())
{
@@ -743,22 +765,105 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
}
}
+
+ /**
+ * Acknowledge up to last message delivered (if any). Used when commiting.
+ *
+ * @return the lastDeliveryTag to acknowledge
+ */
+ Long getLastDelivered()
+ {
+ if (!_receivedDeliveryTags.isEmpty())
+ {
+ Long lastDeliveryTag = _receivedDeliveryTags.poll();
+
+ while (!_receivedDeliveryTags.isEmpty())
+ {
+ lastDeliveryTag = _receivedDeliveryTags.poll();
+ }
+
+ assert _receivedDeliveryTags.isEmpty();
+
+ return lastDeliveryTag;
+ }
+
+ return null;
+ }
+
+ /**
+ * Acknowledge up to last message delivered (if any). Used when commiting.
+ */
+ void acknowledgeDelivered()
+ {
+ synchronized(_commitLock)
+ {
+ ArrayList<Long> tagsToAck = new ArrayList<Long>();
+
+ while (!_receivedDeliveryTags.isEmpty())
+ {
+ tagsToAck.add(_receivedDeliveryTags.poll());
+ }
+
+ Collections.sort(tagsToAck);
+
+ long prevAcked = _lastAcked;
+ long oldAckPoint = -1;
+
+ while(oldAckPoint != prevAcked)
+ {
+ oldAckPoint = prevAcked;
+
+ Iterator<Long> tagsToAckIterator = tagsToAck.iterator();
+
+ while(tagsToAckIterator.hasNext() && tagsToAckIterator.next() == prevAcked+1)
+ {
+ tagsToAckIterator.remove();
+ prevAcked++;
+ }
+
+ Iterator<Long> previousAckIterator = _previouslyAcked.iterator();
+ while(previousAckIterator.hasNext() && previousAckIterator.next() == prevAcked+1)
+ {
+ previousAckIterator.remove();
+ prevAcked++;
+ }
+
+ }
+ if(prevAcked != _lastAcked)
+ {
+ _session.acknowledgeMessage(prevAcked, true);
+ _lastAcked = prevAcked;
+ }
+
+ Iterator<Long> tagsToAckIterator = tagsToAck.iterator();
+
+ while(tagsToAckIterator.hasNext())
+ {
+ Long tag = tagsToAckIterator.next();
+ _session.acknowledgeMessage(tag, false);
+ _previouslyAcked.add(tag);
+ }
+ }
+ }
+
+
void notifyError(Throwable cause)
{
// synchronized (_closed)
{
_closed.set(true);
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (_closedStack != null)
{
- _logger.trace(_consumerTag + " notifyError():" + Arrays
- .asList(Thread.currentThread().getStackTrace()).subList(3, 8));
- _logger.trace(_consumerTag + " previously" + _closedStack.toString());
+ _logger.debug(_consumerTag + " notifyError():"
+ + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
+ _logger.debug(_consumerTag + " previously" + _closedStack.toString());
}
else
{
- _closedStack = Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 8);
+ _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1);
}
}
}
@@ -823,20 +928,13 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
return _noConsume;
}
- public void closeWhenNoMessages(boolean b)
+ public void rollback()
{
- _closeWhenNoMessages = b;
-
- if (_closeWhenNoMessages && _synchronousQueue.isEmpty() && _receiving.get() && (_messageListener != null))
- {
- _receivingThread.interrupt();
- }
-
+ rollbackPendingMessages();
}
- public void rollback()
+ public void rollbackPendingMessages()
{
- // rollback pending messages
if (_synchronousQueue.size() > 0)
{
if (_logger.isDebugEnabled())
@@ -847,6 +945,9 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
Iterator iterator = _synchronousQueue.iterator();
+ int initialSize = _synchronousQueue.size();
+
+ boolean removed = false;
while (iterator.hasNext())
{
@@ -855,22 +956,30 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
{
_session.rejectMessage(((AbstractJMSMessage) o), true);
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Rejected message:" + ((AbstractJMSMessage) o).getDeliveryTag());
+ _logger.debug("Rejected message:" + ((AbstractJMSMessage) o).getDeliveryTag());
}
iterator.remove();
+ removed = true;
}
else
{
- _logger.error("Queue contained a :" + o
- .getClass() + " unable to reject as it is not an AbstractJMSMessage. Will be cleared");
+ _logger.error("Queue contained a :" + o.getClass()
+ + " unable to reject as it is not an AbstractJMSMessage. Will be cleared");
iterator.remove();
+ removed = true;
+ }
}
+
+ if (removed && (initialSize == _synchronousQueue.size()))
+ {
+ _logger.error("Queue had content removed but didn't change in size." + initialSize);
}
+
if (_synchronousQueue.size() != 0)
{
_logger.warn("Queue was not empty after rejecting all messages Remaining:" + _synchronousQueue.size());
@@ -883,7 +992,7 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
public String debugIdentity()
{
- return String.valueOf(_consumerTag);
+ return String.valueOf(_consumerTag) + "[" + System.identityHashCode(this) + "]";
}
public void clearReceiveQueue()
@@ -891,7 +1000,6 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
_synchronousQueue.clear();
}
-
public void start()
{
// do nothing as this is a 0_10 feature
@@ -923,4 +1031,12 @@ public abstract class BasicMessageConsumer<H, B> extends Closeable implements Me
{
_session.addBindingKey(this,amqd,routingKey);
}
+
+ /** to be called when a failover has occured */
+ public void failedOver()
+ {
+ clearReceiveQueue();
+ // TGM FIXME: think this should just be removed
+ // clearUnackedMessages();
+ }
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java
index 4f2d5d8c34..d92899087b 100644
--- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java
+++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java
@@ -117,7 +117,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By
* @param jmsMessage this message has already been processed so can't redo preDeliver
* @param channelId
*/
- public void notifyMessage(AbstractJMSMessage jmsMessage, int channelId)
+ public void notifyMessage(AbstractJMSMessage jmsMessage)
{
boolean messageOk = false;
try
@@ -147,7 +147,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By
MessageCreditUnit.MESSAGE, 1);
}
_logger.debug("messageOk, trying to notify");
- super.notifyMessage(jmsMessage, channelId);
+ super.notifyMessage(jmsMessage);
}
}
@@ -162,7 +162,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By
{
int channelId = getSession().getChannelId();
long deliveryId = message.getMessageTransferId();
- String consumerTag = getConsumerTag().toString();
+ AMQShortString consumerTag = getConsumerTag();
AMQShortString exchange;
AMQShortString routingKey;
boolean redelivered = false;
@@ -211,20 +211,13 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By
* This method is invoked when this consumer is stopped.
* It tells the broker to stop delivering messages to this consumer.
*/
- public void sendCancel() throws JMSAMQException
+ void sendCancel() throws AMQException
{
((AMQSession_0_10) getSession()).getQpidSession().messageCancel(getConsumerTag().toString());
((AMQSession_0_10) getSession()).getQpidSession().sync();
// confirm cancel
getSession().confirmConsumerCancelled(getConsumerTag());
- try
- {
- ((AMQSession_0_10) getSession()).getCurrentException();
- }
- catch (AMQException e)
- {
- throw new JMSAMQException("Problem when stopping consumer", e);
- }
+ ((AMQSession_0_10) getSession()).getCurrentException();
}
void notifyMessage(UnprocessedMessage messageFrame, int channelId)
@@ -261,7 +254,7 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By
}
((UnprocessedMessage_0_10) messageFrame).setReplyToURL(replyToUrl);
}
- super.notifyMessage(messageFrame, channelId);
+ super.notifyMessage(messageFrame);
}
protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException
@@ -486,5 +479,4 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<Struct[], By
}
return o;
}
-
-} \ No newline at end of file
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java
index 1bc9e4d855..5414e25539 100644
--- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java
+++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java
@@ -51,30 +51,17 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<ContentHeader
acknowledgeMode, noConsume, autoClose);
}
- public void sendCancel() throws JMSAMQException
+ void sendCancel() throws AMQException, FailoverException
{
- final AMQFrame cancelFrame =
- BasicCancelBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(),
- _protocolHandler.getProtocolMinorVersion(), _consumerTag, // consumerTag
- false); // nowait
+ BasicCancelBody body = getSession().getMethodRegistry().createBasicCancelBody(_consumerTag, false);
- try
- {
- _protocolHandler.syncWrite(cancelFrame, BasicCancelOkBody.class);
+ final AMQFrame cancelFrame = body.generateFrame(_channelId);
- if (_logger.isDebugEnabled())
- {
- _logger.debug("CancelOk'd for consumer:" + debugIdentity());
- }
+ _protocolHandler.syncWrite(cancelFrame, BasicCancelOkBody.class);
- }
- catch (AMQException e)
+ if (_logger.isDebugEnabled())
{
- throw new JMSAMQException("Error closing consumer: " + e, e);
- }
- catch (FailoverException e)
- {
- throw new JMSAMQException("FailoverException interrupted basic cancel.", e);
+ _logger.debug("CancelOk'd for consumer:" + debugIdentity());
}
}
@@ -86,5 +73,5 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<ContentHeader
messageFrame.getRoutingKey(), messageFrame.getContentHeader(), messageFrame.getBodies());
}
-
+
} \ No newline at end of file
diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
index 4c45ec6638..5bb2955399 100644
--- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
+++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
@@ -36,11 +36,18 @@ import javax.jms.StreamMessage;
import javax.jms.TextMessage;
import javax.jms.Topic;
+import org.apache.mina.common.ByteBuffer;
import org.apache.qpid.AMQException;
import org.apache.qpid.client.message.AbstractJMSMessage;
import org.apache.qpid.client.message.MessageConverter;
import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.CompositeAMQDataBlock;
import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ExchangeDeclareBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,7 +58,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
private AMQConnection _connection;
/**
- * If true, messages will not get a timestamp.
+ * If true, messages will not get a timestamp.
*/
protected boolean _disableTimestamps;
@@ -103,7 +110,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
private long _producerId;
/**
- * The session used to create this producer
+ * The session used to create this producer
*/
protected AMQSession _session;
@@ -118,8 +125,8 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
private static final ContentBody[] NO_CONTENT_BODIES = new ContentBody[0];
protected BasicMessageProducer(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId,
- AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory,
- boolean waitUntilSent)
+ AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory,
+ boolean waitUntilSent)
{
_connection = connection;
_destination = destination;
@@ -146,7 +153,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
}
}
- public abstract void declareDestination(AMQDestination destination);
+ abstract void declareDestination(AMQDestination destination);
public void setDisableMessageID(boolean b) throws JMSException
{
@@ -181,7 +188,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
if ((i != DeliveryMode.NON_PERSISTENT) && (i != DeliveryMode.PERSISTENT))
{
throw new JMSException("DeliveryMode must be either NON_PERSISTENT or PERSISTENT. Value of " + i
- + " is illegal");
+ + " is illegal");
}
_deliveryMode = i;
@@ -293,7 +300,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
{
validateDestination(destination);
sendImpl((AMQDestination) destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory,
- _immediate);
+ _immediate);
}
}
@@ -310,7 +317,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
}
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
- boolean mandatory) throws JMSException
+ boolean mandatory) throws JMSException
{
checkPreConditions();
checkDestination(destination);
@@ -322,7 +329,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
}
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate) throws JMSException
+ boolean mandatory, boolean immediate) throws JMSException
{
checkPreConditions();
checkDestination(destination);
@@ -334,7 +341,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
}
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException
+ boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException
{
checkPreConditions();
checkDestination(destination);
@@ -342,7 +349,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
{
validateDestination(destination);
sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate,
- waitUntilSent);
+ waitUntilSent);
}
}
@@ -388,7 +395,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
else
{
throw new JMSException("Unable to send message, due to class conversion error: "
- + message.getClass().getName());
+ + message.getClass().getName());
}
}
}
@@ -398,14 +405,19 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
if (!(destination instanceof AMQDestination))
{
throw new JMSException("Unsupported destination class: "
- + ((destination != null) ? destination.getClass() : null));
+ + ((destination != null) ? destination.getClass() : null));
}
- declareDestination((AMQDestination) destination);
+ AMQDestination amqDestination = (AMQDestination) destination;
+ if(!amqDestination.isExchangeExistsChecked())
+ {
+ declareDestination(amqDestination);
+ amqDestination.setExchangeExistsChecked(true);
+ }
}
protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate) throws JMSException
+ boolean mandatory, boolean immediate) throws JMSException
{
sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent);
}
@@ -420,16 +432,27 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
* @param timeToLive
* @param mandatory
* @param immediate
+ *
* @throws JMSException
*/
protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive,
- boolean mandatory, boolean immediate, boolean wait) throws JMSException
+ boolean mandatory, boolean immediate, boolean wait) throws JMSException
{
checkTemporaryDestination(destination);
origMessage.setJMSDestination(destination);
AbstractJMSMessage message = convertToNativeMessage(origMessage);
+ if (_transacted)
+ {
+ if (_session.hasFailedOver() && _session.isDirty())
+ {
+ throw new JMSAMQException("Failover has occurred and session is dirty so unable to send.",
+ new AMQSessionDirtyException("Failover has occurred and session is dirty " +
+ "so unable to send."));
+ }
+ }
+
if (_disableMessageId)
{
message.setJMSMessageID(null);
@@ -456,14 +479,27 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
type = AMQDestination.UNKNOWN_TYPE;
}
- // message.getJmsHeaders().setInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName(), type);
+ sendMessage(destination, origMessage, message, deliveryMode, priority, timeToLive, mandatory, immediate, wait);
+
+ if (message != origMessage)
+ {
+ _logger.debug("Updating original message");
+ origMessage.setJMSPriority(message.getJMSPriority());
+ origMessage.setJMSTimestamp(message.getJMSTimestamp());
+ _logger.debug("Setting JMSExpiration:" + message.getJMSExpiration());
+ origMessage.setJMSExpiration(message.getJMSExpiration());
+ origMessage.setJMSMessageID(message.getJMSMessageID());
+ }
- sendMessage(destination, origMessage, message, deliveryMode, priority, timeToLive,
- mandatory, immediate, wait);
+ if (_transacted)
+ {
+ _session.markDirty();
+ }
}
- public abstract void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, int deliveryMode,
- int priority, long timeToLive, boolean mandatory, boolean immediate, boolean wait)throws JMSException;
+ abstract void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message,
+ int deliveryMode, int priority, long timeToLive, boolean mandatory,
+ boolean immediate, boolean wait)throws JMSException;
private void checkTemporaryDestination(AMQDestination destination) throws JMSException
{
@@ -520,7 +556,7 @@ public abstract class BasicMessageProducer extends Closeable implements org.apac
if ((_destination != null) && (suppliedDestination != null))
{
throw new UnsupportedOperationException(
- "This message producer was created with a Destination, therefore you cannot use an unidentified Destination");
+ "This message producer was created with a Destination, therefore you cannot use an unidentified Destination");
}
if (suppliedDestination == null)
diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java
index 2e31c43b4c..5fd6c23f68 100644
--- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java
+++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java
@@ -55,7 +55,7 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer
mandatory, waitUntilSent);
}
- public void declareDestination(AMQDestination destination)
+ void declareDestination(AMQDestination destination)
{
((AMQSession_0_10) getSession()).getQpidSession().exchangeDeclare(destination.getExchangeName().toString(),
destination.getExchangeClass().toString(),
@@ -67,9 +67,9 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer
/**
* Sends a message to a given destination
*/
- public void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message,
- int deliveryMode, int priority, long timeToLive, boolean mandatory, boolean immediate,
- boolean wait) throws JMSException
+ void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message,
+ int deliveryMode, int priority, long timeToLive, boolean mandatory, boolean immediate,
+ boolean wait) throws JMSException
{
message.prepareForSending();
if (message.get010Message() == null)
diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java
index 0e79069463..ff991b1a03 100644
--- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java
+++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java
@@ -45,38 +45,37 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer
super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory,waitUntilSent);
}
- public void declareDestination(AMQDestination destination)
+ void declareDestination(AMQDestination destination)
{
+
+ ExchangeDeclareBody body = getSession().getMethodRegistry().createExchangeDeclareBody(_session.getTicket(),
+ destination.getExchangeName(),
+ destination.getExchangeClass(),
+ false,
+ false,
+ false,
+ false,
+ true,
+ null);
// Declare the exchange
// Note that the durable and internal arguments are ignored since passive is set to false
- // TODO: Be aware of possible changes to parameter order as versions change.
- AMQFrame declare =
- ExchangeDeclareBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(),
- _protocolHandler.getProtocolMinorVersion(), null, // arguments
- false, // autoDelete
- false, // durable
- destination.getExchangeName(), // exchange
- false, // internal
- true, // nowait
- false, // passive
- _session.getTicket(), // ticket
- destination.getExchangeClass()); // type
+
+ AMQFrame declare = body.generateFrame(_channelId);
+
_protocolHandler.writeFrame(declare);
}
- public void sendMessage(AMQDestination destination, Message origMessage,AbstractJMSMessage message,
- int deliveryMode,int priority, long timeToLive, boolean mandatory, boolean immediate, boolean wait) throws JMSException
+ void sendMessage(AMQDestination destination, Message origMessage,AbstractJMSMessage message,
+ int deliveryMode,int priority, long timeToLive, boolean mandatory, boolean immediate,
+ boolean wait) throws JMSException
{
-// AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
- // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
- // Be aware of possible changes to parameter order as versions change.
- AMQFrame publishFrame =
- BasicPublishBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(),
- _protocolHandler.getProtocolMinorVersion(), destination.getExchangeName(), // exchange
- immediate, // immediate
- mandatory, // mandatory
- destination.getRoutingKey(), // routingKey
- _session.getTicket()); // ticket
+ BasicPublishBody body = getSession().getMethodRegistry().createBasicPublishBody(_session.getTicket(),
+ destination.getExchangeName(),
+ destination.getRoutingKey(),
+ mandatory,
+ immediate);
+
+ AMQFrame publishFrame = body.generateFrame(_channelId);
message.prepareForSending();
ByteBuffer payload = message.getData();
@@ -114,13 +113,13 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer
_logger.debug("Sending content body frames to " + destination);
}
- // weight argument of zero indicates no child content headers, just bodies
- // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
- // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+
+ // TODO: This is a hacky way of getting the AMQP class-id for the Basic class
+ int classIfForBasic = getSession().getMethodRegistry().createBasicQosOkBody().getClazz();
+
AMQFrame contentHeaderFrame =
ContentHeaderBody.createAMQFrame(_channelId,
- BasicConsumeBody.getClazz(_protocolHandler.getProtocolMajorVersion(),
- _protocolHandler.getProtocolMinorVersion()), 0, contentHeaderProperties, size);
+ classIfForBasic, 0, contentHeaderProperties, size);
if (_logger.isDebugEnabled())
{
_logger.debug("Sending content header frame to " + destination);
@@ -129,17 +128,19 @@ public class BasicMessageProducer_0_8 extends BasicMessageProducer
frames[0] = publishFrame;
frames[1] = contentHeaderFrame;
CompositeAMQDataBlock compositeFrame = new CompositeAMQDataBlock(frames);
- _protocolHandler.writeFrame(compositeFrame, wait);
- if (message != origMessage)
+ try
+ {
+ _session.checkFlowControl();
+ }
+ catch (InterruptedException e)
{
- _logger.debug("Updating original message");
- origMessage.setJMSPriority(message.getJMSPriority());
- origMessage.setJMSTimestamp(message.getJMSTimestamp());
- _logger.debug("Setting JMSExpiration:" + message.getJMSExpiration());
- origMessage.setJMSExpiration(message.getJMSExpiration());
- origMessage.setJMSMessageID(message.getJMSMessageID());
+ JMSException jmsEx = new JMSException("Interrupted while waiting for flow control to be removed");
+ jmsEx.setLinkedException(e);
+ throw jmsEx;
}
+
+ _protocolHandler.writeFrame(compositeFrame, wait);
}
/**
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 a5e89ef4fc..8cb285c1ac 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
@@ -29,9 +29,10 @@ import org.apache.qpid.framing.AMQShortString;
public enum CustomJMSXProperty
{
JMS_AMQP_NULL,
- JMS_QPID_DESTTYPE,
+ JMS_QPID_DESTTYPE,
JMSXGroupID,
- JMSXGroupSeq;
+ JMSXGroupSeq,
+ JMSXUserID;
private final AMQShortString _nameAsShortString;
@@ -47,7 +48,7 @@ public enum CustomJMSXProperty
}
private static Enumeration _names;
-
+
public static synchronized Enumeration asEnumeration()
{
if(_names == null)
diff --git a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java
index 493e2b5ec0..27783bcacf 100644
--- a/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java
+++ b/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java
@@ -22,11 +22,9 @@
package org.apache.qpid.client;
import javax.jms.Destination;
-import javax.jms.IllegalStateException;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Message;
-import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueSender;
@@ -200,19 +198,19 @@ public class QueueSenderAdapter implements QueueSender
}
AMQDestination destination = (AMQDestination) queue;
- if (!destination.isValidated() && checkQueueBeforePublish())
+ if (!destination.isCheckedForQueueBinding() && checkQueueBeforePublish())
{
if (_delegate.getSession().isStrictAMQP())
{
_delegate._logger.warn("AMQP does not support destination validation before publish, ");
- destination.setValidated(true);
+ destination.setCheckedForQueueBinding(true);
}
else
{
if (_delegate.isBound(destination))
{
- destination.setValidated(true);
+ destination.setCheckedForQueueBinding(true);
}
else
{
diff --git a/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java b/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java
index dd8ca8e197..29fa03fd1d 100644
--- a/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java
+++ b/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java
@@ -21,18 +21,18 @@
package org.apache.qpid.client;
import org.apache.qpid.AMQException;
-import org.apache.qpid.jms.TopicSubscriber;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Topic;
+import javax.jms.TopicSubscriber;
/**
* Wraps a MessageConsumer to fulfill the extended TopicSubscriber contract
*
*/
-class TopicSubscriberAdaptor implements org.apache.qpid.jms.TopicSubscriber
+class TopicSubscriberAdaptor implements TopicSubscriber
{
private final Topic _topic;
private final BasicMessageConsumer _consumer;
diff --git a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java
index fb2d72267b..76c899f565 100644
--- a/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java
@@ -123,13 +123,17 @@ public class FailoverHandler implements Runnable
// We wake up listeners. If they can handle failover, they will extend the
// FailoverRetrySupport class and will in turn block on the latch until failover
// has completed before retrying the operation.
- _amqProtocolHandler.propagateExceptionToWaiters(new FailoverException("Failing over about to start"));
+ _amqProtocolHandler.notifyFailoverStarting();
// Since failover impacts several structures we protect them all with a single mutex. These structures
// are also in child objects of the connection. This allows us to manipulate them without affecting
// client code which runs in a separate thread.
synchronized (_amqProtocolHandler.getConnection().getFailoverMutex())
{
+ //Clear the exception now that we have the failover mutex there can be no one else waiting for a frame so
+ // we can clear the exception.
+ _amqProtocolHandler.failoverInProgress();
+
// We switch in a new state manager temporarily so that the interaction to get to the "connection open"
// state works, without us having to terminate any existing "state waiters". We could theoretically
// have a state waiter waiting until the connection is closed for some reason. Or in future we may have
@@ -141,6 +145,9 @@ public class FailoverHandler implements Runnable
_logger.info("Failover process veto-ed by client");
_amqProtocolHandler.setStateManager(existingStateManager);
+
+ //todo: ritchiem these exceptions are useless... Would be better to attempt to propogate exception that
+ // prompted the failover event.
if (_host != null)
{
_amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException("Redirect was vetoed by client", null));
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 120a07f0fc..e756d7baf9 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
@@ -122,6 +122,13 @@ public class FailoverRetrySupport<T, E extends Exception> implements FailoverSup
{
_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
new file mode 100644
index 0000000000..a150d1446a
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java
@@ -0,0 +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<AccessRequestOkBody>
+{
+ 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/BasicCancelOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java
index 8f0ee05b3e..e3e08667d8 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java
@@ -25,12 +25,13 @@ import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.StateAwareMethodListener;
import org.apache.qpid.framing.BasicCancelOkBody;
+import org.apache.qpid.framing.AMQMethodBody;
import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BasicCancelOkMethodHandler implements StateAwareMethodListener
+public class BasicCancelOkMethodHandler implements StateAwareMethodListener<BasicCancelOkBody>
{
private static final Logger _logger = LoggerFactory.getLogger(BasicCancelOkMethodHandler.class);
@@ -44,16 +45,18 @@ public class BasicCancelOkMethodHandler implements StateAwareMethodListener
private BasicCancelOkMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ public void methodReceived(AMQStateManager stateManager, BasicCancelOkBody body, int channelId)
throws AMQException
{
- BasicCancelOkBody body = (BasicCancelOkBody) evt.getMethod();
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+
if (_logger.isInfoEnabled())
{
- _logger.info("New BasicCancelOk method received for consumer:" + body.consumerTag);
+ _logger.info("New BasicCancelOk method received for consumer:" + body.getConsumerTag());
}
- protocolSession.confirmConsumerCancelled(evt.getChannelId(), body.consumerTag);
+ session.confirmConsumerCancelled(channelId, body.getConsumerTag());
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
index 92ba6fd136..4deaa314ec 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
@@ -21,16 +21,16 @@
package org.apache.qpid.client.handler;
import org.apache.qpid.AMQException;
+import org.apache.qpid.client.message.UnprocessedMessage;
import org.apache.qpid.client.message.UnprocessedMessage_0_8;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.StateAwareMethodListener;
import org.apache.qpid.framing.BasicDeliverBody;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BasicDeliverMethodHandler implements StateAwareMethodListener
+public class BasicDeliverMethodHandler implements StateAwareMethodListener<BasicDeliverBody>
{
private static final Logger _logger = LoggerFactory.getLogger(BasicDeliverMethodHandler.class);
@@ -41,18 +41,18 @@ public class BasicDeliverMethodHandler implements StateAwareMethodListener
return _instance;
}
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ public void methodReceived(AMQStateManager stateManager, BasicDeliverBody body, int channelId)
throws AMQException
{
- BasicDeliverBody deliveryBody = (BasicDeliverBody) evt.getMethod();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
final UnprocessedMessage_0_8 msg = new UnprocessedMessage_0_8(
- evt.getChannelId(),
- deliveryBody.deliveryTag,
- deliveryBody.consumerTag.asString(),
- deliveryBody.getExchange(),
- deliveryBody.getRoutingKey(),
- deliveryBody.getRedelivered());
+ channelId,
+ body.getDeliveryTag(),
+ body.getConsumerTag(),
+ body.getExchange(),
+ body.getRoutingKey(),
+ body.getRedelivered());
_logger.debug("New JmsDeliver method received");
- protocolSession.unprocessedMessageReceived(msg);
+ session.unprocessedMessageReceived(msg);
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java
index bb5a56e5bd..682c3ac2c1 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java
@@ -22,15 +22,18 @@ package org.apache.qpid.client.handler;
import org.apache.qpid.AMQException;
import org.apache.qpid.client.message.ReturnMessage;
+import org.apache.qpid.client.message.UnprocessedMessage;
+import org.apache.qpid.client.message.UnprocessedMessage_0_8;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.StateAwareMethodListener;
import org.apache.qpid.framing.BasicReturnBody;
import org.apache.qpid.protocol.AMQMethodEvent;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BasicReturnMethodHandler implements StateAwareMethodListener
+public class BasicReturnMethodHandler implements StateAwareMethodListener<BasicReturnBody>
{
private static final Logger _logger = LoggerFactory.getLogger(BasicReturnMethodHandler.class);
@@ -41,18 +44,20 @@ public class BasicReturnMethodHandler implements StateAwareMethodListener
return _instance;
}
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
+
+ public void methodReceived(AMQStateManager stateManager, BasicReturnBody body, int channelId)
+ throws AMQException
{
- BasicReturnBody returnBody = (BasicReturnBody)evt.getMethod();
_logger.debug("New JmsBounce method received");
- final ReturnMessage msg = new ReturnMessage(evt.getChannelId(),
- returnBody.getExchange(),
- returnBody.getRoutingKey(),
- returnBody.getReplyText(),
- returnBody.getReplyCode()
- );
-
- protocolSession.unprocessedMessageReceived(msg);
+ final AMQProtocolSession session = stateManager.getProtocolSession();
+ final ReturnMessage msg = new ReturnMessage(channelId,
+ body.getExchange(),
+ body.getRoutingKey(),
+ body.getReplyText(),
+ body.getReplyCode()
+ );
+
+ session.unprocessedMessageReceived(msg);
}
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
index 9ed3ef7a60..ee4cf14d58 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
@@ -38,7 +38,7 @@ import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ChannelCloseMethodHandler implements StateAwareMethodListener
+public class ChannelCloseMethodHandler implements StateAwareMethodListener<ChannelCloseBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandler.class);
@@ -49,22 +49,26 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener
return _handler;
}
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ public void methodReceived(AMQStateManager stateManager, ChannelCloseBody method, int channelId)
throws AMQException
{
_logger.debug("ChannelClose method received");
- ChannelCloseBody method = (ChannelCloseBody) evt.getMethod();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
- AMQConstant errorCode = AMQConstant.getConstant(method.replyCode);
- AMQShortString reason = method.replyText;
+
+ AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode());
+ AMQShortString reason = method.getReplyText();
if (_logger.isDebugEnabled())
{
_logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason);
}
- // TODO: Be aware of possible changes to parameter order as versions change.
- AMQFrame frame = ChannelCloseOkBody.createAMQFrame(evt.getChannelId(), method.getMajor(), method.getMinor());
- protocolSession.writeFrame(frame);
+
+
+ ChannelCloseOkBody body = session.getMethodRegistry().createChannelCloseOkBody();
+ AMQFrame frame = body.generateFrame(channelId);
+ session.writeFrame(frame);
+
if (errorCode != AMQConstant.REPLY_SUCCESS)
{
if (_logger.isDebugEnabled())
@@ -100,6 +104,11 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener
}
// fixme why is this only done when the close is expected...
// should the above forced closes not also cause a close?
- protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason));
+ // ----------
+ // Closing the session only when it is expected allows the errors to be processed
+ // Calling this here will prevent failover. So we should do this for all exceptions
+ // that should never cause failover. Such as authentication errors.
+
+ session.channelClosed(channelId, errorCode, String.valueOf(reason));
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java
index e1fe2697e5..8d3277d4de 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java
@@ -21,6 +21,7 @@
package org.apache.qpid.client.handler;
import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ChannelCloseOkBody;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.StateAwareMethodListener;
@@ -29,7 +30,7 @@ import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ChannelCloseOkMethodHandler implements StateAwareMethodListener
+public class ChannelCloseOkMethodHandler implements StateAwareMethodListener<ChannelCloseOkBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseOkMethodHandler.class);
@@ -40,11 +41,12 @@ public class ChannelCloseOkMethodHandler implements StateAwareMethodListener
return _instance;
}
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ public void methodReceived(AMQStateManager stateManager, ChannelCloseOkBody method, int channelId)
throws AMQException
{
- _logger.info("Received channel-close-ok for channel-id " + evt.getChannelId());
+ _logger.info("Received channel-close-ok for channel-id " + channelId);
+ final AMQProtocolSession session = stateManager.getProtocolSession();
// todo this should do the local closure
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java
new file mode 100644
index 0000000000..b47fe751d6
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java
@@ -0,0 +1,54 @@
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.framing.ChannelFlowBody;
+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.AMQException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+
+public class ChannelFlowMethodHandler implements StateAwareMethodListener<ChannelFlowBody>
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowMethodHandler.class);
+ private static final ChannelFlowMethodHandler _instance = new ChannelFlowMethodHandler();
+
+ public static ChannelFlowMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelFlowMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, ChannelFlowBody body, int channelId)
+ throws AMQException
+ {
+
+ final AMQProtocolSession session = stateManager.getProtocolSession();
+ session.setFlowControl(channelId, body.getActive());
+ }
+
+
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java
index ca3f46d08b..96de8af54b 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java
@@ -30,7 +30,7 @@ import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ChannelFlowOkMethodHandler implements StateAwareMethodListener
+public class ChannelFlowOkMethodHandler implements StateAwareMethodListener<ChannelFlowOkBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowOkMethodHandler.class);
private static final ChannelFlowOkMethodHandler _instance = new ChannelFlowOkMethodHandler();
@@ -43,10 +43,12 @@ public class ChannelFlowOkMethodHandler implements StateAwareMethodListener
private ChannelFlowOkMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ChannelFlowOkBody body, int channelId)
+ throws AMQException
{
- ChannelFlowOkBody method = (ChannelFlowOkBody) evt.getMethod();
- _logger.debug("Received Channel.Flow-Ok message, active = " + method.active);
+
+ _logger.debug("Received Channel.Flow-Ok message, active = " + body.getActive());
}
+
+
}
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
new file mode 100644
index 0000000000..de976b05bd
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java
@@ -0,0 +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<ProtocolVersion, DispatcherFactory> _dispatcherFactories =
+ new HashMap<ProtocolVersion, DispatcherFactory>();
+
+ 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
new file mode 100644
index 0000000000..ae6d5e8283
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java
@@ -0,0 +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;
+ }
+
+
+}
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
new file mode 100644
index 0000000000..6bd6874cde
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java
@@ -0,0 +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;
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java
index 752f44237d..950a3288fc 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java
@@ -36,7 +36,7 @@ import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ConnectionCloseMethodHandler implements StateAwareMethodListener
+public class ConnectionCloseMethodHandler implements StateAwareMethodListener<ConnectionCloseBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ConnectionCloseMethodHandler.class);
@@ -50,32 +50,35 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener
private ConnectionCloseMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ConnectionCloseBody method, int channelId)
+ throws AMQException
{
_logger.info("ConnectionClose frame received");
- ConnectionCloseBody method = (ConnectionCloseBody) evt.getMethod();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
+
// does it matter
// stateManager.changeState(AMQState.CONNECTION_CLOSING);
- AMQConstant errorCode = AMQConstant.getConstant(method.replyCode);
- AMQShortString reason = method.replyText;
+ AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode());
+ AMQShortString reason = method.getReplyText();
try
{
+
+ ConnectionCloseOkBody closeOkBody = session.getMethodRegistry().createConnectionCloseOkBody();
// TODO: check whether channel id of zero is appropriate
// Be aware of possible changes to parameter order as versions change.
- protocolSession.writeFrame(ConnectionCloseOkBody.createAMQFrame((short) 0, method.getMajor(),
- method.getMinor()));
+ session.writeFrame(closeOkBody.generateFrame(0));
if (errorCode != AMQConstant.REPLY_SUCCESS)
{
- if (errorCode == AMQConstant.NOT_ALLOWED)
+ if (errorCode == AMQConstant.NOT_ALLOWED || (errorCode == AMQConstant.ACCESS_REFUSED))
{
- _logger.info("Authentication Error:" + Thread.currentThread().getName());
+ _logger.info("Error :" + errorCode +":"+ Thread.currentThread().getName());
- protocolSession.closeProtocolSession();
+ // todo ritchiem : Why do this here when it is going to be done in the finally block?
+ session.closeProtocolSession();
// todo this is a bit of a fudge (could be conssidered such as each new connection needs a new state manager or at least a fresh state.
stateManager.changeState(AMQState.CONNECTION_NOT_STARTED);
@@ -94,9 +97,13 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener
{
// this actually closes the connection in the case where it is not an error.
- protocolSession.closeProtocolSession();
+ session.closeProtocolSession();
+ // ritchiem: Doing this though will cause any waiting connection start to be released without being able to
+ // see what the cause was.
stateManager.changeState(AMQState.CONNECTION_CLOSED);
}
}
+
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java
index 2e0f273c32..fd7acac84f 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java
@@ -21,13 +21,14 @@
package org.apache.qpid.client.handler;
import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ConnectionOpenOkBody;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQState;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.StateAwareMethodListener;
import org.apache.qpid.protocol.AMQMethodEvent;
-public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener
+public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener<ConnectionOpenOkBody>
{
private static final ConnectionOpenOkMethodHandler _instance = new ConnectionOpenOkMethodHandler();
@@ -40,9 +41,11 @@ public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener
{
}
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ConnectionOpenOkBody body, int channelId)
+ throws AMQException
{
stateManager.changeState(AMQState.CONNECTION_OPEN);
}
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java
index 213c0eba6e..cac68c9467 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java
@@ -30,7 +30,7 @@ import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ConnectionRedirectMethodHandler implements StateAwareMethodListener
+public class ConnectionRedirectMethodHandler implements StateAwareMethodListener<ConnectionRedirectBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ConnectionRedirectMethodHandler.class);
@@ -46,13 +46,13 @@ public class ConnectionRedirectMethodHandler implements StateAwareMethodListener
private ConnectionRedirectMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ConnectionRedirectBody method, int channelId)
+ throws AMQException
{
_logger.info("ConnectionRedirect frame received");
- ConnectionRedirectBody method = (ConnectionRedirectBody) evt.getMethod();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
- String host = method.host.toString();
+ String host = method.getHost().toString();
// the host is in the form hostname:port with the port being optional
int portIndex = host.indexOf(':');
@@ -68,6 +68,7 @@ public class ConnectionRedirectMethodHandler implements StateAwareMethodListener
}
- protocolSession.failover(host, port);
+ session.failover(host, port);
}
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java
index b7776705fe..900aa2abac 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java
@@ -32,7 +32,7 @@ import org.apache.qpid.framing.ConnectionSecureBody;
import org.apache.qpid.framing.ConnectionSecureOkBody;
import org.apache.qpid.protocol.AMQMethodEvent;
-public class ConnectionSecureMethodHandler implements StateAwareMethodListener
+public class ConnectionSecureMethodHandler implements StateAwareMethodListener<ConnectionSecureBody>
{
private static final ConnectionSecureMethodHandler _instance = new ConnectionSecureMethodHandler();
@@ -41,27 +41,26 @@ public class ConnectionSecureMethodHandler implements StateAwareMethodListener
return _instance;
}
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ConnectionSecureBody body, int channelId)
+ throws AMQException
{
- SaslClient client = protocolSession.getSaslClient();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
+ SaslClient client = session.getSaslClient();
if (client == null)
{
throw new AMQException(null, "No SASL client set up - cannot proceed with authentication", null);
}
- ConnectionSecureBody body = (ConnectionSecureBody) evt.getMethod();
+
try
{
// Evaluate server challenge
- byte[] response = client.evaluateChallenge(body.challenge);
- // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
- // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
- // Be aware of possible changes to parameter order as versions change.
- AMQFrame responseFrame = ConnectionSecureOkBody.createAMQFrame(evt.getChannelId(),
- body.getMajor(), body.getMinor(),
- response); // response
- protocolSession.writeFrame(responseFrame);
+ byte[] response = client.evaluateChallenge(body.getChallenge());
+
+ ConnectionSecureOkBody secureOkBody = session.getMethodRegistry().createConnectionSecureOkBody(response);
+
+ session.writeFrame(secureOkBody.generateFrame(channelId));
}
catch (SaslException e)
{
@@ -70,4 +69,6 @@ public class ConnectionSecureMethodHandler implements StateAwareMethodListener
}
+
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java
index 59b493a6f7..d3746f137e 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java
@@ -48,7 +48,7 @@ import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.StringTokenizer;
-public class ConnectionStartMethodHandler implements StateAwareMethodListener
+public class ConnectionStartMethodHandler implements StateAwareMethodListener<ConnectionStartBody>
{
private static final Logger _log = LoggerFactory.getLogger(ConnectionStartMethodHandler.class);
@@ -62,15 +62,16 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener
private ConnectionStartMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ConnectionStartBody body, int channelId)
+ throws AMQException
{
_log.debug("public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, "
+ "AMQMethodEvent evt): called");
- ConnectionStartBody body = (ConnectionStartBody) evt.getMethod();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
+
- ProtocolVersion pv = new ProtocolVersion((byte) body.versionMajor, (byte) body.versionMinor);
+ ProtocolVersion pv = new ProtocolVersion((byte) body.getVersionMajor(), (byte) body.getVersionMinor());
// For the purposes of interop, we can make the client accept the broker's version string.
// If it does, it then internally records the version as being the latest one that it understands.
@@ -83,26 +84,26 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener
if (pv.isSupported())
{
- protocolSession.setProtocolVersion(pv.getMajorVersion(), pv.getMinorVersion());
+ session.setProtocolVersion(pv);
try
{
// Used to hold the SASL mechanism to authenticate with.
String mechanism;
- if (body.mechanisms == null)
+ if (body.getMechanisms()== null)
{
throw new AMQException(null, "mechanism not specified in ConnectionStart method frame", null);
}
else
{
- mechanism = chooseMechanism(body.mechanisms);
+ mechanism = chooseMechanism(body.getMechanisms());
_log.debug("mechanism = " + mechanism);
}
if (mechanism == null)
{
- throw new AMQException(null, "No supported security mechanism found, passed: " + new String(body.mechanisms), null);
+ throw new AMQException(null, "No supported security mechanism found, passed: " + new String(body.getMechanisms()), null);
}
byte[] saslResponse;
@@ -110,7 +111,7 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener
{
SaslClient sc =
Sasl.createSaslClient(new String[] { mechanism }, null, "AMQP", "localhost", null,
- createCallbackHandler(mechanism, protocolSession));
+ createCallbackHandler(mechanism, session));
if (sc == null)
{
throw new AMQException(null, "Client SASL configuration error: no SaslClient could be created for mechanism " + mechanism
@@ -118,21 +119,21 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener
+ " details of how to register non-standard SASL client providers.", null);
}
- protocolSession.setSaslClient(sc);
+ session.setSaslClient(sc);
saslResponse = (sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[0]) : null);
}
catch (SaslException e)
{
- protocolSession.setSaslClient(null);
+ session.setSaslClient(null);
throw new AMQException(null, "Unable to create SASL client: " + e, e);
}
- if (body.locales == null)
+ if (body.getLocales() == null)
{
throw new AMQException(null, "Locales is not defined in Connection Start method", null);
}
- final String locales = new String(body.locales, "utf8");
+ final String locales = new String(body.getLocales(), "utf8");
final StringTokenizer tokenizer = new StringTokenizer(locales, " ");
String selectedLocale = null;
if (tokenizer.hasMoreTokens())
@@ -148,23 +149,20 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener
FieldTable clientProperties = FieldTableFactory.newFieldTable();
clientProperties.setString(new AMQShortString(ClientProperties.instance.toString()),
- protocolSession.getClientID());
+ session.getClientID());
clientProperties.setString(new AMQShortString(ClientProperties.product.toString()),
QpidProperties.getProductName());
clientProperties.setString(new AMQShortString(ClientProperties.version.toString()),
QpidProperties.getReleaseVersion());
clientProperties.setString(new AMQShortString(ClientProperties.platform.toString()), getFullSystemInfo());
+
+ ConnectionStartOkBody connectionStartOkBody = session.getMethodRegistry().createConnectionStartOkBody(clientProperties,new AMQShortString(mechanism),saslResponse,new AMQShortString(locales));
// AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
// TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
// Be aware of possible changes to parameter order as versions change.
- protocolSession.writeFrame(ConnectionStartOkBody.createAMQFrame(evt.getChannelId(),
- protocolSession.getProtocolMajorVersion(), protocolSession.getProtocolMinorVersion(),
- clientProperties, // clientProperties
- new AMQShortString(selectedLocale), // locale
- new AMQShortString(mechanism), // mechanism
- saslResponse)); // response
-
+ session.writeFrame(connectionStartOkBody.generateFrame(channelId));
+
}
catch (UnsupportedEncodingException e)
{
@@ -173,10 +171,10 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener
}
else
{
- _log.error("Broker requested Protocol [" + body.versionMajor + "-" + body.versionMinor
+ _log.error("Broker requested Protocol [" + body.getVersionMajor() + "-" + body.getVersionMinor()
+ "] which is not supported by this version of the client library");
- protocolSession.closeProtocolSession();
+ session.closeProtocolSession();
}
}
@@ -235,4 +233,5 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener
throw new AMQException(null, "Unable to create callback handler: " + e, e);
}
}
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java
index 68556991d7..fc0e40b745 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java
@@ -26,17 +26,13 @@ import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQState;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.StateAwareMethodListener;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.ConnectionOpenBody;
-import org.apache.qpid.framing.ConnectionTuneBody;
-import org.apache.qpid.framing.ConnectionTuneOkBody;
+import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ConnectionTuneMethodHandler implements StateAwareMethodListener
+public class ConnectionTuneMethodHandler implements StateAwareMethodListener<ConnectionTuneBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ConnectionTuneMethodHandler.class);
@@ -50,48 +46,41 @@ public class ConnectionTuneMethodHandler implements StateAwareMethodListener
protected ConnectionTuneMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ConnectionTuneBody frame, int channelId)
+ throws AMQException
{
_logger.debug("ConnectionTune frame received");
- ConnectionTuneBody frame = (ConnectionTuneBody) evt.getMethod();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
+ final MethodRegistry methodRegistry = session.getMethodRegistry();
- ConnectionTuneParameters params = protocolSession.getConnectionTuneParameters();
+
+ ConnectionTuneParameters params = session.getConnectionTuneParameters();
if (params == null)
{
params = new ConnectionTuneParameters();
}
- params.setFrameMax(frame.frameMax);
- params.setChannelMax(frame.channelMax);
- params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.heartbeat));
- protocolSession.setConnectionTuneParameters(params);
+ params.setFrameMax(frame.getFrameMax());
+ params.setChannelMax(frame.getChannelMax());
+ params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.getHeartbeat()));
+ session.setConnectionTuneParameters(params);
stateManager.changeState(AMQState.CONNECTION_NOT_OPENED);
- protocolSession.writeFrame(createTuneOkFrame(evt.getChannelId(), params, frame.getMajor(), frame.getMinor()));
- String host = protocolSession.getAMQConnection().getVirtualHost();
+ ConnectionTuneOkBody tuneOkBody = methodRegistry.createConnectionTuneOkBody(params.getChannelMax(),
+ params.getFrameMax(),
+ params.getHeartbeat());
+ // Be aware of possible changes to parameter order as versions change.
+ session.writeFrame(tuneOkBody.generateFrame(channelId));
+
+ String host = session.getAMQConnection().getVirtualHost();
AMQShortString virtualHost = new AMQShortString("/" + host);
- protocolSession.writeFrame(createConnectionOpenFrame(evt.getChannelId(), virtualHost, null, true, frame.getMajor(),
- frame.getMinor()));
- }
+ ConnectionOpenBody openBody = methodRegistry.createConnectionOpenBody(virtualHost,null,true);
- protected AMQFrame createConnectionOpenFrame(int channel, AMQShortString path, AMQShortString capabilities,
- boolean insist, byte major, byte minor)
- {
// Be aware of possible changes to parameter order as versions change.
- return ConnectionOpenBody.createAMQFrame(channel, major, minor, // AMQP version (major, minor)
- capabilities, // capabilities
- insist, // insist
- path); // virtualHost
+ session.writeFrame(openBody.generateFrame(channelId));
}
- protected AMQFrame createTuneOkFrame(int channel, ConnectionTuneParameters params, byte major, byte minor)
- {
- // Be aware of possible changes to parameter order as versions change.
- return ConnectionTuneOkBody.createAMQFrame(channel, major, minor, params.getChannelMax(), // channelMax
- params.getFrameMax(), // frameMax
- params.getHeartbeat()); // heartbeat
- }
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java
index 862a9be8d4..8de40beb10 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
/**
* @author Apache Software Foundation
*/
-public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener
+public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener<ExchangeBoundOkBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ExchangeBoundOkMethodHandler.class);
private static final ExchangeBoundOkMethodHandler _instance = new ExchangeBoundOkMethodHandler();
@@ -46,14 +46,14 @@ public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener
private ExchangeBoundOkMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
+ public void methodReceived(AMQStateManager stateManager, ExchangeBoundOkBody body, int channelId)
+ throws AMQException
{
if (_logger.isDebugEnabled())
{
- ExchangeBoundOkBody body = (ExchangeBoundOkBody) evt.getMethod();
- _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.replyCode + " text: "
- + body.replyText);
+ _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.getReplyCode() + " text: "
+ + body.getReplyText());
}
}
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java
index 65060d44d2..41225c0569 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
/**
* @author Apache Software Foundation
*/
-public class QueueDeleteOkMethodHandler implements StateAwareMethodListener
+public class QueueDeleteOkMethodHandler implements StateAwareMethodListener<QueueDeleteOkBody>
{
private static final Logger _logger = LoggerFactory.getLogger(QueueDeleteOkMethodHandler.class);
private static final QueueDeleteOkMethodHandler _instance = new QueueDeleteOkMethodHandler();
@@ -46,13 +46,14 @@ public class QueueDeleteOkMethodHandler implements StateAwareMethodListener
private QueueDeleteOkMethodHandler()
{ }
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
- throws AMQException
- {
+ public void methodReceived(AMQStateManager stateManager, QueueDeleteOkBody body, int channelId)
+ throws AMQException
+ {
if (_logger.isDebugEnabled())
{
- QueueDeleteOkBody body = (QueueDeleteOkBody) evt.getMethod();
- _logger.debug("Received Queue.Delete-Ok message, message count: " + body.messageCount);
+ _logger.debug("Received Queue.Delete-Ok message, message count: " + body.getMessageCount());
}
}
+
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java
index cfbf687401..f57f0ff252 100644
--- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java
+++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java
@@ -63,8 +63,8 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
protected boolean _changedData = true;
private Destination _destination;
private JMSHeaderAdapter _headerAdapter;
- private BasicMessageConsumer _consumer;
- private boolean _strictAMQP;
+ private static final boolean STRICT_AMQP_COMPLIANCE =
+ Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT));
/**
* This is 0_10 specific
@@ -108,6 +108,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
return _010message;
}
+
protected AbstractJMSMessage(ByteBuffer data)
{
super(new BasicContentHeaderProperties());
@@ -122,8 +123,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
_changedData = (data == null);
_headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders());
- _strictAMQP =
- Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT));
}
protected AbstractJMSMessage(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange,
@@ -171,7 +170,10 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
{
if (getContentHeaderProperties().getMessageIdAsString() == null)
{
- getContentHeaderProperties().setMessageId("ID:" + UUID.randomUUID());
+ StringBuilder b = new StringBuilder(39);
+ b.append("ID:");
+ b.append(UUID.randomUUID());
+ getContentHeaderProperties().setMessageId(b.toString());
}
return getContentHeaderProperties().getMessageIdAsString();
@@ -351,7 +353,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public boolean getBooleanProperty(AMQShortString propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -361,7 +363,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public boolean getBooleanProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -371,7 +373,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public byte getByteProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -381,7 +383,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public byte[] getBytesProperty(AMQShortString propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -391,7 +393,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public short getShortProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -401,7 +403,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public int getIntProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -411,7 +413,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public long getLongProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -421,7 +423,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public float getFloatProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -431,7 +433,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public double getDoubleProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -441,12 +443,20 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public String getStringProperty(String propertyName) throws JMSException
{
- if (_strictAMQP)
+ //NOTE: if the JMSX Property is a non AMQP property then we must check _strictAMQP and throw as below.
+ if (propertyName.equals(CustomJMSXProperty.JMSXUserID.toString()))
{
- throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ return ((BasicContentHeaderProperties) _contentHeaderProperties).getUserIdAsString();
}
+ else
+ {
+ if (STRICT_AMQP_COMPLIANCE)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
- return getJmsHeaders().getString(propertyName);
+ return getJmsHeaders().getString(propertyName);
+ }
}
public Object getObjectProperty(String propertyName) throws JMSException
@@ -461,7 +471,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setBooleanProperty(AMQShortString propertyName, boolean b) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -472,7 +482,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setBooleanProperty(String propertyName, boolean b) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -483,7 +493,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setByteProperty(String propertyName, byte b) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -494,7 +504,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setBytesProperty(AMQShortString propertyName, byte[] bytes) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -505,7 +515,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setShortProperty(String propertyName, short i) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -523,7 +533,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setLongProperty(String propertyName, long l) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -534,7 +544,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setFloatProperty(String propertyName, float f) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -545,7 +555,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public void setDoubleProperty(String propertyName, double v) throws JMSException
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -741,11 +751,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
}
}
- public void setConsumer(BasicMessageConsumer basicMessageConsumer)
- {
- _consumer = basicMessageConsumer;
- }
-
public void receivedFromServer()
{
_changedData = false;
diff --git a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java
index 48992659f6..0dbad3726c 100644
--- a/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java
+++ b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java
@@ -79,8 +79,17 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory
while (it.hasNext())
{
ContentBody cb = (ContentBody) it.next();
- data.put(cb.payload);
- cb.payload.release();
+ final ByteBuffer payload = cb.payload;
+ if(payload.isDirect() || payload.isReadOnly())
+ {
+ data.put(payload);
+ }
+ else
+ {
+ data.put(payload.array(), payload.arrayOffset(), payload.limit());
+ }
+
+ payload.release();
}
data.flip();
diff --git a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java
index 1fb5e637c9..fed1f1c609 100644
--- a/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java
+++ b/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java
@@ -56,7 +56,11 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm
JMSMapMessage(ByteBuffer data) throws JMSException
{
super(data); // this instantiates a content header
- populateMapFromData();
+ if(data != null)
+ {
+ populateMapFromData();
+ }
+
}
JMSMapMessage(long messageNbr, BasicContentHeaderProperties contentHeader, AMQShortString exchange, AMQShortString routingKey,
@@ -77,7 +81,7 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm
public String toBodyString() throws JMSException
{
- return _map.toString();
+ return _map == null ? "" : _map.toString();
}
public AMQShortString getMimeTypeAsShortString()
diff --git a/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java
index 593c7795b0..c866a5028e 100644
--- a/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java
+++ b/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java
@@ -9,7 +9,7 @@ public class ReturnMessage extends UnprocessedMessage_0_8
public ReturnMessage(int channelId,AMQShortString exchange,AMQShortString routingKey,AMQShortString replyText,int replyCode)
{
- super(channelId,-1,"",exchange,routingKey,false);
+ super(channelId,-1,null,exchange,routingKey,false);
_replyText = replyText;
_replyCode = replyCode;
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java
index 5cb943cd33..17efd7e24f 100644
--- a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java
+++ b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java
@@ -23,6 +23,20 @@ package org.apache.qpid.client.message;
import java.util.List;
import org.apache.qpid.framing.AMQShortString;
+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.client.BasicMessageConsumer;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.framing.BasicReturnBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.MethodDispatcher;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
/**
@@ -36,12 +50,12 @@ public abstract class UnprocessedMessage<H,B>
{
private final int _channelId;
private final long _deliveryId;
- private final String _consumerTag;
+ private final AMQShortString _consumerTag;
protected AMQShortString _exchange;
protected AMQShortString _routingKey;
protected boolean _redelivered;
- public UnprocessedMessage(int channelId,long deliveryId,String consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered)
+ public UnprocessedMessage(int channelId,long deliveryId,AMQShortString consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered)
{
_channelId = channelId;
_deliveryId = deliveryId;
@@ -65,7 +79,7 @@ public abstract class UnprocessedMessage<H,B>
return _deliveryId;
}
- public String getConsumerTag()
+ public AMQShortString getConsumerTag()
{
return _consumerTag;
}
@@ -84,14 +98,13 @@ public abstract class UnprocessedMessage<H,B>
{
return _redelivered;
}
-
public abstract List<B> getBodies();
-
+
public abstract H getContentHeader();
-
+
// specific to 0_10
public String getReplyToURL()
{
return "";
- }
+ }
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java
index 79d829ca39..09f41a9ba6 100644
--- a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java
+++ b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java
@@ -43,7 +43,7 @@ public class UnprocessedMessage_0_10 extends UnprocessedMessage<Struct[],ByteBuf
/** List of ContentBody instances. Due to fragmentation you don't know how big this will be in general */
private List<ByteBuffer> _bodies = new ArrayList<ByteBuffer>();
- public UnprocessedMessage_0_10(int channelId,long deliveryId,String consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered)
+ public UnprocessedMessage_0_10(int channelId,long deliveryId,AMQShortString consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered)
{
super(channelId,deliveryId,consumerTag,exchange,routingKey,redelivered);
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java
index 8e32de382b..78da8cdca2 100644
--- a/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java
+++ b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java
@@ -26,6 +26,7 @@ import java.util.List;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.framing.BasicReturnBody;
import org.apache.qpid.framing.ContentBody;
import org.apache.qpid.framing.ContentHeaderBody;
@@ -46,11 +47,22 @@ public class UnprocessedMessage_0_8 extends UnprocessedMessage<ContentHeaderBody
/** List of ContentBody instances. Due to fragmentation you don't know how big this will be in general */
private List<ContentBody> _bodies;
- public UnprocessedMessage_0_8(int channelId,long deliveryId,String consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered)
+ public UnprocessedMessage_0_8(int channelId,long deliveryId,AMQShortString consumerTag,AMQShortString exchange,AMQShortString routingKey,boolean redelivered)
{
super(channelId,deliveryId,consumerTag,exchange,routingKey,redelivered);
}
+ public UnprocessedMessage_0_8(int channelId, BasicReturnBody body)
+ {
+ //FIXME: TGM, SRSLY 4RL
+ super(channelId, 0, null, body.getExchange(), body.getRoutingKey(), false);
+ }
+
+ public UnprocessedMessage_0_8(int channelId, BasicDeliverBody body)
+ {
+ super(channelId, body.getDeliveryTag(), body.getConsumerTag(), body.getExchange(), body.getRoutingKey(), false);
+ }
+
public void receiveBody(ContentBody body)
{
@@ -119,8 +131,8 @@ public class UnprocessedMessage_0_8 extends UnprocessedMessage<ContentHeaderBody
}
if(_deliverBody != null)
{
- buf.append("Delivery tag " + _deliverBody.deliveryTag);
- buf.append("Consumer tag " + _deliverBody.consumerTag);
+ buf.append("Delivery tag " + _deliverBody.getDeliveryTag());
+ buf.append("Consumer tag " + _deliverBody.getConsumerTag());
buf.append("Deliver Body " + _deliverBody);
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
index 2c435aba6c..97b3660240 100644
--- a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
@@ -21,10 +21,15 @@
package org.apache.qpid.client.protocol;
import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoFilterChain;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.ReadThrottleFilterBuilder;
import org.apache.mina.filter.SSLFilter;
+import org.apache.mina.filter.WriteBufferLimitFilterBuilder;
+import org.apache.mina.filter.codec.ProtocolCodecException;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.qpid.AMQConnectionClosedException;
import org.apache.qpid.AMQDisconnectedException;
import org.apache.qpid.AMQException;
@@ -39,16 +44,7 @@ import org.apache.qpid.client.state.AMQState;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.state.listener.SpecificMethodFrameListener;
import org.apache.qpid.codec.AMQCodecFactory;
-import org.apache.qpid.framing.AMQBody;
-import org.apache.qpid.framing.AMQDataBlock;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.ConnectionCloseBody;
-import org.apache.qpid.framing.ConnectionCloseOkBody;
-import org.apache.qpid.framing.ContentBody;
-import org.apache.qpid.framing.ContentHeaderBody;
-import org.apache.qpid.framing.HeartbeatBody;
+import org.apache.qpid.framing.*;
import org.apache.qpid.pool.ReadWriteThreadModel;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.protocol.AMQMethodEvent;
@@ -57,7 +53,9 @@ import org.apache.qpid.ssl.SSLContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
import java.util.Iterator;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
@@ -155,9 +153,19 @@ public class AMQProtocolHandler extends IoHandlerAdapter
/** Used to provide a condition to wait upon for operations that are required to wait for failover to complete. */
private CountDownLatch _failoverLatch;
+
+ /** The last failover exception that occured */
+ private FailoverException _lastFailoverException;
+
/** Defines the default timeout to use for synchronous protocol commands. */
private final long DEFAULT_SYNC_TIMEOUT = 1000 * 30;
+ /** Default buffer size for pending messages reads */
+ private static final String DEFAULT_READ_BUFFER_LIMIT = "262144";
+
+ /** Default buffer size for pending messages writes */
+ private static final String DEFAULT_WRITE_BUFFER_LIMIT = "262144";
+
/**
* Creates a new protocol handler, associated with the specified client connection instance.
*
@@ -211,9 +219,34 @@ public class AMQProtocolHandler extends IoHandlerAdapter
}
catch (RuntimeException e)
{
- e.printStackTrace();
+ _logger.error(e.getMessage(), e);
}
+ if (Boolean.getBoolean("protectio"))
+ {
+ try
+ {
+ //Add IO Protection Filters
+ IoFilterChain chain = session.getFilterChain();
+
+ session.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter());
+
+ ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER_LIMIT)));
+ readfilter.attach(chain);
+
+ WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER_LIMIT)));
+ writefilter.attach(chain);
+ session.getFilterChain().remove("tempExecutorFilterForFilterBuilder");
+
+ _logger.info("Using IO Read/Write Filter Protection");
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to attach IO Read/Write Filter Protection :" + e.getMessage());
+ }
+ }
_protocolSession = new AMQProtocolSession(this, session, _connection, getStateManager());
_protocolSession.init();
}
@@ -327,13 +360,27 @@ public class AMQProtocolHandler extends IoHandlerAdapter
if (_failoverState == FailoverState.NOT_STARTED)
{
// if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException)))
- if (cause instanceof AMQConnectionClosedException)
+ if ((cause instanceof AMQConnectionClosedException) || cause instanceof IOException)
{
_logger.info("Exception caught therefore going to attempt failover: " + cause, cause);
// this will attemp failover
sessionClosed(session);
}
+ else
+ {
+
+ if (cause instanceof ProtocolCodecException)
+ {
+ _logger.info("Protocol Exception caught NOT going to attempt failover as " +
+ "cause isn't AMQConnectionClosedException: " + cause, cause);
+
+ AMQException amqe = new AMQException("Protocol handler error: " + cause, cause);
+ propagateExceptionToWaiters(amqe);
+ }
+ _connection.exceptionReceived(cause);
+
+ }
// FIXME Need to correctly handle other exceptions. Things like ...
// if (cause instanceof AMQChannelClosedException)
@@ -349,7 +396,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter
// we notify the state manager of the error in case we have any clients waiting on a state
// change. Those "waiters" will be interrupted and can handle the exception
- AMQException amqe = new AMQException(null, "Protocol handler error: " + cause, cause);
+ AMQException amqe = new AMQException("Protocol handler error: " + cause, cause);
propagateExceptionToWaiters(amqe);
_connection.exceptionReceived(cause);
}
@@ -364,7 +411,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter
*/
public void propagateExceptionToWaiters(Exception e)
{
- getStateManager().error(e);
+
if (!_frameListeners.isEmpty())
{
final Iterator it = _frameListeners.iterator();
@@ -376,98 +423,111 @@ public class AMQProtocolHandler extends IoHandlerAdapter
}
}
- private static int _messageReceivedCount;
-
- public void messageReceived(IoSession session, Object message) throws Exception
+ public void notifyFailoverStarting()
{
- final boolean debug = _logger.isDebugEnabled();
- final long msgNumber = ++_messageReceivedCount;
-
- if (debug && ((msgNumber % 1000) == 0))
+ // Set the last exception in the sync block to ensure the ordering with add.
+ // either this gets done and the add does the ml.error
+ // or the add completes first and the iterator below will do ml.error
+ synchronized (_frameListeners)
{
- _logger.debug("Received " + _messageReceivedCount + " protocol messages");
+ _lastFailoverException = new FailoverException("Failing over about to start");
}
- AMQFrame frame = (AMQFrame) message;
+ propagateExceptionToWaiters(_lastFailoverException);
+ }
- final AMQBody bodyFrame = frame.getBodyFrame();
+ public void failoverInProgress()
+ {
+ _lastFailoverException = null;
+ }
- HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody);
+ private static int _messageReceivedCount;
- switch (bodyFrame.getFrameType())
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ if(message instanceof AMQFrame)
{
- case AMQMethodBody.TYPE:
+ final boolean debug = _logger.isDebugEnabled();
+ final long msgNumber = ++_messageReceivedCount;
- if (debug)
- {
- _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + frame);
- }
+ if (debug && ((msgNumber % 1000) == 0))
+ {
+ _logger.debug("Received " + _messageReceivedCount + " protocol messages");
+ }
- final AMQMethodEvent<AMQMethodBody> evt =
- new AMQMethodEvent<AMQMethodBody>(frame.getChannel(), (AMQMethodBody) bodyFrame);
+ AMQFrame frame = (AMQFrame) message;
- try
- {
+ final AMQBody bodyFrame = frame.getBodyFrame();
- boolean wasAnyoneInterested = getStateManager().methodReceived(evt);
- if (!_frameListeners.isEmpty())
- {
- Iterator it = _frameListeners.iterator();
- while (it.hasNext())
- {
- final AMQMethodListener listener = (AMQMethodListener) it.next();
- wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested;
- }
- }
-
- if (!wasAnyoneInterested)
- {
- throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:"
- + _frameListeners, null);
- }
- }
- catch (AMQException e)
- {
- getStateManager().error(e);
- if (!_frameListeners.isEmpty())
- {
- Iterator it = _frameListeners.iterator();
- while (it.hasNext())
- {
- final AMQMethodListener listener = (AMQMethodListener) it.next();
- listener.error(e);
- }
- }
-
- exceptionCaught(session, e);
- }
+ HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody);
- break;
+ bodyFrame.handle(frame.getChannel(),_protocolSession);
- case ContentHeaderBody.TYPE:
+ _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes());
+ }
+ else if (message instanceof ProtocolInitiation)
+ {
+ // We get here if the server sends a response to our initial protocol header
+ // suggesting an alternate ProtocolVersion; the server will then close the
+ // connection.
+ ProtocolInitiation protocolInit = (ProtocolInitiation) message;
+ ProtocolVersion pv = protocolInit.checkVersion();
+ getConnection().setProtocolVersion(pv);
+
+ // get round a bug in old versions of qpid whereby the connection is not closed
+ _stateManager.changeState(AMQState.CONNECTION_CLOSED);
+ }
+ }
- _protocolSession.messageContentHeaderReceived(frame.getChannel(), (ContentHeaderBody) bodyFrame);
- break;
+ public void methodBodyReceived(final int channelId, final AMQBody bodyFrame, IoSession session)//, final IoSession session)
+ throws AMQException
+ {
- case ContentBody.TYPE:
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + bodyFrame);
+ }
- _protocolSession.messageContentBodyReceived(frame.getChannel(), (ContentBody) bodyFrame);
- break;
+ final AMQMethodEvent<AMQMethodBody> evt =
+ new AMQMethodEvent<AMQMethodBody>(channelId, (AMQMethodBody) bodyFrame);
- case HeartbeatBody.TYPE:
+ try
+ {
- if (debug)
+ boolean wasAnyoneInterested = getStateManager().methodReceived(evt);
+ if (!_frameListeners.isEmpty())
+ {
+ //This iterator is safe from the error state as the frame listeners always add before they send so their
+ // will be ready and waiting for this response.
+ Iterator it = _frameListeners.iterator();
+ while (it.hasNext())
{
- _logger.debug("Received heartbeat");
+ final AMQMethodListener listener = (AMQMethodListener) it.next();
+ wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested;
}
+ }
- break;
-
- default:
+ if (!wasAnyoneInterested)
+ {
+ throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:"
+ + _frameListeners, null);
+ }
+ }
+ catch (AMQException e)
+ {
+ if (!_frameListeners.isEmpty())
+ {
+ Iterator it = _frameListeners.iterator();
+ while (it.hasNext())
+ {
+ final AMQMethodListener listener = (AMQMethodListener) it.next();
+ listener.error(e);
+ }
+ }
+ exceptionCaught(session, e);
}
- _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes());
}
private static int _messagesOut;
@@ -506,6 +566,12 @@ public class AMQProtocolHandler extends IoHandlerAdapter
getStateManager().attainState(s);
}
+ public AMQState attainState(Set<AMQState> states) throws AMQException
+ {
+ return getStateManager().attainState(states);
+ }
+
+
/**
* Convenience method that writes a frame to the protocol session. Equivalent to calling
* getProtocolSession().write().
@@ -547,7 +613,15 @@ public class AMQProtocolHandler extends IoHandlerAdapter
{
try
{
- _frameListeners.add(listener);
+ synchronized (_frameListeners)
+ {
+ if (_lastFailoverException != null)
+ {
+ throw _lastFailoverException;
+ }
+
+ _frameListeners.add(listener);
+ }
_protocolSession.writeFrame(frame);
AMQMethodEvent e = listener.blockForFrame(timeout);
@@ -556,10 +630,6 @@ public class AMQProtocolHandler extends IoHandlerAdapter
// When control resumes before this line, a reply will have been received
// that matches the criteria defined in the blocking listener
}
- catch (AMQException e)
- {
- throw e;
- }
finally
{
// If we don't removeKey the listener then no-one will
@@ -600,16 +670,11 @@ public class AMQProtocolHandler extends IoHandlerAdapter
{
getStateManager().changeState(AMQState.CONNECTION_CLOSING);
- // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
- // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
- // Be aware of possible changes to parameter order as versions change.
- final AMQFrame frame =
- ConnectionCloseBody.createAMQFrame(0, _protocolSession.getProtocolMajorVersion(),
- _protocolSession.getProtocolMinorVersion(), // AMQP version (major, minor)
- 0, // classId
- 0, // methodId
- AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
- new AMQShortString("JMS client is closing the connection.")); // replyText
+ ConnectionCloseBody body = _protocolSession.getMethodRegistry().createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
+ new AMQShortString("JMS client is closing the connection."),0,0);
+
+
+ final AMQFrame frame = body.generateFrame(0);
try
{
@@ -682,7 +747,10 @@ public class AMQProtocolHandler extends IoHandlerAdapter
public void setStateManager(AMQStateManager stateManager)
{
_stateManager = stateManager;
- _protocolSession.setStateManager(stateManager);
+ if (_protocolSession != null)
+ {
+ _protocolSession.setStateManager(stateManager);
+ }
}
public AMQProtocolSession getProtocolSession()
@@ -709,4 +777,14 @@ public class AMQProtocolHandler extends IoHandlerAdapter
{
return _protocolSession.getProtocolMinorVersion();
}
+
+ public MethodRegistry getMethodRegistry()
+ {
+ return getStateManager().getMethodRegistry();
+ }
+
+ public ProtocolVersion getProtocolVersion()
+ {
+ return _protocolSession.getProtocolVersion();
+ }
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
index f5008496d9..6e782e0bfc 100644
--- a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
+++ b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
@@ -21,40 +21,31 @@
package org.apache.qpid.client.protocol;
import org.apache.commons.lang.StringUtils;
-
import org.apache.mina.common.CloseFuture;
import org.apache.mina.common.IdleStatus;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.WriteFuture;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.security.sasl.SaslClient;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import org.apache.qpid.AMQException;
import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.ConnectionTuneParameters;
-// import org.apache.qpid.client.message.UnexpectedBodyReceivedException;
import org.apache.qpid.client.message.ReturnMessage;
import org.apache.qpid.client.message.UnprocessedMessage;
import org.apache.qpid.client.message.UnprocessedMessage_0_8;
import org.apache.qpid.client.state.AMQStateManager;
-import org.apache.qpid.framing.AMQDataBlock;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.ContentBody;
-import org.apache.qpid.framing.ContentHeaderBody;
-import org.apache.qpid.framing.MainRegistry;
-import org.apache.qpid.framing.ProtocolInitiation;
-import org.apache.qpid.framing.ProtocolVersion;
-import org.apache.qpid.framing.VersionSpecificRegistry;
+import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jms.JMSException;
-import javax.security.sasl.SaslClient;
-
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import org.apache.qpid.client.handler.ClientMethodDispatcherImpl;
/**
* Wrapper for protocol session that provides type-safe access to session attributes. <p/> The underlying protocol
@@ -95,18 +86,27 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
* Maps from a channel id to an unprocessed message. This is used to tie together the JmsDeliverBody (which arrives
* first) with the subsequent content header and content bodies.
*/
- protected ConcurrentMap<Integer,UnprocessedMessage_0_8> _channelId2UnprocessedMsgMap = new ConcurrentHashMap<Integer,UnprocessedMessage_0_8>();
+ private final ConcurrentMap<Integer,UnprocessedMessage> _channelId2UnprocessedMsgMap = new ConcurrentHashMap<Integer,UnprocessedMessage>();
+ private final UnprocessedMessage[] _channelId2UnprocessedMsgArray = new UnprocessedMessage[16];
/** Counter to ensure unique queue names */
protected int _queueId = 1;
protected final Object _queueIdLock = new Object();
- private byte _protocolMinorVersion;
- private byte _protocolMajorVersion;
- private VersionSpecificRegistry _registry =
- MainRegistry.getVersionSpecificRegistry(ProtocolVersion.getLatestSupportedVersion());
+ private ProtocolVersion _protocolVersion;
+// private VersionSpecificRegistry _registry =
+// MainRegistry.getVersionSpecificRegistry(ProtocolVersion.getLatestSupportedVersion());
+
+
+ private MethodRegistry _methodRegistry =
+ MethodRegistry.getMethodRegistry(ProtocolVersion.getLatestSupportedVersion());
+
+
+ private MethodDispatcher _methodDispatcher;
+
private final AMQConnection _connection;
+ private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0;
public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection)
{
@@ -126,6 +126,9 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
_minaProtocolSession.setWriteTimeout(LAST_WRITE_FUTURE_JOIN_TIMEOUT);
_stateManager = stateManager;
_stateManager.setProtocolSession(this);
+ _protocolVersion = connection.getProtocolVersion();
+ _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(),
+ stateManager);
_connection = connection;
}
@@ -135,7 +138,7 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
// start the process of setting up the connection. This is the first place that
// data is written to the server.
- _minaProtocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()));
+ _minaProtocolSession.write(new ProtocolInitiation(_connection.getProtocolVersion()));
}
public String getClientID()
@@ -164,6 +167,8 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
public void setStateManager(AMQStateManager stateManager)
{
_stateManager = stateManager;
+ _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(_protocolVersion,
+ stateManager);
}
public String getVirtualHost()
@@ -230,14 +235,25 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
*
* @throws AMQException if this was not expected
*/
- public void unprocessedMessageReceived(UnprocessedMessage_0_8 message) throws AMQException
+ public void unprocessedMessageReceived(UnprocessedMessage message) throws AMQException
{
- _channelId2UnprocessedMsgMap.put(message.getChannelId(), message);
+ final int channelId = message.getChannelId();
+ if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0)
+ {
+ _channelId2UnprocessedMsgArray[channelId] = message;
+ }
+ else
+ {
+ _channelId2UnprocessedMsgMap.put(channelId, message);
+ }
}
- public void messageContentHeaderReceived(int channelId, ContentHeaderBody contentHeader) throws AMQException
+ public void contentHeaderReceived(int channelId, ContentHeaderBody contentHeader) throws AMQException
{
- UnprocessedMessage_0_8 msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgMap.get(channelId);
+ final UnprocessedMessage msg = (channelId & FAST_CHANNEL_ACCESS_MASK) == 0 ? _channelId2UnprocessedMsgArray[channelId]
+ : _channelId2UnprocessedMsgMap.get(channelId);
+
+
if (msg == null)
{
throw new AMQException(null, "Error: received content header without having received a BasicDeliver frame first", null);
@@ -255,9 +271,19 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
}
}
- public void messageContentBodyReceived(int channelId, ContentBody contentBody) throws AMQException
+ public void contentBodyReceived(final int channelId, ContentBody contentBody) throws AMQException
{
- UnprocessedMessage_0_8 msg = _channelId2UnprocessedMsgMap.get(channelId);
+ UnprocessedMessage_0_8 msg;
+ final boolean fastAccess = (channelId & FAST_CHANNEL_ACCESS_MASK) == 0;
+ if(fastAccess)
+ {
+ msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgArray[channelId];
+ }
+ else
+ {
+ msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgMap.get(channelId);
+ }
+
if (msg == null)
{
throw new AMQException(null, "Error: received content body without having received a JMSDeliver frame first", null);
@@ -265,7 +291,14 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
if (msg.getContentHeader() == null)
{
- _channelId2UnprocessedMsgMap.remove(channelId);
+ if(fastAccess)
+ {
+ _channelId2UnprocessedMsgArray[channelId] = null;
+ }
+ else
+ {
+ _channelId2UnprocessedMsgMap.remove(channelId);
+ }
throw new AMQException(null, "Error: received content body without having received a ContentHeader frame first", null);
}
@@ -285,6 +318,11 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
}
}
+ public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException
+ {
+
+ }
+
/**
* Deliver a message to the appropriate session, removing the unprocessed message from our map
*
@@ -295,7 +333,14 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
{
AMQSession session = getSession(channelId);
session.messageReceived(msg);
- _channelId2UnprocessedMsgMap.remove(channelId);
+ if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0)
+ {
+ _channelId2UnprocessedMsgArray[channelId] = null;
+ }
+ else
+ {
+ _channelId2UnprocessedMsgMap.remove(channelId);
+ }
}
protected AMQSession getSession(int channelId)
@@ -440,26 +485,64 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
session.confirmConsumerCancelled(consumerTag);
}
- public void setProtocolVersion(final byte versionMajor, final byte versionMinor)
+ public void setProtocolVersion(final ProtocolVersion pv)
{
- _protocolMajorVersion = versionMajor;
- _protocolMinorVersion = versionMinor;
- _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor);
+ _protocolVersion = pv;
+ _methodRegistry = MethodRegistry.getMethodRegistry(pv);
+ _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(pv, _stateManager);
+
+ // _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor);
}
public byte getProtocolMinorVersion()
{
- return _protocolMinorVersion;
+ return _protocolVersion.getMinorVersion();
}
public byte getProtocolMajorVersion()
{
- return _protocolMajorVersion;
+ return _protocolVersion.getMajorVersion();
}
- public VersionSpecificRegistry getRegistry()
+ public ProtocolVersion getProtocolVersion()
{
- return _registry;
+ return _protocolVersion;
}
+// public VersionSpecificRegistry getRegistry()
+// {
+// return _registry;
+// }
+
+ public MethodRegistry getMethodRegistry()
+ {
+ return _methodRegistry;
+ }
+
+ public MethodDispatcher getMethodDispatcher()
+ {
+ return _methodDispatcher;
+ }
+
+
+ public void setTicket(int ticket, int channelId)
+ {
+ final AMQSession session = getSession(channelId);
+ session.setTicket(ticket);
+ }
+ public void setMethodDispatcher(MethodDispatcher methodDispatcher)
+ {
+ _methodDispatcher = methodDispatcher;
+ }
+
+ public void setFlowControl(final int channelId, final boolean active)
+ {
+ final AMQSession session = getSession(channelId);
+ session.setFlowControl(active);
+ }
+
+ public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException
+ {
+ _protocolHandler.methodBodyReceived(channel, amqMethodBody, _minaProtocolSession);
+ }
}
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
new file mode 100644
index 0000000000..5185278eef
--- /dev/null
+++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java
@@ -0,0 +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);
+ }
+}
diff --git a/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java
index 4996f59345..d32d10542f 100644
--- a/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java
+++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java
@@ -24,8 +24,22 @@ package org.apache.qpid.client.state;
* States used in the AMQ protocol. Used by the finite state machine to determine
* valid responses.
*/
-public class AMQState
+public enum AMQState
{
+
+ CONNECTION_NOT_STARTED(1, "CONNECTION_NOT_STARTED"),
+
+ CONNECTION_NOT_TUNED(2, "CONNECTION_NOT_TUNED"),
+
+ CONNECTION_NOT_OPENED(3, "CONNECTION_NOT_OPENED"),
+
+ CONNECTION_OPEN(4, "CONNECTION_OPEN"),
+
+ CONNECTION_CLOSING(5, "CONNECTION_CLOSING"),
+
+ CONNECTION_CLOSED(6, "CONNECTION_CLOSED");
+
+
private final int _id;
private final String _name;
@@ -41,16 +55,6 @@ public class AMQState
return "AMQState: id = " + _id + " name: " + _name;
}
- public static final AMQState CONNECTION_NOT_STARTED = new AMQState(1, "CONNECTION_NOT_STARTED");
-
- public static final AMQState CONNECTION_NOT_TUNED = new AMQState(2, "CONNECTION_NOT_TUNED");
-
- public static final AMQState CONNECTION_NOT_OPENED = new AMQState(3, "CONNECTION_NOT_OPENED");
- public static final AMQState CONNECTION_OPEN = new AMQState(4, "CONNECTION_OPEN");
- public static final AMQState CONNECTION_CLOSING = new AMQState(5, "CONNECTION_CLOSING");
-
- public static final AMQState CONNECTION_CLOSED = new AMQState(6, "CONNECTION_CLOSED");
-
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
index 9f430d76a7..eda1a1f5fd 100644
--- a/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
+++ b/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
@@ -21,49 +21,22 @@
package org.apache.qpid.client.state;
import org.apache.qpid.AMQException;
-import org.apache.qpid.client.handler.BasicCancelOkMethodHandler;
-import org.apache.qpid.client.handler.BasicDeliverMethodHandler;
-import org.apache.qpid.client.handler.BasicReturnMethodHandler;
-import org.apache.qpid.client.handler.ChannelCloseMethodHandler;
-import org.apache.qpid.client.handler.ChannelCloseOkMethodHandler;
-import org.apache.qpid.client.handler.ChannelFlowOkMethodHandler;
-import org.apache.qpid.client.handler.ConnectionCloseMethodHandler;
-import org.apache.qpid.client.handler.ConnectionOpenOkMethodHandler;
-import org.apache.qpid.client.handler.ConnectionSecureMethodHandler;
-import org.apache.qpid.client.handler.ConnectionStartMethodHandler;
-import org.apache.qpid.client.handler.ConnectionTuneMethodHandler;
-import org.apache.qpid.client.handler.ExchangeBoundOkMethodHandler;
-import org.apache.qpid.client.handler.QueueDeleteOkMethodHandler;
import org.apache.qpid.client.protocol.AMQProtocolSession;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.framing.BasicCancelOkBody;
-import org.apache.qpid.framing.BasicDeliverBody;
-import org.apache.qpid.framing.BasicReturnBody;
-import org.apache.qpid.framing.ChannelCloseBody;
-import org.apache.qpid.framing.ChannelCloseOkBody;
-import org.apache.qpid.framing.ChannelFlowOkBody;
-import org.apache.qpid.framing.ConnectionCloseBody;
-import org.apache.qpid.framing.ConnectionOpenOkBody;
-import org.apache.qpid.framing.ConnectionSecureBody;
-import org.apache.qpid.framing.ConnectionStartBody;
-import org.apache.qpid.framing.ConnectionTuneBody;
-import org.apache.qpid.framing.ExchangeBoundOkBody;
-import org.apache.qpid.framing.QueueDeleteOkBody;
+import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.protocol.AMQMethodListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* The state manager is responsible for managing the state of the protocol session. <p/> For each AMQProtocolHandler
* there is a separate state manager.
*/
-public class AMQStateManager implements AMQMethodListener
+public class AMQStateManager
{
private static final Logger _logger = LoggerFactory.getLogger(AMQStateManager.class);
@@ -72,15 +45,15 @@ public class AMQStateManager implements AMQMethodListener
/** The current state */
private AMQState _currentState;
+
/**
* Maps from an AMQState instance to a Map from Class to StateTransitionHandler. The class must be a subclass of
* AMQFrame.
*/
- protected final Map _state2HandlersMap = new HashMap();
- private final CopyOnWriteArraySet _stateListeners = new CopyOnWriteArraySet();
+
private final Object _stateLock = new Object();
- private static final long MAXIMUM_STATE_WAIT_TIME = 30000L;
+ private static final long MAXIMUM_STATE_WAIT_TIME = Long.parseLong(System.getProperty("amqj.MaximumStateWait", "30000"));
public AMQStateManager()
{
@@ -96,54 +69,11 @@ public class AMQStateManager implements AMQMethodListener
{
_protocolSession = protocolSession;
_currentState = state;
- if (register)
- {
- registerListeners();
- }
- }
- protected void registerListeners()
- {
- Map frame2handlerMap = new HashMap();
-
- // we need to register a map for the null (i.e. all state) handlers otherwise you get
- // a stack overflow in the handler searching code when you present it with a frame for which
- // no handlers are registered
- //
- _state2HandlersMap.put(null, frame2handlerMap);
-
- frame2handlerMap = new HashMap();
- frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap);
-
- frame2handlerMap = new HashMap();
- frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap);
-
- frame2handlerMap = new HashMap();
- frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap);
-
- //
- // ConnectionOpen handlers
- //
- frame2handlerMap = new HashMap();
- frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandler.getInstance());
- frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance());
- frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance());
- frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance());
- frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance());
- frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance());
- frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap);
}
+
+
public AMQState getCurrentState()
{
return _currentState;
@@ -160,72 +90,17 @@ public class AMQStateManager implements AMQMethodListener
}
}
- public void error(Exception e)
- {
- _logger.debug("State manager receive error notification: " + e);
- synchronized (_stateListeners)
- {
- final Iterator it = _stateListeners.iterator();
- while (it.hasNext())
- {
- final StateListener l = (StateListener) it.next();
- l.error(e);
- }
- }
- }
public <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws AMQException
{
- StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod());
- if (handler != null)
- {
- handler.methodReceived(this, _protocolSession, evt);
-
- return true;
- }
- return false;
+ B method = evt.getMethod();
+
+ // StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod());
+ method.execute(_protocolSession.getMethodDispatcher(), evt.getChannelId());
+ return true;
}
- protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState, AMQMethodBody frame)
- // throws IllegalStateTransitionException
- {
- final Class clazz = frame.getClass();
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Looking for state[" + currentState + "] transition handler for frame " + clazz);
- }
-
- final Map classToHandlerMap = (Map) _state2HandlersMap.get(currentState);
-
- if (classToHandlerMap == null)
- {
- // if no specialised per state handler is registered look for a
- // handler registered for "all" states
- return findStateTransitionHandler(null, frame);
- }
-
- final StateAwareMethodListener handler = (StateAwareMethodListener) classToHandlerMap.get(clazz);
- if (handler == null)
- {
- if (currentState == null)
- {
- _logger.debug("No state transition handler defined for receiving frame " + frame);
-
- return null;
- }
- else
- {
- // if no specialised per state handler is registered look for a
- // handler registered for "all" states
- return findStateTransitionHandler(null, frame);
- }
- }
- else
- {
- return handler;
- }
- }
public void attainState(final AMQState s) throws AMQException
{
@@ -272,4 +147,46 @@ public class AMQStateManager implements AMQMethodListener
{
_protocolSession = session;
}
+
+ public MethodRegistry getMethodRegistry()
+ {
+ return getProtocolSession().getMethodRegistry();
+ }
+
+ public AMQState attainState(Set<AMQState> stateSet) throws AMQException
+ {
+ synchronized (_stateLock)
+ {
+ final long waitUntilTime = System.currentTimeMillis() + MAXIMUM_STATE_WAIT_TIME;
+ long waitTime = MAXIMUM_STATE_WAIT_TIME;
+
+ while (!stateSet.contains(_currentState) && (waitTime > 0))
+ {
+ try
+ {
+ _stateLock.wait(MAXIMUM_STATE_WAIT_TIME);
+ }
+ catch (InterruptedException e)
+ {
+ _logger.warn("Thread interrupted");
+ }
+
+ if (!stateSet.contains(_currentState))
+ {
+ waitTime = waitUntilTime - System.currentTimeMillis();
+ }
+ }
+
+ if (!stateSet.contains(_currentState))
+ {
+ _logger.warn("State not achieved within permitted time. Current state " + _currentState
+ + ", desired state: " + stateSet);
+ throw new AMQException(null, "State not achieved within permitted time. Current state " + _currentState
+ + ", desired state: " + stateSet, null);
+ }
+ return _currentState;
+ }
+
+
+ }
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java b/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java
index b3932533ce..8c65f56af3 100644
--- a/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java
+++ b/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java
@@ -21,6 +21,7 @@
package org.apache.qpid.client.state;
import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQMethodBody;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.protocol.AMQMethodEvent;
@@ -29,8 +30,9 @@ import org.apache.qpid.protocol.AMQMethodEvent;
* the opportunity to update state.
*
*/
-public interface StateAwareMethodListener
+public interface StateAwareMethodListener<B extends AMQMethodBody>
{
- void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession,
- AMQMethodEvent evt) throws AMQException;
+
+ void methodReceived(AMQStateManager stateManager, B body, int channelId) throws AMQException;
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java
index 5482e48699..b2f7ae8395 100644
--- a/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java
+++ b/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java
@@ -24,6 +24,7 @@ import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.ConnectFuture;
import org.apache.mina.common.IoConnector;
import org.apache.mina.common.SimpleByteBufferAllocator;
+import org.apache.mina.transport.socket.nio.ExistingSocketConnector;
import org.apache.mina.transport.socket.nio.SocketConnectorConfig;
import org.apache.mina.transport.socket.nio.SocketSessionConfig;
@@ -36,6 +37,9 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
public class SocketTransportConnection implements ITransportConnection
{
@@ -83,8 +87,34 @@ public class SocketTransportConnection implements ITransportConnection
_logger.info("send-buffer-size = " + scfg.getSendBufferSize());
scfg.setReceiveBufferSize(Integer.getInteger("amqj.receiveBufferSize", DEFAULT_BUFFER_SIZE));
_logger.info("recv-buffer-size = " + scfg.getReceiveBufferSize());
- final InetSocketAddress address = new InetSocketAddress(brokerDetail.getHost(), brokerDetail.getPort());
- _logger.info("Attempting connection to " + address);
+
+ final InetSocketAddress address;
+
+ if (brokerDetail.getTransport().equals(BrokerDetails.SOCKET))
+ {
+ address = null;
+
+ Socket socket = TransportConnection.removeOpenSocket(brokerDetail.getHost());
+
+ if (socket != null)
+ {
+ _logger.info("Using existing Socket:" + socket);
+
+ ((ExistingSocketConnector) ioConnector).setOpenSocket(socket);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Active Socket must be provided for broker " +
+ "with 'socket://<SocketID>' transport:" + brokerDetail);
+ }
+ }
+ else
+ {
+ address = new InetSocketAddress(brokerDetail.getHost(), brokerDetail.getPort());
+ _logger.info("Attempting connection to " + address);
+ }
+
+
ConnectFuture future = ioConnector.connect(address, protocolHandler);
// wait for connection to complete
diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
index 883cb2340a..a871c754b5 100644
--- a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
+++ b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
@@ -23,6 +23,8 @@ package org.apache.qpid.client.transport;
import org.apache.mina.common.IoConnector;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.transport.socket.nio.ExistingSocketConnector;
+import org.apache.mina.transport.socket.nio.MultiThreadSocketConnector;
import org.apache.mina.transport.socket.nio.SocketConnector;
import org.apache.mina.transport.vmpipe.VmPipeAcceptor;
import org.apache.mina.transport.vmpipe.VmPipeAddress;
@@ -34,8 +36,10 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.net.Socket;
+
/**
* The TransportConnection is a helper class responsible for connecting to an AMQ server. It sets up the underlying
@@ -54,12 +58,25 @@ public class TransportConnection
private static final int TCP = 0;
private static final int VM = 1;
+ private static final int SOCKET = 2;
private static Logger _logger = LoggerFactory.getLogger(TransportConnection.class);
private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQPFastProtocolHandler";
- public static ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException
+ private static Map<String, Socket> _openSocketRegister = new ConcurrentHashMap<String, Socket>();
+
+ public static void registerOpenSocket(String socketID, Socket openSocket)
+ {
+ _openSocketRegister.put(socketID, openSocket);
+ }
+
+ public static Socket removeOpenSocket(String socketID)
+ {
+ return _openSocketRegister.remove(socketID);
+ }
+
+ public static synchronized ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException
{
int transport = getTransport(details.getTransport());
@@ -68,7 +85,7 @@ public class TransportConnection
throw new AMQNoTransportForProtocolException(details, null, null);
}
- if (transport == _currentInstance)
+ /* if (transport == _currentInstance)
{
if (transport == VM)
{
@@ -83,13 +100,23 @@ public class TransportConnection
}
}
- _currentInstance = transport;
+ _currentInstance = transport;*/
+ ITransportConnection instance;
switch (transport)
{
-
+ case SOCKET:
+ instance =
+ new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory()
+ {
+ public IoConnector newSocketConnector()
+ {
+ return new ExistingSocketConnector();
+ }
+ });
+ break;
case TCP:
- _instance = new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory()
+ instance = new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory()
{
public IoConnector newSocketConnector()
{
@@ -97,38 +124,44 @@ public class TransportConnection
// FIXME - this needs to be sorted to use the new Mina MultiThread SA.
if (Boolean.getBoolean("qpidnio"))
{
- _logger.error("Using Qpid NIO - sysproperty 'qpidnio' is set.");
- // result = new org.apache.qpid.nio.SocketConnector(); // non-blocking connector
+ _logger.warn("Using Qpid MultiThreaded NIO - " + (System.getProperties().containsKey("qpidnio")
+ ? "Qpid NIO is new default"
+ : "Sysproperty 'qpidnio' is set"));
+ result = new MultiThreadSocketConnector();
}
- // else
-
+ else
{
_logger.info("Using Mina NIO");
result = new SocketConnector(); // non-blocking connector
}
-
// Don't have the connector's worker thread wait around for other connections (we only use
// one SocketConnector per connection at the moment anyway). This allows short-running
// clients (like unit tests) to complete quickly.
result.setWorkerTimeout(0);
-
return result;
}
});
break;
-
case VM:
{
- _instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker"));
+ instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker"));
break;
}
+ default:
+ // FIXME: TGM
+ throw new AMQNoTransportForProtocolException(details, null, null);
}
- return _instance;
+ return instance;
}
private static int getTransport(String transport)
{
+ if (transport.equals(BrokerDetails.SOCKET))
+ {
+ return SOCKET;
+ }
+
if (transport.equals(BrokerDetails.TCP))
{
return TCP;
@@ -151,7 +184,15 @@ public class TransportConnection
{
if (AutoCreate)
{
- createVMBroker(port);
+ if (AutoCreate)
+ {
+ createVMBroker(port);
+ }
+ else
+ {
+ throw new AMQVMBrokerCreationException(null, port, "VM Broker on port " + port
+ + " does not exist. Auto create disabled.", null);
+ }
}
else
{
@@ -281,16 +322,17 @@ public class TransportConnection
public static void killAllVMBrokers()
{
_logger.info("Killing all VM Brokers");
- _acceptor.unbindAll();
-
- Iterator keys = _inVmPipeAddress.keySet().iterator();
-
- while (keys.hasNext())
+ if (_acceptor != null)
{
- int id = (Integer) keys.next();
- _inVmPipeAddress.remove(id);
+ _acceptor.unbindAll();
}
-
+ synchronized (_inVmPipeAddress)
+ {
+ _inVmPipeAddress.clear();
+ }
+ _acceptor = null;
+ _currentInstance = -1;
+ _currentVMPort = -1;
}
public static void killVMBroker(int port)
@@ -300,6 +342,8 @@ public class TransportConnection
{
_logger.info("Killing VM Broker:" + port);
_inVmPipeAddress.remove(port);
+ // This does need to be sychronized as otherwise mina can hang
+ // if a new connection is made
_acceptor.unbind(pipe);
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java
index d9137dc8b1..dca6efba67 100644
--- a/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java
+++ b/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java
@@ -22,15 +22,12 @@ package org.apache.qpid.client.transport;
import org.apache.mina.common.ConnectFuture;
import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.transport.vmpipe.QpidVmPipeConnector;
import org.apache.mina.transport.vmpipe.VmPipeAddress;
import org.apache.mina.transport.vmpipe.VmPipeConnector;
-
import org.apache.qpid.client.protocol.AMQProtocolHandler;
import org.apache.qpid.jms.BrokerDetails;
-import org.apache.qpid.pool.PoolingFilter;
-import org.apache.qpid.pool.ReferenceCountingExecutorService;
import org.apache.qpid.pool.ReadWriteThreadModel;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -49,10 +46,10 @@ public class VmPipeTransportConnection implements ITransportConnection
public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException
{
- final VmPipeConnector ioConnector = new VmPipeConnector();
+ final VmPipeConnector ioConnector = new QpidVmPipeConnector();
final IoServiceConfig cfg = ioConnector.getDefaultConfig();
- cfg.setThreadModel(ReadWriteThreadModel.getInstance());
+ cfg.setThreadModel(ReadWriteThreadModel.getInstance());
final VmPipeAddress address = new VmPipeAddress(_port);
_logger.info("Attempting connection to " + address);
diff --git a/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java b/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
index 133285fe87..72ba16086d 100644
--- a/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
+++ b/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
@@ -33,8 +33,10 @@ public interface BrokerDetails
*/
public static final String OPTIONS_RETRY = "retries";
public static final String OPTIONS_CONNECT_TIMEOUT = "connecttimeout";
+ public static final String OPTIONS_CONNECT_DELAY = "connectdelay";
public static final int DEFAULT_PORT = 5672;
+ public static final String SOCKET = "socket";
public static final String TCP = "tcp";
public static final String VM = "vm";
diff --git a/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java b/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java
index 11376467d7..da8cd4f750 100644
--- a/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java
+++ b/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java
@@ -21,6 +21,7 @@
package org.apache.qpid.jms;
import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ProtocolVersion;
import java.util.List;
@@ -45,7 +46,7 @@ public interface ConnectionURL
public static final String OPTIONS_TEMPORARY_QUEUE_EXCHANGE = "temporaryQueueExchange";
public static final byte URL_0_8 = 1;
public static final byte URL_0_10 = 2;
-
+
String getURL();
String getFailoverMethod();
@@ -89,4 +90,5 @@ public interface ConnectionURL
AMQShortString getTemporaryQueueExchangeName();
AMQShortString getTemporaryTopicExchangeName();
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java b/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java
index 6ec883ff0b..8e3ccc3b02 100644
--- a/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java
+++ b/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java
@@ -34,7 +34,6 @@ public class FailoverPolicy
private static final long MINUTE = 60000L;
private static final long DEFAULT_METHOD_TIMEOUT = 1 * MINUTE;
- private static final long DEFAULT_FAILOVER_TIMEOUT = 4 * MINUTE;
private FailoverMethod[] _methods = new FailoverMethod[1];
@@ -161,16 +160,7 @@ public class FailoverPolicy
}
else
{
- if ((now - _lastFailTime) >= DEFAULT_FAILOVER_TIMEOUT)
- {
- _logger.info("Failover timeout");
-
- return false;
- }
- else
- {
- _lastMethodTime = now;
- }
+ _lastMethodTime = now;
}
}
else
diff --git a/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java b/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
index b91fc2d960..b830c377b8 100644
--- a/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
+++ b/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
@@ -50,4 +50,8 @@ public interface MessageProducer extends javax.jms.MessageProducer
void send(Destination destination, Message message, int deliveryMode,
int priority, long timeToLive, boolean mandatory, boolean immediate)
throws JMSException;
+
+ void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
+ boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException;
+
}
diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java
index 74a46de63b..9c172da6a2 100644
--- a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java
+++ b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java
@@ -22,7 +22,6 @@ package org.apache.qpid.jms.failover;
import org.apache.qpid.jms.BrokerDetails;
import org.apache.qpid.jms.ConnectionURL;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,34 +34,22 @@ public class FailoverRoundRobinServers implements FailoverMethod
/** The default number of times to retry each server */
public static final int DEFAULT_SERVER_RETRIES = 0;
- /**
- * The index into the hostDetails array of the broker to which we are connected
- */
+ /** The index into the hostDetails array of the broker to which we are connected */
private int _currentBrokerIndex = -1;
- /**
- * The number of times to retry connecting for each server
- */
+ /** The number of times to retry connecting for each server */
private int _serverRetries;
- /**
- * The current number of retry attempts made
- */
+ /** The current number of retry attempts made */
private int _currentServerRetry;
- /**
- * The number of times to cycle through the servers
- */
+ /** The number of times to cycle through the servers */
private int _cycleRetries;
- /**
- * The current number of cycles performed.
- */
+ /** The current number of cycles performed. */
private int _currentCycleRetries;
- /**
- * Array of BrokerDetail used to make connections.
- */
+ /** Array of BrokerDetail used to make connections. */
private ConnectionURL _connectionDetails;
public FailoverRoundRobinServers(ConnectionURL connectionDetails)
@@ -128,6 +115,8 @@ public class FailoverRoundRobinServers implements FailoverMethod
public BrokerDetails getNextBrokerDetails()
{
+ boolean doDelay = false;
+
if (_currentBrokerIndex == (_connectionDetails.getBrokerCount() - 1))
{
if (_currentServerRetry < _serverRetries)
@@ -143,6 +132,7 @@ public class FailoverRoundRobinServers implements FailoverMethod
else
{
_logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex));
+ doDelay=true;
}
_currentServerRetry++;
@@ -175,6 +165,7 @@ public class FailoverRoundRobinServers implements FailoverMethod
else
{
_logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex));
+ doDelay=true;
}
_currentServerRetry++;
@@ -189,7 +180,28 @@ public class FailoverRoundRobinServers implements FailoverMethod
}
}
- return _connectionDetails.getBrokerDetails(_currentBrokerIndex);
+ BrokerDetails broker = _connectionDetails.getBrokerDetails(_currentBrokerIndex);
+
+ String delayStr = broker.getProperty(BrokerDetails.OPTIONS_CONNECT_DELAY);
+ if (delayStr != null && doDelay)
+ {
+ Long delay = Long.parseLong(delayStr);
+ _logger.info("Delay between connect retries:" + delay);
+ try
+ {
+ Thread.sleep(delay);
+ }
+ catch (InterruptedException ie)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ _logger.info("No delay between connect retries, use tcp://host:port?connectdelay='value' to enable.");
+ }
+
+ return broker;
}
public void setBroker(BrokerDetails broker)
diff --git a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java
index d59c1fb98d..b1bc3cee96 100644
--- a/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java
+++ b/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java
@@ -22,25 +22,23 @@ package org.apache.qpid.jms.failover;
import org.apache.qpid.jms.BrokerDetails;
import org.apache.qpid.jms.ConnectionURL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class FailoverSingleServer implements FailoverMethod
{
+ private static final Logger _logger = LoggerFactory.getLogger(FailoverSingleServer.class);
+
/** The default number of times to rety a conection to this server */
public static final int DEFAULT_SERVER_RETRIES = 1;
- /**
- * The details of the Single Server
- */
+ /** The details of the Single Server */
private BrokerDetails _brokerDetail;
- /**
- * The number of times to retry connecting to the sever
- */
+ /** The number of times to retry connecting to the sever */
private int _retries;
- /**
- * The current number of attempts made to the server
- */
+ /** The current number of attempts made to the server */
private int _currentRetries;
@@ -78,7 +76,7 @@ public class FailoverSingleServer implements FailoverMethod
public BrokerDetails getCurrentBrokerDetails()
{
- return _brokerDetail;
+ return _brokerDetail;
}
public BrokerDetails getNextBrokerDetails()
@@ -91,11 +89,29 @@ public class FailoverSingleServer implements FailoverMethod
{
if (_currentRetries < _retries)
{
- _currentRetries ++;
+ _currentRetries++;
}
+ }
+
- return _brokerDetail;
+ String delayStr = _brokerDetail.getProperty(BrokerDetails.OPTIONS_CONNECT_DELAY);
+ if (delayStr != null && _currentRetries != 1)
+ {
+ Long delay = Long.parseLong(delayStr);
+ _logger.info("Delay between connect retries:" + delay);
+ try
+ {
+
+ Thread.sleep(delay);
+ }
+ catch (InterruptedException ie)
+ {
+ _logger.info("No delay between connect retries, use tcp://host:port?connectdelay='value' to enable.");
+ return null;
+ }
}
+
+ return _brokerDetail;
}
public void setBroker(BrokerDetails broker)
@@ -138,10 +154,10 @@ public class FailoverSingleServer implements FailoverMethod
public String toString()
{
- return "SingleServer:\n"+
- "Max Retries:"+_retries+
- "\nCurrent Retry:"+_currentRetries+
- "\n"+_brokerDetail+"\n";
+ return "SingleServer:\n" +
+ "Max Retries:" + _retries +
+ "\nCurrent Retry:" + _currentRetries +
+ "\n" + _brokerDetail + "\n";
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java b/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java
index 42a168bad8..43ac56dee9 100644
--- a/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java
+++ b/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java
@@ -99,8 +99,6 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor
_logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL));
}
-
-
createConnectionFactories(data, environment);
createDestinations(data, environment);
diff --git a/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java b/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java
index a246352d8b..2fe01fc126 100644
--- a/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java
+++ b/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java
@@ -183,7 +183,7 @@ public class TestLargePublisher
}
catch (UnknownHostException e)
{
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ e.printStackTrace();
}
catch (AMQException e)
{
diff --git a/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java b/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java
index 33891142b5..37b4ff1498 100644
--- a/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java
+++ b/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java
@@ -133,7 +133,7 @@ public class TestPublisher
}
catch (JMSException e)
{
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ e.printStackTrace();
}
}
@@ -163,7 +163,7 @@ public class TestPublisher
}
catch (UnknownHostException e)
{
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ e.printStackTrace();
}
catch (AMQException e)
{
diff --git a/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java b/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
index 8724c65b61..fe418535d6 100644
--- a/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
+++ b/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
@@ -126,7 +126,7 @@ public class AMQQueueDeferredOrderingTest extends TestCase
_logger.info("Consuming messages");
for (int i = 0; i < NUM_MESSAGES; i++)
{
- Message msg = consumer.receive(1500);
+ Message msg = consumer.receive(3000);
assertNotNull("Message should not be null", msg);
assertTrue("Message should be a text message", msg instanceof TextMessage);
assertEquals("Message content does not match expected", Integer.toString(i), ((TextMessage) msg).getText());
diff --git a/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java b/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
index 75e50ee09b..136b9b94b6 100644
--- a/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
+++ b/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
@@ -20,13 +20,17 @@
*/
package org.apache.qpid.client;
+import junit.framework.TestCase;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
import org.apache.qpid.testutil.QpidTestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
@@ -34,7 +38,9 @@ import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+import java.util.Hashtable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
diff --git a/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java b/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java
index 2eb511f8cd..12b84b1495 100644
--- a/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java
+++ b/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java
@@ -20,13 +20,17 @@
*/
package org.apache.qpid.client;
+import junit.framework.TestCase;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
import org.apache.qpid.testutil.QpidTestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
@@ -34,6 +38,9 @@ import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+
+import java.util.Hashtable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
diff --git a/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java b/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
index 882915fb8f..c920499a07 100644
--- a/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
+++ b/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
@@ -29,7 +29,16 @@ import org.apache.qpid.testutil.QpidTestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.jms.*;
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.spi.InitialContextFactory;
@@ -65,12 +74,15 @@ public class ResetMessageListenerTest extends QpidTestCase
private final CountDownLatch _allFirstMessagesSent = new CountDownLatch(2); // all messages Sent Lock
private final CountDownLatch _allSecondMessagesSent = new CountDownLatch(2); // all messages Sent Lock
private final CountDownLatch _allFirstMessagesSent010 = new CountDownLatch(MSG_COUNT); // all messages Sent Lock
- private final CountDownLatch _allSecondMessagesSent010 = new CountDownLatch(MSG_COUNT); // all messages Sent Lock
+ private final CountDownLatch _allSecondMessagesSent010 = new CountDownLatch(MSG_COUNT); // all messages Sent Lock
+
+ private String oldImmediatePrefetch;
protected void setUp() throws Exception
{
super.setUp();
+ oldImmediatePrefetch = System.getProperty(AMQSession.IMMEDIATE_PREFETCH);
System.setProperty(AMQSession.IMMEDIATE_PREFETCH, "true");
_clientConnection = getConnection("guest", "guest");
@@ -109,8 +121,12 @@ public class ResetMessageListenerTest extends QpidTestCase
{
_clientConnection.close();
- _producerConnection.close();
super.tearDown();
+ if (oldImmediatePrefetch == null)
+ {
+ oldImmediatePrefetch = AMQSession.IMMEDIATE_PREFETCH_DEFAULT;
+ }
+ System.setProperty(AMQSession.IMMEDIATE_PREFETCH, oldImmediatePrefetch);
}
public void testAsynchronousRecieve()
@@ -238,7 +254,7 @@ public class ResetMessageListenerTest extends QpidTestCase
try
{
- _allSecondMessagesSent.await(1000, TimeUnit.MILLISECONDS);
+ _allSecondMessagesSent.await(5000, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
index 737daeb350..61ba3aad3a 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
@@ -60,6 +60,7 @@ public class PropertyValueTest extends QpidTestCase implements MessageListener
private final List<String> messages = new ArrayList<String>();
private int _count = 1;
public String _connectionString = "vm://:1";
+ private static final String USERNAME = "guest";
protected void setUp() throws Exception
{
@@ -171,7 +172,7 @@ public class PropertyValueTest extends QpidTestCase implements MessageListener
m.setJMSReplyTo(q);
m.setStringProperty("TempQueue", q.toString());
- _logger.trace("Message:" + m);
+ _logger.debug("Message:" + m);
Assert.assertEquals("Check temp queue has been set correctly", m.getJMSReplyTo().toString(),
m.getStringProperty("TempQueue"));
@@ -287,7 +288,14 @@ public class PropertyValueTest extends QpidTestCase implements MessageListener
((AMQMessage) m).setVoidProperty(new AMQShortString("void"));
Assert.assertTrue("Check void properties are correctly transported",
- ((AMQMessage) m).getPropertyHeaders().containsKey("void"));
+ ((AMQMessage) m).getPropertyHeaders().containsKey("void"));
+
+ //JMSXUserID
+ if (m.getStringProperty("JMSXUserID") != null)
+ {
+ Assert.assertEquals("Check 'JMSXUserID' is supported ", USERNAME,
+ m.getStringProperty("JMSXUserID"));
+ }
}
received.clear();
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java
index ed4f6041df..987b30ce28 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java
@@ -21,7 +21,7 @@
package org.apache.qpid.test.unit.basic;
import junit.framework.TestCase;
-
+import org.apache.qpid.AMQException;
import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.client.AMQDestination;
import org.apache.qpid.client.AMQQueue;
@@ -29,11 +29,14 @@ import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.BasicMessageProducer;
import org.apache.qpid.client.transport.TransportConnection;
import org.apache.qpid.testutil.QpidTestCase;
+import org.apache.qpid.url.URLSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.jms.Connection;
import javax.jms.DeliveryMode;
+import javax.jms.InvalidSelectorException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
@@ -47,11 +50,12 @@ public class SelectorTest extends QpidTestCase implements MessageListener
private AMQSession _session;
private int count;
public String _connectionString = "vm://:1";
+ private static final String INVALID_SELECTOR = "Cost LIKE 5";
protected void setUp() throws Exception
{
super.setUp();
- init((AMQConnection) getConnection("guest", "guest"));
+ init((AMQConnection) getConnection("guest", "guest"));
}
protected void tearDown() throws Exception
@@ -59,19 +63,19 @@ public class SelectorTest extends QpidTestCase implements MessageListener
super.tearDown();
}
- private void init(AMQConnection connection) throws Exception
+ private void init(AMQConnection connection) throws JMSException
{
init(connection, new AMQQueue(connection, randomize("SessionStartTest"), true));
}
- private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ private void init(AMQConnection connection, AMQDestination destination) throws JMSException
{
_connection = connection;
_destination = destination;
connection.start();
String selector = null;
- // selector = "Cost = 2 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT;
+ selector = "Cost = 2 AND \"property-with-hyphen\" = 'wibble'";
// selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT;
_session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
@@ -79,13 +83,17 @@ public class SelectorTest extends QpidTestCase implements MessageListener
_session.createConsumer(destination, selector).setMessageListener(this);
}
- public synchronized void test() throws JMSException, InterruptedException
+ public synchronized void test() throws Exception
{
try
{
+
+ init((AMQConnection) getConnection("guest", "guest", randomize("Client")));
+
Message msg = _session.createTextMessage("Message");
msg.setJMSPriority(1);
msg.setIntProperty("Cost", 2);
+ msg.setStringProperty("property-with-hyphen", "wibble");
msg.setJMSType("Special");
_logger.info("Sending Message:" + msg);
@@ -105,10 +113,147 @@ public class SelectorTest extends QpidTestCase implements MessageListener
// throw new RuntimeException("Did not get message!");
}
}
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ else
+ {
+ System.out.println("SUCCESS!!");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ _logger.debug("IE :" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ }
+ catch (URLSyntaxException e)
+ {
+ _logger.debug("URL:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ fail("Wrong exception");
+ }
+ catch (AMQException e)
+ {
+ _logger.debug("AMQ:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ fail("Wrong exception");
+ }
+
finally
{
- _session.close();
- _connection.close();
+ if (_session != null)
+ {
+ _session.close();
+ }
+ if (_connection != null)
+ {
+ _connection.close();
+ }
+ }
+ }
+
+
+ public void testInvalidSelectors() throws Exception
+ {
+ Connection connection = null;
+
+ try
+ {
+ connection = getConnection("guest", "guest", randomize("Client"));
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ }
+ catch (JMSException e)
+ {
+ fail(e.getMessage());
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+ catch (URLSyntaxException e)
+ {
+ fail("Error:" + e.getMessage());
+ }
+
+ //Try Creating a Browser
+ try
+ {
+ _session.createBrowser(_session.createQueue("Ping"), INVALID_SELECTOR);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ else
+ {
+ _logger.debug("SUCCESS!!");
+ }
+ }
+
+ //Try Creating a Consumer
+ try
+ {
+ _session.createConsumer(_session.createQueue("Ping"), INVALID_SELECTOR);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ else
+ {
+ _logger.debug("SUCCESS!!");
+ }
+ }
+
+ //Try Creating a Receiever
+ try
+ {
+ _session.createReceiver(_session.createQueue("Ping"), INVALID_SELECTOR);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("JMS:" + e.getClass().getSimpleName() + ":" + e.getMessage());
+ if (!(e instanceof InvalidSelectorException))
+ {
+ fail("Wrong exception:" + e.getMessage());
+ }
+ else
+ {
+ _logger.debug("SUCCESS!!");
+ }
+ }
+
+ finally
+ {
+ if (_session != null)
+ {
+ try
+ {
+ _session.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Error cleaning up:" + e.getMessage());
+ }
+ }
+ if (_connection != null)
+ {
+ try
+ {
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Error cleaning up:" + e.getMessage());
+ }
+ }
}
}
@@ -127,9 +272,29 @@ public class SelectorTest extends QpidTestCase implements MessageListener
public static void main(String[] argv) throws Exception
{
SelectorTest test = new SelectorTest();
- test._connectionString = (argv.length == 0) ? "localhost:5672" : argv[0];
- test.setUp();
- test.test();
+ test._connectionString = (argv.length == 0) ? "localhost:3000" : argv[0];
+
+ try
+ {
+ while (true)
+ {
+ if (test._connectionString.contains("vm://:1"))
+ {
+ test.setUp();
+ }
+ test.test();
+
+ if (test._connectionString.contains("vm://:1"))
+ {
+ test.tearDown();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ e.printStackTrace();
+ }
}
public static junit.framework.Test suite()
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java
index 10c054a863..6f1ddebb0c 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTest.java
@@ -35,9 +35,9 @@ import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
-public class CloseTests extends QpidTestCase
+public class CloseTest extends QpidTestCase
{
- private static final Logger _logger = LoggerFactory.getLogger(CloseTests.class);
+ private static final Logger _logger = LoggerFactory.getLogger(CloseTest.class);
private static final String BROKER = "vm://:1";
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java
index 6a4e01affd..965c22af4a 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java
@@ -75,18 +75,11 @@ public class AMQSessionTest extends QpidTestCase
public void testCreateDurableSubscriber() throws JMSException
{
- try
- {
- TopicSubscriber subscriber = _session.createDurableSubscriber(_topic, "mysubname");
- assertEquals("Topic names should match from durable TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+ TopicSubscriber subscriber = _session.createDurableSubscriber(_topic, "mysubname");
+ assertEquals("Topic names should match from durable TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName());
- subscriber = _session.createDurableSubscriber(_topic, "mysubname", "abc", false);
- assertEquals("Topic names should match from durable TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName());
- }
- catch (Throwable e)
- {
- e.printStackTrace();
- }
+ subscriber = _session.createDurableSubscriber(_topic, "mysubname2", "abc", false);
+ assertEquals("Topic names should match from durable TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName());
}
public void testCreateQueueReceiver() throws JMSException
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
index 85fcf6d95a..b6776a1a44 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
@@ -37,7 +37,7 @@ import org.apache.qpid.protocol.AMQMethodEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListener
+public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListener<ChannelCloseBody>
{
private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandlerNoCloseOk.class);
@@ -48,14 +48,15 @@ public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListe
return _handler;
}
- public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ public void methodReceived(AMQStateManager stateManager, ChannelCloseBody method, int channelId)
throws AMQException
{
_logger.debug("ChannelClose method received");
- ChannelCloseBody method = (ChannelCloseBody) evt.getMethod();
+ final AMQProtocolSession session = stateManager.getProtocolSession();
- AMQConstant errorCode = AMQConstant.getConstant(method.replyCode);
- AMQShortString reason = method.replyText;
+
+ AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode());
+ AMQShortString reason = method.getReplyText();
if (_logger.isDebugEnabled())
{
_logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason);
@@ -95,6 +96,6 @@ public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListe
}
- protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason));
+ session.channelClosed(channelId, errorCode, String.valueOf(reason));
}
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java
index 08d6b0bcab..45a9ca1dd6 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java
@@ -26,17 +26,13 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.AMQTimeoutException;
import org.apache.qpid.testutil.QpidTestCase;
import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.handler.ClientMethodDispatcherImpl;
import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.client.state.AMQStateManager;
import org.apache.qpid.client.transport.TransportConnection;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.ChannelCloseOkBody;
-import org.apache.qpid.framing.ChannelOpenBody;
-import org.apache.qpid.framing.ChannelOpenOkBody;
-import org.apache.qpid.framing.ExchangeDeclareBody;
-import org.apache.qpid.framing.ExchangeDeclareOkBody;
+import org.apache.qpid.framing.*;
import org.apache.qpid.jms.ConnectionListener;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.url.URLSyntaxException;
@@ -53,6 +49,9 @@ import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
public class ChannelCloseTest extends QpidTestCase implements ExceptionListener, ConnectionListener
{
@@ -140,8 +139,11 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener,
/*
close channel and send guff then send ok no errors
+ REMOVE TEST - The behaviour after server has sent close is undefined.
+ the server should be free to fail as it may wish to reclaim its resources
+ immediately after close.
*/
- public void testSendingMethodsAfterClose() throws Exception
+ /*public void testSendingMethodsAfterClose() throws Exception
{
// this is testing an 0.8 connection
if(isBroker08())
@@ -167,7 +169,19 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener,
// Set StateManager to manager that ignores Close-oks
AMQProtocolSession protocolSession=
((AMQConnection) _connection).getProtocolHandler().getProtocolSession();
- AMQStateManager newStateManager=new NoCloseOKStateManager(protocolSession);
+
+ MethodDispatcher d = protocolSession.getMethodDispatcher();
+
+ MethodDispatcher wrappedDispatcher = (MethodDispatcher)
+ Proxy.newProxyInstance(d.getClass().getClassLoader(),
+ d.getClass().getInterfaces(),
+ new MethodDispatcherProxyHandler(
+ (ClientMethodDispatcherImpl) d));
+
+ protocolSession.setMethodDispatcher(wrappedDispatcher);
+
+
+ AMQStateManager newStateManager=new NoCloseOKStateManager(protocolSession);
newStateManager.changeState(oldStateManager.getCurrentState());
((AMQConnection) _connection).getProtocolHandler().setStateManager(newStateManager);
@@ -257,7 +271,7 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener,
}
}
}
-
+*/
private void createChannelAndTest(int channel) throws FailoverException
{
// Create A channel
@@ -284,10 +298,9 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener,
private void sendClose(int channel)
{
- AMQFrame frame =
- ChannelCloseOkBody.createAMQFrame(channel,
- ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(),
- ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion());
+ ChannelCloseOkBody body =
+ ((AMQConnection) _connection).getProtocolHandler().getMethodRegistry().createChannelCloseOkBody();
+ AMQFrame frame = body.generateFrame(channel);
((AMQConnection) _connection).getProtocolHandler().writeFrame(frame);
}
@@ -345,35 +358,43 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener,
private void declareExchange(int channelId, String _type, String _name, boolean nowait)
throws AMQException, FailoverException
{
- AMQFrame exchangeDeclare =
- ExchangeDeclareBody.createAMQFrame(channelId,
- ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(),
- ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion(), null, // arguments
- false, // autoDelete
- false, // durable
- new AMQShortString(_name), // exchange
- false, // internal
- nowait, // nowait
- true, // passive
- 0, // ticket
- new AMQShortString(_type)); // type
-
- if (nowait)
- {
- ((AMQConnection) _connection).getProtocolHandler().writeFrame(exchangeDeclare);
- }
- else
- {
- ((AMQConnection) _connection).getProtocolHandler().syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class,
- SYNC_TIMEOUT);
- }
+ ExchangeDeclareBody body =
+ ((AMQConnection) _connection).getProtocolHandler()
+ .getMethodRegistry()
+ .createExchangeDeclareBody(0,
+ new AMQShortString(_name),
+ new AMQShortString(_type),
+ true,
+ false,
+ false,
+ false,
+ nowait,
+ null);
+ AMQFrame exchangeDeclare = body.generateFrame(channelId);
+ AMQProtocolHandler protocolHandler = ((AMQConnection) _connection).getProtocolHandler();
+
+
+ if (nowait)
+ {
+ protocolHandler.writeFrame(exchangeDeclare);
+ }
+ else
+ {
+ protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class, SYNC_TIMEOUT);
+ }
+
+// return null;
+// }
+// }, (AMQConnection)_connection).execute();
+
}
private void createChannel(int channelId) throws AMQException, FailoverException
{
- ((AMQConnection) _connection).getProtocolHandler().syncWrite(ChannelOpenBody.createAMQFrame(channelId,
- ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(),
- ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion(), null), // outOfBand
+ ChannelOpenBody body =
+ ((AMQConnection) _connection).getProtocolHandler().getMethodRegistry().createChannelOpenBody(null);
+
+ ((AMQConnection) _connection).getProtocolHandler().syncWrite(body.generateFrame(channelId), // outOfBand
ChannelOpenOkBody.class);
}
@@ -402,4 +423,28 @@ public class ChannelCloseTest extends QpidTestCase implements ExceptionListener,
public void failoverComplete()
{ }
+
+ private static final class MethodDispatcherProxyHandler implements InvocationHandler
+ {
+ private final ClientMethodDispatcherImpl _underlyingDispatcher;
+ private final ChannelCloseMethodHandlerNoCloseOk _handler = ChannelCloseMethodHandlerNoCloseOk.getInstance();
+
+
+ public MethodDispatcherProxyHandler(ClientMethodDispatcherImpl dispatcher)
+ {
+ _underlyingDispatcher = dispatcher;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ if(method.getName().equals("dispatchChannelClose"))
+ {
+ _handler.methodReceived(_underlyingDispatcher.getStateManager(),
+ (ChannelCloseBody) args[0], (Integer)args[1]);
+ }
+ Method dispatcherMethod = _underlyingDispatcher.getClass().getMethod(method.getName(), method.getParameterTypes());
+ return dispatcherMethod.invoke(_underlyingDispatcher, args);
+
+ }
+ }
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java
index d128f30727..c7eb745566 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java
@@ -59,49 +59,7 @@ public class NoCloseOKStateManager extends AMQStateManager
super(protocolSession);
}
- protected void registerListeners()
- {
- Map frame2handlerMap = new HashMap();
-
- // we need to register a map for the null (i.e. all state) handlers otherwise you get
- // a stack overflow in the handler searching code when you present it with a frame for which
- // no handlers are registered
- //
- _state2HandlersMap.put(null, frame2handlerMap);
-
- frame2handlerMap = new HashMap();
- frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap);
-
- frame2handlerMap = new HashMap();
- frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap);
-
- frame2handlerMap = new HashMap();
- frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap);
-
- //
- // ConnectionOpen handlers
- //
- frame2handlerMap = new HashMap();
- // Use Test Handler for Close methods to not send Close-OKs
- frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandlerNoCloseOk.getInstance());
-
- frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance());
- frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
- frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance());
- frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance());
- frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance());
- frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance());
- frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance());
- frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance());
- _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap);
- }
+
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
index 7eb74e2492..f856e8c20b 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
@@ -33,6 +33,7 @@ import org.apache.qpid.jms.Session;
import junit.framework.TestCase;
import javax.jms.Connection;
+import javax.jms.JMSException;
import javax.jms.QueueSession;
import javax.jms.TopicSession;
@@ -55,25 +56,30 @@ public class ConnectionTest extends TestCase
TransportConnection.killVMBroker(1);
}
- public void testSimpleConnection()
+ public void testSimpleConnection() throws Exception
{
+ AMQConnection conn = null;
try
{
- AMQConnection conn = new AMQConnection(_broker, "guest", "guest", "fred", "test");
- conn.close();
+ conn = new AMQConnection(_broker, "guest", "guest", "fred", "test");
}
catch (Exception e)
{
fail("Connection to " + _broker + " should succeed. Reason: " + e);
}
+ finally
+ {
+ conn.close();
+ }
}
- public void testDefaultExchanges()
+ public void testDefaultExchanges() throws Exception
{
+ AMQConnection conn = null;
try
{
- AMQConnection conn = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='"
+ conn = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='"
+ _broker
+ "?retries='1''&defaultQueueExchange='test.direct'"
+ "&defaultTopicExchange='test.topic'"
@@ -106,37 +112,53 @@ public class ConnectionTest extends TestCase
topicSession.close();
-
- conn.close();
}
catch (Exception e)
{
fail("Connection to " + _broker + " should succeed. Reason: " + e);
}
+ finally
+ {
+ conn.close();
+ }
}
- //fixme AMQAuthenticationException is not propogaged
- public void PasswordFailureConnection() throws Exception
+ //See QPID-771
+ public void testPasswordFailureConnection() throws Exception
{
+ AMQConnection conn = null;
try
{
- new AMQConnection("amqp://guest:rubbishpassword@clientid/test?brokerlist='" + _broker + "?retries='1''");
+ conn = new AMQConnection("amqp://guest:rubbishpassword@clientid/test?brokerlist='" + _broker + "?retries='1''");
fail("Connection should not be established password is wrong.");
}
catch (AMQException amqe)
{
- if (!(amqe instanceof AMQAuthenticationException))
+ if (amqe.getCause().getClass() == Exception.class)
+ {
+ System.err.println("QPID-594 : WARNING RACE CONDITION. Unable to determine cause of Connection Failure.");
+ return;
+ }
+
+ assertEquals("Exception was wrong type", JMSException.class, amqe.getCause().getClass());
+ Exception linked = ((JMSException) amqe.getCause()).getLinkedException();
+ assertEquals("Exception was wrong type", AMQAuthenticationException.class, linked.getClass());
+ }
+ finally
+ {
+ if (conn != null)
{
- fail("Correct exception not thrown. Excpected 'AMQAuthenticationException' got: " + amqe);
+ conn.close();
}
}
}
public void testConnectionFailure() throws Exception
{
+ AMQConnection conn = null;
try
{
- new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_NotRunning + "?retries='0''");
+ conn = new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_NotRunning + "?retries='0''");
fail("Connection should not be established");
}
catch (AMQException amqe)
@@ -146,14 +168,22 @@ public class ConnectionTest extends TestCase
fail("Correct exception not thrown. Excpected 'AMQConnectionException' got: " + amqe);
}
}
+ finally
+ {
+ if (conn != null)
+ {
+ conn.close();
+ }
+ }
}
public void testUnresolvedHostFailure() throws Exception
{
+ AMQConnection conn = null;
try
{
- new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_BadDNS + "?retries='0''");
+ conn = new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_BadDNS + "?retries='0''");
fail("Connection should not be established");
}
catch (AMQException amqe)
@@ -163,6 +193,38 @@ public class ConnectionTest extends TestCase
fail("Correct exception not thrown. Excpected 'AMQUnresolvedAddressException' got: " + amqe);
}
}
+ finally
+ {
+ if (conn != null)
+ {
+ conn.close();
+ }
+ }
+
+ }
+
+ public void testUnresolvedVirtualHostFailure() throws Exception
+ {
+ AMQConnection conn = null;
+ try
+ {
+ conn = new AMQConnection("amqp://guest:guest@clientid/rubbishhost?brokerlist='" + _broker + "?retries='0''");
+ fail("Connection should not be established");
+ }
+ catch (AMQException amqe)
+ {
+ if (!(amqe instanceof AMQConnectionFailureException))
+ {
+ fail("Correct exception not thrown. Excpected 'AMQConnectionFailureException' got: " + amqe);
+ }
+ }
+ finally
+ {
+ if (conn != null)
+ {
+ conn.close();
+ }
+ }
}
public void testClientIdCannotBeChanged() throws Exception
@@ -180,7 +242,10 @@ public class ConnectionTest extends TestCase
}
finally
{
- connection.close();
+ if (connection != null)
+ {
+ connection.close();
+ }
}
}
@@ -188,7 +253,14 @@ public class ConnectionTest extends TestCase
{
Connection connection = new AMQConnection(_broker, "guest", "guest",
null, "test");
- assertNotNull(connection.getClientID());
+ try
+ {
+ assertNotNull(connection.getClientID());
+ }
+ finally
+ {
+ connection.close();
+ }
connection.close();
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java
index bfbba61913..27adc4dd77 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java
@@ -473,6 +473,25 @@ public class ConnectionURLTest extends TestCase
}
+ public void testSocketProtocol() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@id/test" + "?brokerlist='socket://VM-Unique-socketID'";
+
+ try
+ {
+ AMQConnectionURL curl = new AMQConnectionURL(url);
+ assertNotNull(curl);
+ assertEquals(1, curl.getBrokerCount());
+ assertNotNull(curl.getBrokerDetails(0));
+ assertEquals(BrokerDetails.SOCKET, curl.getBrokerDetails(0).getTransport());
+ assertEquals("VM-Unique-socketID", curl.getBrokerDetails(0).getHost());
+ assertEquals("URL does not toString as expected", url, curl.toString());
+ }
+ catch (URLSyntaxException e)
+ {
+ fail(e.getMessage());
+ }
+ }
public static junit.framework.Test suite()
{
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java
index 3776ff767f..4cdd7dd7e8 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java
@@ -27,8 +27,9 @@ import org.apache.qpid.client.AMQConnection;
import org.apache.qpid.client.protocol.AMQProtocolHandler;
import org.apache.qpid.client.protocol.AMQProtocolSession;
import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.testutil.QpidTestCase;
-public class AMQProtocolSessionTest extends TestCase
+public class AMQProtocolSessionTest extends QpidTestCase
{
private static class AMQProtSession extends AMQProtocolSession
{
@@ -50,7 +51,7 @@ public class AMQProtocolSessionTest extends TestCase
}
//private Strings for test values and expected results
- private String _brokenAddress;
+ private String _brokenAddress = "tcp://myAddress;:";;
private String _generatedAddress;
private String _emptyAddress;
private String _generatedAddress_2;
@@ -64,7 +65,7 @@ public class AMQProtocolSessionTest extends TestCase
super.setUp();
//don't care about the values set here apart from the dummy IoSession
- _testSession = new AMQProtSession(null,new TestIoSession(),null);
+ _testSession = new AMQProtSession(null,new TestIoSession(), (AMQConnection) getConnection("guest", "guest"));
//initialise addresses for test and expected results
_port = 123;
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 e78c295ce5..d25986d991 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
@@ -29,8 +29,8 @@ import org.apache.qpid.testutil.QpidTestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import uk.co.thebadgerset.junit.concurrency.TestRunnable;
-import uk.co.thebadgerset.junit.concurrency.ThreadTestCoordinator;
+import org.apache.qpid.junit.concurrency.TestRunnable;
+import org.apache.qpid.junit.concurrency.ThreadTestCoordinator;
import javax.jms.Connection;
import javax.jms.Message;
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTests.java b/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java
index af19db5128..9caba63fe4 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTests.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java
@@ -25,7 +25,7 @@ import org.apache.qpid.testutil.QpidTestCase;
* Crash Recovery tests for durable subscription
*
*/
-public class DurableSubscriberTests extends QpidTestCase
+public class DurableSubscriberTest extends QpidTestCase
{
private final String _topicName = "durableSubscriberTopic";
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
index c4b94a6f36..6883a09f1b 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
@@ -39,6 +39,7 @@ import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
+import java.util.Enumeration;
/**
* @author Apache Software Foundation
@@ -84,6 +85,12 @@ public class JMSPropertiesTest extends QpidTestCase
sentMsg.setJMSType(JMS_TYPE);
sentMsg.setJMSReplyTo(JMS_REPLY_TO);
+ String JMSXGroupID_VALUE = "group";
+ sentMsg.setStringProperty("JMSXGroupID", JMSXGroupID_VALUE);
+
+ int JMSXGroupSeq_VALUE = 1;
+ sentMsg.setIntProperty("JMSXGroupSeq", JMSXGroupSeq_VALUE);
+
// send it
producer.send(sentMsg);
@@ -100,6 +107,30 @@ public class JMSPropertiesTest extends QpidTestCase
// assertEquals("JMS Delivery Mode mismatch",sentMsg.getJMSDeliveryMode(),rm.getJMSDeliveryMode());
assertEquals("JMS Type mismatch", sentMsg.getJMSType(), rm.getJMSType());
assertEquals("JMS Reply To mismatch", sentMsg.getJMSReplyTo(), rm.getJMSReplyTo());
+ assertTrue("JMSMessageID Does not start ID:", rm.getJMSMessageID().startsWith("ID:"));
+
+ //Validate that the JMSX values are correct
+ assertEquals("JMSXGroupID is not as expected:", JMSXGroupID_VALUE, rm.getStringProperty("JMSXGroupID"));
+ assertEquals("JMSXGroupSeq is not as expected:", JMSXGroupSeq_VALUE, rm.getIntProperty("JMSXGroupSeq"));
+
+ boolean JMSXGroupID_Available = false;
+ boolean JMSXGroupSeq_Available = false;
+ Enumeration props = con.getMetaData().getJMSXPropertyNames();
+ while (props.hasMoreElements())
+ {
+ String name = (String) props.nextElement();
+ if (name.equals("JMSXGroupID"))
+ {
+ JMSXGroupID_Available = true;
+ }
+ if (name.equals("JMSXGroupSeq"))
+ {
+ JMSXGroupSeq_Available = true;
+ }
+ }
+
+ assertTrue("JMSXGroupID not available.",JMSXGroupID_Available);
+ assertTrue("JMSXGroupSeq not available.",JMSXGroupSeq_Available);
con.close();
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
index 23bd2a960a..50ed1dee9e 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
@@ -41,6 +41,14 @@ import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.TopicSubscriber;
+/**
+ * @todo Code to check that a consumer gets only one particular method could be factored into a re-usable method (as
+ * a static on a base test helper class, e.g. TestUtils.
+ *
+ * @todo Code to create test end-points using session per connection, or all sessions on one connection, to be factored
+ * out to make creating this test variation simpler. Want to make this variation available through LocalCircuit,
+ * driven by the test model.
+ */
public class DurableSubscriptionTest extends QpidTestCase
{
private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class);
@@ -113,12 +121,26 @@ public class DurableSubscriptionTest extends QpidTestCase
con.close();
}
- public void testDurability() throws Exception
+ public void testDurabilityAUTOACK() throws Exception
{
+ durabilityImpl(Session.AUTO_ACKNOWLEDGE);
+ }
+ public void testDurabilityNOACKSessionPerConnection() throws Exception
+ {
+ durabilityImplSessionPerConnection(AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ public void testDurabilityAUTOACKSessionPerConnection() throws Exception
+ {
+ durabilityImplSessionPerConnection(Session.AUTO_ACKNOWLEDGE);
+ }
+
+ private void durabilityImpl(int ackMode) throws Exception
+ {
AMQConnection con = (AMQConnection) getConnection("guest", "guest");
- AMQTopic topic = new AMQTopic(con, "MyDurableSubscriptionTestTopic");
- Session session1 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ AMQTopic topic = new AMQTopic(con, "MyTopic");
+ Session session1 = con.createSession(false, ackMode);
MessageConsumer consumer1 = session1.createConsumer(topic);
Session sessionProd = con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
@@ -144,10 +166,82 @@ public class DurableSubscriptionTest extends QpidTestCase
consumer2.close();
- Session session3 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ producer.send(session1.createTextMessage("B"));
+
+ _logger.info("Receive message on consumer 1 :expecting B");
+ msg = consumer1.receive(500);
+ assertNotNull("Consumer 1 should get message 'B'.", msg);
+ assertEquals("Incorrect Message recevied on consumer1.", "B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(500);
+ assertNull("There should be no more messages for consumption on consumer1.", msg);
+
+ Session session3 = con.createSession(false, ackMode);
MessageConsumer consumer3 = session3.createDurableSubscriber(topic, "MySubscription");
- producer.send(session1.createTextMessage("B"));
+ _logger.info("Receive message on consumer 3 :expecting B");
+ msg = consumer3.receive(500);
+ assertNotNull("Consumer 3 should get message 'B'.", msg);
+ assertEquals("Incorrect Message recevied on consumer4.", "B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 3 :expecting null");
+ msg = consumer3.receive(500);
+ assertNull("There should be no more messages for consumption on consumer3.", msg);
+
+ consumer1.close();
+ consumer3.close();
+
+ con.close();
+ }
+
+ private void durabilityImplSessionPerConnection(int ackMode) throws Exception
+ {
+ Message msg;
+ // Create producer.
+ AMQConnection con0 = (AMQConnection) getConnection("guest", "guest");
+ con0.start();
+ Session session0 = con0.createSession(false, ackMode);
+
+ AMQTopic topic = new AMQTopic(con0, "MyTopic");
+
+ Session sessionProd = con0.createSession(false, ackMode);
+ MessageProducer producer = sessionProd.createProducer(topic);
+
+ // Create consumer 1.
+ AMQConnection con1 = (AMQConnection) getConnection("guest", "guest");
+ con1.start();
+ Session session1 = con1.createSession(false, ackMode);
+
+ MessageConsumer consumer1 = session0.createConsumer(topic);
+
+ // Create consumer 2.
+ AMQConnection con2 = (AMQConnection) getConnection("guest", "guest");
+ con2.start();
+ Session session2 = con2.createSession(false, ackMode);
+
+ TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription");
+
+ // Send message and check that both consumers get it and only it.
+ producer.send(session0.createTextMessage("A"));
+
+ msg = consumer1.receive(500);
+ assertNotNull("Message should be available", msg);
+ assertEquals("Message Text doesn't match", "A", ((TextMessage) msg).getText());
+ msg = consumer1.receive(500);
+ assertNull("There should be no more messages for consumption on consumer1.", msg);
+
+ msg = consumer2.receive();
+ assertNotNull(msg);
+ assertEquals("Consumer 2 should also received the first msg.", "A", ((TextMessage) msg).getText());
+ msg = consumer2.receive(500);
+ assertNull("There should be no more messages for consumption on consumer2.", msg);
+
+ // Detach the durable subscriber.
+ consumer2.close();
+ session2.close();
+ con2.close();
+
+ // Send message and receive on open consumer.
+ producer.send(session0.createTextMessage("B"));
_logger.info("Receive message on consumer 1 :expecting B");
msg = consumer1.receive(1000);
@@ -156,17 +250,27 @@ public class DurableSubscriptionTest extends QpidTestCase
msg = consumer1.receive(1000);
assertEquals(null, msg);
+ // Re-attach a new consumer to the durable subscription, and check that it gets the message that it missed.
+ AMQConnection con3 = (AMQConnection) getConnection("guest", "guest");
+ con3.start();
+ Session session3 = con3.createSession(false, ackMode);
+
+ TopicSubscriber consumer3 = session3.createDurableSubscriber(topic, "MySubscription");
+
_logger.info("Receive message on consumer 3 :expecting B");
- msg = consumer3.receive(1000);
- assertEquals("B", ((TextMessage) msg).getText());
+ msg = consumer3.receive(500);
+ assertNotNull("Consumer 3 should get message 'B'.", msg);
+ assertEquals("Incorrect Message recevied on consumer4.", "B", ((TextMessage) msg).getText());
_logger.info("Receive message on consumer 3 :expecting null");
- msg = consumer3.receive(1000);
- assertEquals(null, msg);
- // we need to unsubscribe as the session is NO_ACKNOWLEDGE
- // messages for the durable subscriber are not deleted so the test cannot
- // be run twice in a row
- session2.unsubscribe("MySubscription");
- con.close();
+ msg = consumer3.receive(500);
+ assertNull("There should be no more messages for consumption on consumer3.", msg);
+
+ consumer1.close();
+ consumer3.close();
+
+ con0.close();
+ con1.close();
+ con3.close();
}
public static junit.framework.Test suite()
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java
index 495cc98f31..f2f35644c6 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java
@@ -409,9 +409,9 @@ public class CommitRollbackTest extends QpidTestCase
}
else
{
- _logger.warn("Got 2, message prefetched wasn't cleared or messages was in transit when rollback occured");
+ _logger.warn("Got 2, message prefetched wasn't cleared or messages was in transit when rollback occured");
assertFalse("Already received message two", _gottwo);
-
+ assertFalse("Already received message redelivered two", _gottwoRedelivered);
_gottwo = true;
}
}
@@ -419,6 +419,13 @@ public class CommitRollbackTest extends QpidTestCase
verifyMessages(_consumer.receive(1000));
}
+ /**
+ * This test sends two messages receives on of them but doesn't ack it.
+ * The consumer is then closed
+ * the first message should be returned as redelivered.
+ * the second message should be delivered normally.
+ * @throws Exception
+ */
public void testSend2ThenCloseAfter1andTryAgain() throws Exception
{
assertTrue("session is not transacted", _session.getTransacted());
@@ -437,6 +444,7 @@ public class CommitRollbackTest extends QpidTestCase
assertTrue("Messasge is marked as redelivered" + result, !result.getJMSRedelivered());
_logger.info("Closing Consumer");
+
_consumer.close();
_logger.info("Creating New consumer");
diff --git a/java/common/build.xml b/java/common/build.xml
index af27eb8d8f..6172a680ec 100644
--- a/java/common/build.xml
+++ b/java/common/build.xml
@@ -26,8 +26,8 @@
<property name="generated.package" value="org/apache/qpid/framing" />
<property name="generated.dir" location="${module.precompiled}/${generated.package}" />
<property name="xml.spec.dir" location="${project.root}/../specs" />
- <property name="xml.spec.deps" value="amqp.0-8.xml cluster.0-8.xml" />
- <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/cluster.0-8.xml" />
+ <property name="xml.spec.deps" value="amqp.0-8.xml amqp.0-9.xml" />
+ <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml" />
<property name="mllib.dir" value="${project.root}/../python" />
<property name="gentools.timestamp" location="${generated.dir}/gentools.timestamp" />
<property name="jython.timestamp" location="${generated.dir}/jython.timestamp" />
@@ -72,6 +72,8 @@
<fileset dir="${gentools.home}/lib">
<include name="**/*.jar"/>
</fileset>
+ <pathelement path="${gentools.home}/lib/velocity-1.4.jar" />
+ <pathelement path="${gentools.home}/lib/velocity-dep-1.4.jar" />
</classpath>
</java>
<touch file="${gentools.timestamp}" />
diff --git a/java/common/pom.xml b/java/common/pom.xml
index ee3ae1b0a7..714087d843 100644
--- a/java/common/pom.xml
+++ b/java/common/pom.xml
@@ -139,18 +139,6 @@
<dependencies>
<dependency>
- <groupId>commons-configuration</groupId>
- <artifactId>commons-configuration</artifactId>
- </dependency>
-
- <dependency>
- <groupId>commons-cli</groupId>
- <artifactId>commons-cli</artifactId>
- </dependency>
-
-
-
- <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.4.0</version>
diff --git a/java/common/protocol-version.xml b/java/common/protocol-version.xml
index 2ed7dbff23..ee9db6049b 100644
--- a/java/common/protocol-version.xml
+++ b/java/common/protocol-version.xml
@@ -27,18 +27,23 @@
<property name="generated.dir" location="${generated.path}/${generated.package}" />
<property name="generated.timestamp" location="${generated.dir}/timestamp" />
<property name="xml.spec.dir" location="${topDirectoryLocation}/../specs" />
- <property name="xml.spec.deps" value="amqp.0-8.xml cluster.0-8.xml" />
- <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/cluster.0-8.xml" />
+ <property name="xml.spec.deps" value="amqp.0-8.xml amqp.0-9.xml" />
+ <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/amqp.0-9.xml" />
+ <property name="template.dir" value="${topDirectoryLocation}/common/templates" />
+
- <target name="generate" depends="compile_generator,check_generate_deps" unless="generation.notRequired">
+ <!--<target name="generate" depends="compile_generator,check_generate_deps" unless="generation.notRequired">-->
+ <target name="generate" depends="compile_generator" unless="generation.notRequired">
<mkdir dir="${generated.dir}"/>
<java classname="org.apache.qpid.gentools.Main" fork="true" dir="${gentools.home}/src" failonerror="true">
- <arg line="-j -o ${generated.dir} -t ${topDirectoryLocation}/common/templates ${xml.spec.list}" />
+ <arg line="-j -o ${generated.dir} -t ${template.dir} ${xml.spec.list}" />
<classpath>
<pathelement path="${gentools.home}/src" />
- <fileset dir="${gentools.home}/lib">
- <include name="**/*.jar"/>
- </fileset>
+ <fileset dir="${gentools.home}/lib">
+ <include name="**/*.jar"/>
+ </fileset>
+ <pathelement path="${gentools.home}/lib/velocity-1.4.jar" />
+ <pathelement path="${gentools.home}/lib/velocity-dep-1.4.jar" />
</classpath>
</java>
<touch file="${generated.timestamp}" />
@@ -47,7 +52,8 @@
<target name="check_generate_deps">
<uptodate property="generation.notRequired" targetfile="${generated.timestamp}">
<srcfiles dir="${xml.spec.dir}" includes="${xml.spec.deps}" />
- </uptodate>
+ <srcfiles dir="${template.dir}" includes="**/*.vm **/*.tmpl" />
+ </uptodate>
</target>
<target name="compile_generator">
diff --git a/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java b/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java
new file mode 100644
index 0000000000..bed80d5954
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java
@@ -0,0 +1,528 @@
+package org.apache.mina.common;
+
+import org.apache.mina.common.ByteBuffer;
+
+import java.nio.*;
+
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+public class FixedSizeByteBufferAllocator implements ByteBufferAllocator
+{
+
+
+ private static final int MINIMUM_CAPACITY = 1;
+
+ public FixedSizeByteBufferAllocator ()
+ {
+ }
+
+ public ByteBuffer allocate( int capacity, boolean direct )
+ {
+ java.nio.ByteBuffer nioBuffer;
+ if( direct )
+ {
+ nioBuffer = java.nio.ByteBuffer.allocateDirect( capacity );
+ }
+ else
+ {
+ nioBuffer = java.nio.ByteBuffer.allocate( capacity );
+ }
+ return new FixedSizeByteBuffer( nioBuffer );
+ }
+
+ public ByteBuffer wrap( java.nio.ByteBuffer nioBuffer )
+ {
+ return new FixedSizeByteBuffer( nioBuffer );
+ }
+
+ public void dispose()
+ {
+ }
+
+
+
+ private static final class FixedSizeByteBuffer extends ByteBuffer
+ {
+ private java.nio.ByteBuffer buf;
+ private int refCount = 1;
+ private int mark = -1;
+
+
+ protected FixedSizeByteBuffer( java.nio.ByteBuffer buf )
+ {
+ this.buf = buf;
+ buf.order( ByteOrder.BIG_ENDIAN );
+ refCount = 1;
+ }
+
+ public synchronized void acquire()
+ {
+ if( refCount <= 0 )
+ {
+ throw new IllegalStateException( "Already released buffer." );
+ }
+
+ refCount ++;
+ }
+
+ public void release()
+ {
+ synchronized( this )
+ {
+ if( refCount <= 0 )
+ {
+ refCount = 0;
+ throw new IllegalStateException(
+ "Already released buffer. You released the buffer too many times." );
+ }
+
+ refCount --;
+ if( refCount > 0)
+ {
+ return;
+ }
+ }
+ }
+
+ public java.nio.ByteBuffer buf()
+ {
+ return buf;
+ }
+
+ public boolean isPooled()
+ {
+ return false;
+ }
+
+ public void setPooled( boolean pooled )
+ {
+ }
+
+ public ByteBuffer duplicate() {
+ return new FixedSizeByteBuffer( this.buf.duplicate() );
+ }
+
+ public ByteBuffer slice() {
+ return new FixedSizeByteBuffer( this.buf.slice() );
+ }
+
+ public ByteBuffer asReadOnlyBuffer() {
+ return new FixedSizeByteBuffer( this.buf.asReadOnlyBuffer() );
+ }
+
+ public byte[] array()
+ {
+ return buf.array();
+ }
+
+ public int arrayOffset()
+ {
+ return buf.arrayOffset();
+ }
+
+ public boolean isDirect()
+ {
+ return buf.isDirect();
+ }
+
+ public boolean isReadOnly()
+ {
+ return buf.isReadOnly();
+ }
+
+ public int capacity()
+ {
+ return buf.capacity();
+ }
+
+ public ByteBuffer capacity( int newCapacity )
+ {
+ if( newCapacity > capacity() )
+ {
+ // Allocate a new buffer and transfer all settings to it.
+ int pos = position();
+ int limit = limit();
+ ByteOrder bo = order();
+
+ capacity0( newCapacity );
+ buf.limit( limit );
+ if( mark >= 0 )
+ {
+ buf.position( mark );
+ buf.mark();
+ }
+ buf.position( pos );
+ buf.order( bo );
+ }
+
+ return this;
+ }
+
+ protected void capacity0( int requestedCapacity )
+ {
+ int newCapacity = MINIMUM_CAPACITY;
+ while( newCapacity < requestedCapacity )
+ {
+ newCapacity <<= 1;
+ }
+
+ java.nio.ByteBuffer oldBuf = this.buf;
+ java.nio.ByteBuffer newBuf;
+ if( isDirect() )
+ {
+ newBuf = java.nio.ByteBuffer.allocateDirect( newCapacity );
+ }
+ else
+ {
+ newBuf = java.nio.ByteBuffer.allocate( newCapacity );
+ }
+
+ newBuf.clear();
+ oldBuf.clear();
+ newBuf.put( oldBuf );
+ this.buf = newBuf;
+ }
+
+
+
+ public boolean isAutoExpand()
+ {
+ return false;
+ }
+
+ public ByteBuffer setAutoExpand( boolean autoExpand )
+ {
+ if(autoExpand) throw new IllegalArgumentException();
+ else return this;
+ }
+
+ public ByteBuffer expand( int pos, int expectedRemaining )
+ {
+ int end = pos + expectedRemaining;
+ if( end > capacity() )
+ {
+ // The buffer needs expansion.
+ capacity( end );
+ }
+
+ if( end > limit() )
+ {
+ // We call limit() directly to prevent StackOverflowError
+ buf.limit( end );
+ }
+ return this;
+ }
+
+ public int position()
+ {
+ return buf.position();
+ }
+
+ public ByteBuffer position( int newPosition )
+ {
+
+ buf.position( newPosition );
+ if( mark > newPosition )
+ {
+ mark = -1;
+ }
+ return this;
+ }
+
+ public int limit()
+ {
+ return buf.limit();
+ }
+
+ public ByteBuffer limit( int newLimit )
+ {
+ buf.limit( newLimit );
+ if( mark > newLimit )
+ {
+ mark = -1;
+ }
+ return this;
+ }
+
+ public ByteBuffer mark()
+ {
+ buf.mark();
+ mark = position();
+ return this;
+ }
+
+ public int markValue()
+ {
+ return mark;
+ }
+
+ public ByteBuffer reset()
+ {
+ buf.reset();
+ return this;
+ }
+
+ public ByteBuffer clear()
+ {
+ buf.clear();
+ mark = -1;
+ return this;
+ }
+
+ public ByteBuffer flip()
+ {
+ buf.flip();
+ mark = -1;
+ return this;
+ }
+
+ public ByteBuffer rewind()
+ {
+ buf.rewind();
+ mark = -1;
+ return this;
+ }
+
+ public byte get()
+ {
+ return buf.get();
+ }
+
+ public ByteBuffer put( byte b )
+ {
+ buf.put( b );
+ return this;
+ }
+
+ public byte get( int index )
+ {
+ return buf.get( index );
+ }
+
+ public ByteBuffer put( int index, byte b )
+ {
+ buf.put( index, b );
+ return this;
+ }
+
+ public ByteBuffer get( byte[] dst, int offset, int length )
+ {
+ buf.get( dst, offset, length );
+ return this;
+ }
+
+ public ByteBuffer put( java.nio.ByteBuffer src )
+ {
+ buf.put( src );
+ return this;
+ }
+
+ public ByteBuffer put( byte[] src, int offset, int length )
+ {
+ buf.put( src, offset, length );
+ return this;
+ }
+
+ public ByteBuffer compact()
+ {
+ buf.compact();
+ mark = -1;
+ return this;
+ }
+
+ public ByteOrder order()
+ {
+ return buf.order();
+ }
+
+ public ByteBuffer order( ByteOrder bo )
+ {
+ buf.order( bo );
+ return this;
+ }
+
+ public char getChar()
+ {
+ return buf.getChar();
+ }
+
+ public ByteBuffer putChar( char value )
+ {
+ buf.putChar( value );
+ return this;
+ }
+
+ public char getChar( int index )
+ {
+ return buf.getChar( index );
+ }
+
+ public ByteBuffer putChar( int index, char value )
+ {
+ buf.putChar( index, value );
+ return this;
+ }
+
+ public CharBuffer asCharBuffer()
+ {
+ return buf.asCharBuffer();
+ }
+
+ public short getShort()
+ {
+ return buf.getShort();
+ }
+
+ public ByteBuffer putShort( short value )
+ {
+ buf.putShort( value );
+ return this;
+ }
+
+ public short getShort( int index )
+ {
+ return buf.getShort( index );
+ }
+
+ public ByteBuffer putShort( int index, short value )
+ {
+ buf.putShort( index, value );
+ return this;
+ }
+
+ public ShortBuffer asShortBuffer()
+ {
+ return buf.asShortBuffer();
+ }
+
+ public int getInt()
+ {
+ return buf.getInt();
+ }
+
+ public ByteBuffer putInt( int value )
+ {
+ buf.putInt( value );
+ return this;
+ }
+
+ public int getInt( int index )
+ {
+ return buf.getInt( index );
+ }
+
+ public ByteBuffer putInt( int index, int value )
+ {
+ buf.putInt( index, value );
+ return this;
+ }
+
+ public IntBuffer asIntBuffer()
+ {
+ return buf.asIntBuffer();
+ }
+
+ public long getLong()
+ {
+ return buf.getLong();
+ }
+
+ public ByteBuffer putLong( long value )
+ {
+ buf.putLong( value );
+ return this;
+ }
+
+ public long getLong( int index )
+ {
+ return buf.getLong( index );
+ }
+
+ public ByteBuffer putLong( int index, long value )
+ {
+ buf.putLong( index, value );
+ return this;
+ }
+
+ public LongBuffer asLongBuffer()
+ {
+ return buf.asLongBuffer();
+ }
+
+ public float getFloat()
+ {
+ return buf.getFloat();
+ }
+
+ public ByteBuffer putFloat( float value )
+ {
+ buf.putFloat( value );
+ return this;
+ }
+
+ public float getFloat( int index )
+ {
+ return buf.getFloat( index );
+ }
+
+ public ByteBuffer putFloat( int index, float value )
+ {
+ buf.putFloat( index, value );
+ return this;
+ }
+
+ public FloatBuffer asFloatBuffer()
+ {
+ return buf.asFloatBuffer();
+ }
+
+ public double getDouble()
+ {
+ return buf.getDouble();
+ }
+
+ public ByteBuffer putDouble( double value )
+ {
+ buf.putDouble( value );
+ return this;
+ }
+
+ public double getDouble( int index )
+ {
+ return buf.getDouble( index );
+ }
+
+ public ByteBuffer putDouble( int index, double value )
+ {
+ buf.putDouble( index, value );
+ return this;
+ }
+
+ public DoubleBuffer asDoubleBuffer()
+ {
+ return buf.asDoubleBuffer();
+ }
+
+
+ }
+
+
+}
diff --git a/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java b/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java
new file mode 100644
index 0000000000..c515263317
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java
@@ -0,0 +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.mina.common.support;
+
+import org.apache.mina.common.IoFuture;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.IoFutureListener;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * A default implementation of {@link org.apache.mina.common.IoFuture}.
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 440259 $, $Date: 2006-09-05 14:01:47 +0900 (í™”, 05 9ì›” 2006) $
+ */
+public class DefaultIoFuture implements IoFuture
+{
+ private final IoSession session;
+ private final Object lock;
+ private List listeners;
+ private Object result;
+ private boolean ready;
+
+
+ /**
+ * Creates a new instance.
+ *
+ * @param session an {@link IoSession} which is associated with this future
+ */
+ public DefaultIoFuture( IoSession session )
+ {
+ this.session = session;
+ this.lock = this;
+ }
+
+ /**
+ * Creates a new instance which uses the specified object as a lock.
+ */
+ public DefaultIoFuture( IoSession session, Object lock )
+ {
+ if( lock == null )
+ {
+ throw new NullPointerException( "lock" );
+ }
+ this.session = session;
+ this.lock = lock;
+ }
+
+ public IoSession getSession()
+ {
+ return session;
+ }
+
+ public Object getLock()
+ {
+ return lock;
+ }
+
+ public void join()
+ {
+ synchronized( lock )
+ {
+ while( !ready )
+ {
+ try
+ {
+ lock.wait();
+ }
+ catch( InterruptedException e )
+ {
+ }
+ }
+ }
+ }
+
+ public boolean join( long timeoutInMillis )
+ {
+ long startTime = ( timeoutInMillis <= 0 ) ? 0 : System
+ .currentTimeMillis();
+ long waitTime = timeoutInMillis;
+
+ synchronized( lock )
+ {
+ if( ready )
+ {
+ return ready;
+ }
+ else if( waitTime <= 0 )
+ {
+ return ready;
+ }
+
+ for( ;; )
+ {
+ try
+ {
+ lock.wait( waitTime );
+ }
+ catch( InterruptedException e )
+ {
+ }
+
+ if( ready )
+ return true;
+ else
+ {
+ waitTime = timeoutInMillis - ( System.currentTimeMillis() - startTime );
+ if( waitTime <= 0 )
+ {
+ return ready;
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isReady()
+ {
+ synchronized( lock )
+ {
+ return ready;
+ }
+ }
+
+ /**
+ * Sets the result of the asynchronous operation, and mark it as finished.
+ */
+ protected void setValue( Object newValue )
+ {
+ synchronized( lock )
+ {
+ // Allow only once.
+ if( ready )
+ {
+ return;
+ }
+
+ result = newValue;
+ ready = true;
+ lock.notifyAll();
+
+ notifyListeners();
+ }
+ }
+
+ /**
+ * Returns the result of the asynchronous operation.
+ */
+ protected Object getValue()
+ {
+ synchronized( lock )
+ {
+ return result;
+ }
+ }
+
+ public void addListener( IoFutureListener listener )
+ {
+ if( listener == null )
+ {
+ throw new NullPointerException( "listener" );
+ }
+
+ synchronized( lock )
+ {
+ if(listeners == null)
+ {
+ listeners = new ArrayList();
+ }
+ listeners.add( listener );
+ if( ready )
+ {
+ listener.operationComplete( this );
+ }
+ }
+ }
+
+ public void removeListener( IoFutureListener listener )
+ {
+ if( listener == null )
+ {
+ throw new NullPointerException( "listener" );
+ }
+
+ synchronized( lock )
+ {
+ listeners.remove( listener );
+ }
+ }
+
+ private void notifyListeners()
+ {
+ synchronized( lock )
+ {
+
+ if(listeners != null)
+ {
+
+ for( Iterator i = listeners.iterator(); i.hasNext(); ) {
+ ( ( IoFutureListener ) i.next() ).operationComplete( this );
+ }
+ }
+ }
+ }
+}
+
+
+
diff --git a/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java b/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java
new file mode 100644
index 0000000000..47f19aa76d
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java
@@ -0,0 +1,48 @@
+package org.apache.mina.filter;
+
+import org.apache.mina.common.IoFilter;/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+public class WriteBufferFullExeception extends RuntimeException
+{
+ private IoFilter.WriteRequest _writeRequest;
+
+ public WriteBufferFullExeception()
+ {
+ this(null);
+ }
+
+ public WriteBufferFullExeception(IoFilter.WriteRequest writeRequest)
+ {
+ _writeRequest = writeRequest;
+ }
+
+
+ public void setWriteRequest(IoFilter.WriteRequest writeRequest)
+ {
+ _writeRequest = writeRequest;
+ }
+
+ public IoFilter.WriteRequest getWriteRequest()
+ {
+ return _writeRequest;
+ }
+}
diff --git a/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java b/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java
new file mode 100644
index 0000000000..4e9db9071a
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java
@@ -0,0 +1,272 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.filter;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.DefaultIoFilterChainBuilder;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.executor.ExecutorFilter;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This filter will turn the asynchronous filterWrite method in to a blocking send when there are more than
+ * the prescribed number of messages awaiting filterWrite. It should be used in conjunction with the
+ * {@link ReadThrottleFilterBuilder} on a server as the blocking writes will allow the read thread to
+ * cause an Out of Memory exception due to a back log of unprocessed messages.
+ *
+ * This is should only be viewed as a temporary work around for DIRMINA-302.
+ *
+ * A true solution should not be implemented as a filter as this issue will always occur. On a machine
+ * where the network is slower than the local producer.
+ *
+ * Suggested improvement is to allow implementation of policices on what to do when buffer is full.
+ *
+ * They could be:
+ * Block - As this does
+ * Wait on a given Future - to drain more of the queue.. in essence this filter with high/low watermarks
+ * Throw Exception - through the client filterWrite() method to allow them to get immediate feedback on buffer state
+ *
+ * <p/>
+ * <p>Usage:
+ * <p/>
+ * <pre><code>
+ * DefaultFilterChainBuilder builder = ...
+ * WriteBufferLimitFilterBuilder filter = new WriteBufferLimitFilterBuilder();
+ * filter.attach( builder );
+ * </code></pre>
+ * <p/>
+ * or
+ * <p/>
+ * <pre><code>
+ * IoFilterChain chain = ...
+ * WriteBufferLimitFilterBuilder filter = new WriteBufferLimitFilterBuilder();
+ * filter.attach( chain );
+ * </code></pre>
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $
+ */
+public class WriteBufferLimitFilterBuilder
+{
+ public static final String PENDING_SIZE = WriteBufferLimitFilterBuilder.class.getName() + ".pendingSize";
+
+ private static int DEFAULT_CONNECTION_BUFFER_MESSAGE_COUNT = 5000;
+
+ private volatile boolean throwNotBlock = false;
+
+ private volatile int maximumConnectionBufferCount;
+ private volatile long maximumConnectionBufferSize;
+
+ private final Object _blockLock = new Object();
+
+ private int _blockWaiters = 0;
+
+
+ public WriteBufferLimitFilterBuilder()
+ {
+ this(DEFAULT_CONNECTION_BUFFER_MESSAGE_COUNT);
+ }
+
+ public WriteBufferLimitFilterBuilder(int maxWriteBufferSize)
+ {
+ setMaximumConnectionBufferCount(maxWriteBufferSize);
+ }
+
+
+ /**
+ * Set the maximum amount pending items in the writeQueue for a given session.
+ * Changing the value will only take effect when new data is received for a
+ * connection, including existing connections. Default value is 5000 msgs.
+ *
+ * @param maximumConnectionBufferCount New buffer size. Must be > 0
+ */
+ public void setMaximumConnectionBufferCount(int maximumConnectionBufferCount)
+ {
+ this.maximumConnectionBufferCount = maximumConnectionBufferCount;
+ this.maximumConnectionBufferSize = 0;
+ }
+
+ public void setMaximumConnectionBufferSize(long maximumConnectionBufferSize)
+ {
+ this.maximumConnectionBufferSize = maximumConnectionBufferSize;
+ this.maximumConnectionBufferCount = 0;
+ }
+
+ /**
+ * Attach this filter to the specified filter chain. It will search for the ThreadPoolFilter, and attach itself
+ * before and after that filter.
+ *
+ * @param chain {@link IoFilterChain} to attach self to.
+ */
+ public void attach(IoFilterChain chain)
+ {
+ String name = getThreadPoolFilterEntryName(chain.getAll());
+
+ chain.addBefore(name, getClass().getName() + ".sendlimit", new SendLimit());
+ }
+
+ /**
+ * Attach this filter to the specified builder. It will search for the
+ * {@link ExecutorFilter}, and attach itself before and after that filter.
+ *
+ * @param builder {@link DefaultIoFilterChainBuilder} to attach self to.
+ */
+ public void attach(DefaultIoFilterChainBuilder builder)
+ {
+ String name = getThreadPoolFilterEntryName(builder.getAll());
+
+ builder.addBefore(name, getClass().getName() + ".sendlimit", new SendLimit());
+ }
+
+ private String getThreadPoolFilterEntryName(List entries)
+ {
+ Iterator i = entries.iterator();
+
+ while (i.hasNext())
+ {
+ IoFilterChain.Entry entry = (IoFilterChain.Entry) i.next();
+
+ if (entry.getFilter().getClass().isAssignableFrom(ExecutorFilter.class))
+ {
+ return entry.getName();
+ }
+ }
+
+ throw new IllegalStateException("Chain does not contain a ExecutorFilter");
+ }
+
+
+ public class SendLimit extends IoFilterAdapter
+ {
+ public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception
+ {
+ try
+ {
+ waitTillSendAllowed(session);
+ }
+ catch (WriteBufferFullExeception wbfe)
+ {
+ nextFilter.exceptionCaught(session, wbfe);
+ }
+
+ if (writeRequest.getMessage() instanceof ByteBuffer)
+ {
+ increasePendingWriteSize(session, (ByteBuffer) writeRequest.getMessage());
+ }
+
+ nextFilter.filterWrite(session, writeRequest);
+ }
+
+ private void increasePendingWriteSize(IoSession session, ByteBuffer message)
+ {
+ synchronized (session)
+ {
+ Long pendingSize = getScheduledWriteBytes(session) + message.remaining();
+ session.setAttribute(PENDING_SIZE, pendingSize);
+ }
+ }
+
+ private boolean sendAllowed(IoSession session)
+ {
+ if (session.isClosing())
+ {
+ return true;
+ }
+
+ int lmswm = maximumConnectionBufferCount;
+ long lmswb = maximumConnectionBufferSize;
+
+ return (lmswm == 0 || session.getScheduledWriteRequests() < lmswm)
+ && (lmswb == 0 || getScheduledWriteBytes(session) < lmswb);
+ }
+
+ private long getScheduledWriteBytes(IoSession session)
+ {
+ synchronized (session)
+ {
+ Long i = (Long) session.getAttribute(PENDING_SIZE);
+ return null == i ? 0 : i;
+ }
+ }
+
+ private void waitTillSendAllowed(IoSession session)
+ {
+ synchronized (_blockLock)
+ {
+ if (throwNotBlock)
+ {
+ throw new WriteBufferFullExeception();
+ }
+
+ _blockWaiters++;
+
+ while (!sendAllowed(session))
+ {
+ try
+ {
+ _blockLock.wait();
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore.
+ }
+ }
+ _blockWaiters--;
+ }
+ }
+
+ public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception
+ {
+ if (message instanceof ByteBuffer)
+ {
+ decrementPendingWriteSize(session, (ByteBuffer) message);
+ }
+ notifyWaitingWriters();
+ nextFilter.messageSent(session, message);
+ }
+
+ private void decrementPendingWriteSize(IoSession session, ByteBuffer message)
+ {
+ synchronized (session)
+ {
+ session.setAttribute(PENDING_SIZE, getScheduledWriteBytes(session) - message.remaining());
+ }
+ }
+
+ private void notifyWaitingWriters()
+ {
+ synchronized (_blockLock)
+ {
+ if (_blockWaiters != 0)
+ {
+ _blockLock.notifyAll();
+ }
+ }
+
+ }
+
+ }//SentLimit
+
+
+}
diff --git a/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java b/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java
new file mode 100644
index 0000000000..3f7e206cb4
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.filter.codec;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+
+/**
+ * A {@link ProtocolDecoder} that cumulates the content of received
+ * buffers to a <em>cumulative buffer</em> to help users implement decoders.
+ * <p>
+ * If the received {@link ByteBuffer} is only a part of a message.
+ * decoders should cumulate received buffers to make a message complete or
+ * to postpone decoding until more buffers arrive.
+ * <p>
+ * Here is an example decoder that decodes CRLF terminated lines into
+ * <code>Command</code> objects:
+ * <pre>
+ * public class CRLFTerminatedCommandLineDecoder
+ * extends CumulativeProtocolDecoder {
+ *
+ * private Command parseCommand(ByteBuffer in) {
+ * // Convert the bytes in the specified buffer to a
+ * // Command object.
+ * ...
+ * }
+ *
+ * protected boolean doDecode(IoSession session, ByteBuffer in,
+ * ProtocolDecoderOutput out)
+ * throws Exception {
+ *
+ * // Remember the initial position.
+ * int start = in.position();
+ *
+ * // Now find the first CRLF in the buffer.
+ * byte previous = 0;
+ * while (in.hasRemaining()) {
+ * byte current = in.get();
+ *
+ * if (previous == '\r' && current == '\n') {
+ * // Remember the current position and limit.
+ * int position = in.position();
+ * int limit = in.limit();
+ * try {
+ * in.position(start);
+ * in.limit(position);
+ * // The bytes between in.position() and in.limit()
+ * // now contain a full CRLF terminated line.
+ * out.write(parseCommand(in.slice()));
+ * } finally {
+ * // Set the position to point right after the
+ * // detected line and set the limit to the old
+ * // one.
+ * in.position(position);
+ * in.limit(limit);
+ * }
+ * // Decoded one line; CumulativeProtocolDecoder will
+ * // call me again until I return false. So just
+ * // return true until there are no more lines in the
+ * // buffer.
+ * return true;
+ * }
+ *
+ * previous = current;
+ * }
+ *
+ * // Could not find CRLF in the buffer. Reset the initial
+ * // position to the one we recorded above.
+ * in.position(start);
+ *
+ * return false;
+ * }
+ * }
+ * </pre>
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $
+ */
+public abstract class OurCumulativeProtocolDecoder extends ProtocolDecoderAdapter {
+
+ private static final String BUFFER = OurCumulativeProtocolDecoder.class
+ .getName()
+ + ".Buffer";
+
+ /**
+ * Creates a new instance.
+ */
+ protected OurCumulativeProtocolDecoder() {
+ }
+
+ /**
+ * Cumulates content of <tt>in</tt> into internal buffer and forwards
+ * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}.
+ * <tt>doDecode()</tt> is invoked repeatedly until it returns <tt>false</tt>
+ * and the cumulative buffer is NOT compacted after decoding ends.
+ *
+ * @throws IllegalStateException if your <tt>doDecode()</tt> returned
+ * <tt>true</tt> not consuming the cumulative buffer.
+ */
+ public void decode(IoSession session, ByteBuffer in,
+ ProtocolDecoderOutput out) throws Exception {
+ boolean usingSessionBuffer = true;
+ ByteBuffer buf = (ByteBuffer) session.getAttribute(BUFFER);
+ // If we have a session buffer, append data to that; otherwise
+ // use the buffer read from the network directly.
+ if (buf != null) {
+ buf.put(in);
+ buf.flip();
+ } else {
+ buf = in;
+ usingSessionBuffer = false;
+ }
+
+ for (;;) {
+ int oldPos = buf.position();
+ boolean decoded = doDecode(session, buf, out);
+ if (decoded) {
+ if (buf.position() == oldPos) {
+ throw new IllegalStateException(
+ "doDecode() can't return true when buffer is not consumed.");
+ }
+
+ if (!buf.hasRemaining()) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+
+ // if there is any data left that cannot be decoded, we store
+ // it in a buffer in the session and next time this decoder is
+ // invoked the session buffer gets appended to
+ if (buf.hasRemaining()) {
+ storeRemainingInSession(buf, session);
+ } else {
+ if (usingSessionBuffer)
+ removeSessionBuffer(session);
+ }
+ }
+
+ /**
+ * Implement this method to consume the specified cumulative buffer and
+ * decode its content into message(s).
+ *
+ * @param in the cumulative buffer
+ * @return <tt>true</tt> if and only if there's more to decode in the buffer
+ * and you want to have <tt>doDecode</tt> method invoked again.
+ * Return <tt>false</tt> if remaining data is not enough to decode,
+ * then this method will be invoked again when more data is cumulated.
+ * @throws Exception if cannot decode <tt>in</tt>.
+ */
+ protected abstract boolean doDecode(IoSession session, ByteBuffer in,
+ ProtocolDecoderOutput out) throws Exception;
+
+ /**
+ * Releases the cumulative buffer used by the specified <tt>session</tt>.
+ * Please don't forget to call <tt>super.dispose( session )</tt> when
+ * you override this method.
+ */
+ public void dispose(IoSession session) throws Exception {
+ removeSessionBuffer(session);
+ }
+
+ private void removeSessionBuffer(IoSession session) {
+ ByteBuffer buf = (ByteBuffer) session.removeAttribute(BUFFER);
+ if (buf != null) {
+ buf.release();
+ }
+ }
+
+ private void storeRemainingInSession(ByteBuffer buf, IoSession session) {
+ ByteBuffer remainingBuf = ByteBuffer.allocate(buf.capacity());
+ remainingBuf.setAutoExpand(true);
+ remainingBuf.order(buf.order());
+ remainingBuf.put(buf);
+ session.setAttribute(BUFFER, remainingBuf);
+ }
+}
diff --git a/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java b/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java
new file mode 100644
index 0000000000..b8c6f29720
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java
@@ -0,0 +1,440 @@
+package org.apache.mina.filter.codec;
+
+
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT 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.*;
+import org.apache.mina.common.support.DefaultWriteFuture;
+import org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput;
+import org.apache.mina.util.SessionLog;
+import org.apache.mina.util.Queue;
+
+
+public class QpidProtocolCodecFilter extends IoFilterAdapter
+{
+ public static final String ENCODER = QpidProtocolCodecFilter.class.getName() + ".encoder";
+ public static final String DECODER = QpidProtocolCodecFilter.class.getName() + ".decoder";
+
+ private static final Class[] EMPTY_PARAMS = new Class[0];
+ private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap( new byte[0] );
+
+ private final ProtocolCodecFactory factory;
+
+ public QpidProtocolCodecFilter( ProtocolCodecFactory factory )
+ {
+ if( factory == null )
+ {
+ throw new NullPointerException( "factory" );
+ }
+ this.factory = factory;
+ }
+
+ public QpidProtocolCodecFilter( final ProtocolEncoder encoder, final ProtocolDecoder decoder )
+ {
+ if( encoder == null )
+ {
+ throw new NullPointerException( "encoder" );
+ }
+ if( decoder == null )
+ {
+ throw new NullPointerException( "decoder" );
+ }
+
+ this.factory = new ProtocolCodecFactory()
+ {
+ public ProtocolEncoder getEncoder()
+ {
+ return encoder;
+ }
+
+ public ProtocolDecoder getDecoder()
+ {
+ return decoder;
+ }
+ };
+ }
+
+ public QpidProtocolCodecFilter( final Class encoderClass, final Class decoderClass )
+ {
+ if( encoderClass == null )
+ {
+ throw new NullPointerException( "encoderClass" );
+ }
+ if( decoderClass == null )
+ {
+ throw new NullPointerException( "decoderClass" );
+ }
+ if( !ProtocolEncoder.class.isAssignableFrom( encoderClass ) )
+ {
+ throw new IllegalArgumentException( "encoderClass: " + encoderClass.getName() );
+ }
+ if( !ProtocolDecoder.class.isAssignableFrom( decoderClass ) )
+ {
+ throw new IllegalArgumentException( "decoderClass: " + decoderClass.getName() );
+ }
+ try
+ {
+ encoderClass.getConstructor( EMPTY_PARAMS );
+ }
+ catch( NoSuchMethodException e )
+ {
+ throw new IllegalArgumentException( "encoderClass doesn't have a public default constructor." );
+ }
+ try
+ {
+ decoderClass.getConstructor( EMPTY_PARAMS );
+ }
+ catch( NoSuchMethodException e )
+ {
+ throw new IllegalArgumentException( "decoderClass doesn't have a public default constructor." );
+ }
+
+ this.factory = new ProtocolCodecFactory()
+ {
+ public ProtocolEncoder getEncoder() throws Exception
+ {
+ return ( ProtocolEncoder ) encoderClass.newInstance();
+ }
+
+ public ProtocolDecoder getDecoder() throws Exception
+ {
+ return ( ProtocolDecoder ) decoderClass.newInstance();
+ }
+ };
+ }
+
+ public void onPreAdd( IoFilterChain parent, String name, IoFilter.NextFilter nextFilter ) throws Exception
+ {
+ if( parent.contains( ProtocolCodecFilter.class ) )
+ {
+ throw new IllegalStateException( "A filter chain cannot contain more than one QpidProtocolCodecFilter." );
+ }
+ }
+
+ public void messageReceived( IoFilter.NextFilter nextFilter, IoSession session, Object message ) throws Exception
+ {
+ if( !( message instanceof ByteBuffer ) )
+ {
+ nextFilter.messageReceived( session, message );
+ return;
+ }
+
+ ByteBuffer in = ( ByteBuffer ) message;
+ ProtocolDecoder decoder = getDecoder( session );
+ ProtocolDecoderOutput decoderOut = getDecoderOut( session, nextFilter );
+
+ try
+ {
+ decoder.decode( session, in, decoderOut );
+ }
+ catch( Throwable t )
+ {
+ ProtocolDecoderException pde;
+ if( t instanceof ProtocolDecoderException )
+ {
+ pde = ( ProtocolDecoderException ) t;
+ }
+ else
+ {
+ pde = new ProtocolDecoderException( t );
+ }
+ pde.setHexdump( in.getHexDump() );
+ throw pde;
+ }
+ finally
+ {
+ // Dispose the decoder if this session is connectionless.
+ if( session.getTransportType().isConnectionless() )
+ {
+ disposeDecoder( session );
+ }
+
+ // Release the read buffer.
+ in.release();
+
+ decoderOut.flush();
+ }
+ }
+
+ public void messageSent( IoFilter.NextFilter nextFilter, IoSession session, Object message ) throws Exception
+ {
+ if( message instanceof HiddenByteBuffer )
+ {
+ return;
+ }
+
+ if( !( message instanceof MessageByteBuffer ) )
+ {
+ nextFilter.messageSent( session, message );
+ return;
+ }
+
+ nextFilter.messageSent( session, ( ( MessageByteBuffer ) message ).message );
+ }
+
+ public void filterWrite( IoFilter.NextFilter nextFilter, IoSession session, IoFilter.WriteRequest writeRequest ) throws Exception
+ {
+ Object message = writeRequest.getMessage();
+ if( message instanceof ByteBuffer )
+ {
+ nextFilter.filterWrite( session, writeRequest );
+ return;
+ }
+
+ ProtocolEncoder encoder = getEncoder( session );
+ ProtocolEncoderOutputImpl encoderOut = getEncoderOut( session, nextFilter, writeRequest );
+
+ try
+ {
+ encoder.encode( session, message, encoderOut );
+ encoderOut.flush();
+ nextFilter.filterWrite(
+ session,
+ new IoFilter.WriteRequest(
+ new MessageByteBuffer( writeRequest.getMessage() ),
+ writeRequest.getFuture(), writeRequest.getDestination() ) );
+ }
+ catch( Throwable t )
+ {
+ ProtocolEncoderException pee;
+ if( t instanceof ProtocolEncoderException )
+ {
+ pee = ( ProtocolEncoderException ) t;
+ }
+ else
+ {
+ pee = new ProtocolEncoderException( t );
+ }
+ throw pee;
+ }
+ finally
+ {
+ // Dispose the encoder if this session is connectionless.
+ if( session.getTransportType().isConnectionless() )
+ {
+ disposeEncoder( session );
+ }
+ }
+ }
+
+ public void sessionClosed( IoFilter.NextFilter nextFilter, IoSession session ) throws Exception
+ {
+ // Call finishDecode() first when a connection is closed.
+ ProtocolDecoder decoder = getDecoder( session );
+ ProtocolDecoderOutput decoderOut = getDecoderOut( session, nextFilter );
+ try
+ {
+ decoder.finishDecode( session, decoderOut );
+ }
+ catch( Throwable t )
+ {
+ ProtocolDecoderException pde;
+ if( t instanceof ProtocolDecoderException )
+ {
+ pde = ( ProtocolDecoderException ) t;
+ }
+ else
+ {
+ pde = new ProtocolDecoderException( t );
+ }
+ throw pde;
+ }
+ finally
+ {
+ // Dispose all.
+ disposeEncoder( session );
+ disposeDecoder( session );
+
+ decoderOut.flush();
+ }
+
+ nextFilter.sessionClosed( session );
+ }
+
+ private ProtocolEncoder getEncoder( IoSession session ) throws Exception
+ {
+ ProtocolEncoder encoder = ( ProtocolEncoder ) session.getAttribute( ENCODER );
+ if( encoder == null )
+ {
+ encoder = factory.getEncoder();
+ session.setAttribute( ENCODER, encoder );
+ }
+ return encoder;
+ }
+
+ private ProtocolEncoderOutputImpl getEncoderOut( IoSession session, IoFilter.NextFilter nextFilter, IoFilter.WriteRequest writeRequest )
+ {
+ return new ProtocolEncoderOutputImpl( session, nextFilter, writeRequest );
+ }
+
+ private ProtocolDecoder getDecoder( IoSession session ) throws Exception
+ {
+ ProtocolDecoder decoder = ( ProtocolDecoder ) session.getAttribute( DECODER );
+ if( decoder == null )
+ {
+ decoder = factory.getDecoder();
+ session.setAttribute( DECODER, decoder );
+ }
+ return decoder;
+ }
+
+ private ProtocolDecoderOutput getDecoderOut( IoSession session, IoFilter.NextFilter nextFilter )
+ {
+ return new SimpleProtocolDecoderOutput( session, nextFilter );
+ }
+
+ private void disposeEncoder( IoSession session )
+ {
+ ProtocolEncoder encoder = ( ProtocolEncoder ) session.removeAttribute( ENCODER );
+ if( encoder == null )
+ {
+ return;
+ }
+
+ try
+ {
+ encoder.dispose( session );
+ }
+ catch( Throwable t )
+ {
+ SessionLog.warn(
+ session,
+ "Failed to dispose: " + encoder.getClass().getName() +
+ " (" + encoder + ')' );
+ }
+ }
+
+ private void disposeDecoder( IoSession session )
+ {
+ ProtocolDecoder decoder = ( ProtocolDecoder ) session.removeAttribute( DECODER );
+ if( decoder == null )
+ {
+ return;
+ }
+
+ try
+ {
+ decoder.dispose( session );
+ }
+ catch( Throwable t )
+ {
+ SessionLog.warn(
+ session,
+ "Falied to dispose: " + decoder.getClass().getName() +
+ " (" + decoder + ')' );
+ }
+ }
+
+ private static class HiddenByteBuffer extends ByteBufferProxy
+ {
+ private HiddenByteBuffer( ByteBuffer buf )
+ {
+ super( buf );
+ }
+ }
+
+ private static class MessageByteBuffer extends ByteBufferProxy
+ {
+ private final Object message;
+
+ private MessageByteBuffer( Object message )
+ {
+ super( EMPTY_BUFFER );
+ this.message = message;
+ }
+
+ public void acquire()
+ {
+ // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message
+ }
+
+ public void release()
+ {
+ // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message
+ }
+ }
+
+ private static class ProtocolEncoderOutputImpl implements ProtocolEncoderOutput
+ {
+ private ByteBuffer buffer;
+
+ private final IoSession session;
+ private final IoFilter.NextFilter nextFilter;
+ private final IoFilter.WriteRequest writeRequest;
+
+ public ProtocolEncoderOutputImpl( IoSession session, IoFilter.NextFilter nextFilter, IoFilter.WriteRequest writeRequest )
+ {
+ this.session = session;
+ this.nextFilter = nextFilter;
+ this.writeRequest = writeRequest;
+ }
+
+
+
+ public void write( ByteBuffer buf )
+ {
+ if(buffer != null)
+ {
+ flush();
+ }
+ buffer = buf;
+ }
+
+ public void mergeAll()
+ {
+ }
+
+ public WriteFuture flush()
+ {
+ WriteFuture future = null;
+ if( buffer == null )
+ {
+ return null;
+ }
+ else
+ {
+ ByteBuffer buf = buffer;
+ // Flush only when the buffer has remaining.
+ if( buf.hasRemaining() )
+ {
+ future = doFlush( buf );
+ }
+
+ }
+
+ return future;
+ }
+
+
+ protected WriteFuture doFlush( ByteBuffer buf )
+ {
+ WriteFuture future = new DefaultWriteFuture( session );
+ nextFilter.filterWrite(
+ session,
+ new IoFilter.WriteRequest(
+ buf,
+ future, writeRequest.getDestination() ) );
+ return future;
+ }
+ }
+}
+
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java
new file mode 100644
index 0000000000..e5360d32e0
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java
@@ -0,0 +1,547 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.socket.nio;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.mina.common.ExceptionMonitor;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.support.BaseIoAcceptor;
+import org.apache.mina.util.Queue;
+import org.apache.mina.util.NewThreadExecutor;
+import org.apache.mina.util.NamePreservingRunnable;
+import edu.emory.mathcs.backport.java.util.concurrent.Executor;
+
+/**
+ * {@link IoAcceptor} for socket transport (TCP/IP).
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $
+ */
+public class MultiThreadSocketAcceptor extends SocketAcceptor
+{
+ /**
+ * @noinspection StaticNonFinalField
+ */
+ private static volatile int nextId = 0;
+
+ private final Executor executor;
+ private final Object lock = new Object();
+ private final int id = nextId ++;
+ private final String threadName = "SocketAcceptor-" + id;
+ private final Map channels = new HashMap();
+
+ private final Queue registerQueue = new Queue();
+ private final Queue cancelQueue = new Queue();
+
+ private final MultiThreadSocketIoProcessor[] ioProcessors;
+ private final int processorCount;
+
+ /**
+ * @noinspection FieldAccessedSynchronizedAndUnsynchronized
+ */
+ private Selector selector;
+ private Worker worker;
+ private int processorDistributor = 0;
+
+ /**
+ * Create an acceptor with a single processing thread using a NewThreadExecutor
+ */
+ public MultiThreadSocketAcceptor()
+ {
+ this( 1, new NewThreadExecutor() );
+ }
+
+ /**
+ * Create an acceptor with the desired number of processing threads
+ *
+ * @param processorCount Number of processing threads
+ * @param executor Executor to use for launching threads
+ */
+ public MultiThreadSocketAcceptor( int processorCount, Executor executor )
+ {
+ if( processorCount < 1 )
+ {
+ throw new IllegalArgumentException( "Must have at least one processor" );
+ }
+
+ this.executor = executor;
+ this.processorCount = processorCount;
+ ioProcessors = new MultiThreadSocketIoProcessor[processorCount];
+
+ for( int i = 0; i < processorCount; i++ )
+ {
+ ioProcessors[i] = new MultiThreadSocketIoProcessor( "SocketAcceptorIoProcessor-" + id + "." + i, executor );
+ }
+ }
+
+
+ /**
+ * Binds to the specified <code>address</code> and handles incoming connections with the specified
+ * <code>handler</code>. Backlog value is configured to the value of <code>backlog</code> property.
+ *
+ * @throws IOException if failed to bind
+ */
+ public void bind( SocketAddress address, IoHandler handler, IoServiceConfig config ) throws IOException
+ {
+ if( handler == null )
+ {
+ throw new NullPointerException( "handler" );
+ }
+
+ if( address != null && !( address instanceof InetSocketAddress ) )
+ {
+ throw new IllegalArgumentException( "Unexpected address type: " + address.getClass() );
+ }
+
+ if( config == null )
+ {
+ config = getDefaultConfig();
+ }
+
+ RegistrationRequest request = new RegistrationRequest( address, handler, config );
+
+ synchronized( registerQueue )
+ {
+ registerQueue.push( request );
+ }
+
+ startupWorker();
+
+ selector.wakeup();
+
+ synchronized( request )
+ {
+ while( !request.done )
+ {
+ try
+ {
+ request.wait();
+ }
+ catch( InterruptedException e )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( e );
+ }
+ }
+ }
+
+ if( request.exception != null )
+ {
+ throw request.exception;
+ }
+ }
+
+
+ private synchronized void startupWorker() throws IOException
+ {
+ synchronized( lock )
+ {
+ if( worker == null )
+ {
+ selector = Selector.open();
+ worker = new Worker();
+
+ executor.execute( new NamePreservingRunnable( worker ) );
+ }
+ }
+ }
+
+ public void unbind( SocketAddress address )
+ {
+ if( address == null )
+ {
+ throw new NullPointerException( "address" );
+ }
+
+ CancellationRequest request = new CancellationRequest( address );
+
+ try
+ {
+ startupWorker();
+ }
+ catch( IOException e )
+ {
+ // IOException is thrown only when Worker thread is not
+ // running and failed to open a selector. We simply throw
+ // IllegalArgumentException here because we can simply
+ // conclude that nothing is bound to the selector.
+ throw new IllegalArgumentException( "Address not bound: " + address );
+ }
+
+ synchronized( cancelQueue )
+ {
+ cancelQueue.push( request );
+ }
+
+ selector.wakeup();
+
+ synchronized( request )
+ {
+ while( !request.done )
+ {
+ try
+ {
+ request.wait();
+ }
+ catch( InterruptedException e )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( e );
+ }
+ }
+ }
+
+ if( request.exception != null )
+ {
+ request.exception.fillInStackTrace();
+
+ throw request.exception;
+ }
+ }
+
+
+ private class Worker implements Runnable
+ {
+ public void run()
+ {
+ Thread.currentThread().setName(MultiThreadSocketAcceptor.this.threadName );
+
+ for( ; ; )
+ {
+ try
+ {
+ int nKeys = selector.select();
+
+ registerNew();
+
+ if( nKeys > 0 )
+ {
+ processSessions( selector.selectedKeys() );
+ }
+
+ cancelKeys();
+
+ if( selector.keys().isEmpty() )
+ {
+ synchronized( lock )
+ {
+ if( selector.keys().isEmpty() &&
+ registerQueue.isEmpty() &&
+ cancelQueue.isEmpty() )
+ {
+ worker = null;
+ try
+ {
+ selector.close();
+ }
+ catch( IOException e )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( e );
+ }
+ finally
+ {
+ selector = null;
+ }
+ break;
+ }
+ }
+ }
+ }
+ catch( IOException e )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( e );
+
+ try
+ {
+ Thread.sleep( 1000 );
+ }
+ catch( InterruptedException e1 )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( e1 );
+ }
+ }
+ }
+ }
+
+ private void processSessions( Set keys ) throws IOException
+ {
+ Iterator it = keys.iterator();
+ while( it.hasNext() )
+ {
+ SelectionKey key = ( SelectionKey ) it.next();
+
+ it.remove();
+
+ if( !key.isAcceptable() )
+ {
+ continue;
+ }
+
+ ServerSocketChannel ssc = ( ServerSocketChannel ) key.channel();
+
+ SocketChannel ch = ssc.accept();
+
+ if( ch == null )
+ {
+ continue;
+ }
+
+ boolean success = false;
+ try
+ {
+
+ RegistrationRequest req = ( RegistrationRequest ) key.attachment();
+
+ MultiThreadSocketSessionImpl session = new MultiThreadSocketSessionImpl(
+ MultiThreadSocketAcceptor.this, nextProcessor(), getListeners(),
+ req.config, ch, req.handler, req.address );
+
+ // New Interface
+// SocketSessionImpl session = new SocketSessionImpl(
+// SocketAcceptor.this, nextProcessor(), getListeners(),
+// req.config, ch, req.handler, req.address );
+
+
+ getFilterChainBuilder().buildFilterChain( session.getFilterChain() );
+ req.config.getFilterChainBuilder().buildFilterChain( session.getFilterChain() );
+ req.config.getThreadModel().buildFilterChain( session.getFilterChain() );
+ session.getIoProcessor().addNew( session );
+ success = true;
+ }
+ catch( Throwable t )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( t );
+ }
+ finally
+ {
+ if( !success )
+ {
+ ch.close();
+ }
+ }
+ }
+ }
+ }
+
+ private MultiThreadSocketIoProcessor nextProcessor()
+ {
+ return ioProcessors[processorDistributor++ % processorCount];
+ }
+
+
+ private void registerNew()
+ {
+ if( registerQueue.isEmpty() )
+ {
+ return;
+ }
+
+ for( ; ; )
+ {
+ RegistrationRequest req;
+
+ synchronized( registerQueue )
+ {
+ req = ( RegistrationRequest ) registerQueue.pop();
+ }
+
+ if( req == null )
+ {
+ break;
+ }
+
+ ServerSocketChannel ssc = null;
+
+ try
+ {
+ ssc = ServerSocketChannel.open();
+ ssc.configureBlocking( false );
+
+ // Configure the server socket,
+ SocketAcceptorConfig cfg;
+ if( req.config instanceof SocketAcceptorConfig )
+ {
+ cfg = ( SocketAcceptorConfig ) req.config;
+ }
+ else
+ {
+ cfg = ( SocketAcceptorConfig ) getDefaultConfig();
+ }
+
+ ssc.socket().setReuseAddress( cfg.isReuseAddress() );
+ ssc.socket().setReceiveBufferSize(
+ ( ( SocketSessionConfig ) cfg.getSessionConfig() ).getReceiveBufferSize() );
+
+ // and bind.
+ ssc.socket().bind( req.address, cfg.getBacklog() );
+ if( req.address == null || req.address.getPort() == 0 )
+ {
+ req.address = ( InetSocketAddress ) ssc.socket().getLocalSocketAddress();
+ }
+ ssc.register( selector, SelectionKey.OP_ACCEPT, req );
+
+ synchronized( channels )
+ {
+ channels.put( req.address, ssc );
+ }
+
+ getListeners().fireServiceActivated(
+ this, req.address, req.handler, req.config );
+ }
+ catch( IOException e )
+ {
+ req.exception = e;
+ }
+ finally
+ {
+ synchronized( req )
+ {
+ req.done = true;
+
+ req.notifyAll();
+ }
+
+ if( ssc != null && req.exception != null )
+ {
+ try
+ {
+ ssc.close();
+ }
+ catch( IOException e )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( e );
+ }
+ }
+ }
+ }
+ }
+
+
+ private void cancelKeys()
+ {
+ if( cancelQueue.isEmpty() )
+ {
+ return;
+ }
+
+ for( ; ; )
+ {
+ CancellationRequest request;
+
+ synchronized( cancelQueue )
+ {
+ request = ( CancellationRequest ) cancelQueue.pop();
+ }
+
+ if( request == null )
+ {
+ break;
+ }
+
+ ServerSocketChannel ssc;
+ synchronized( channels )
+ {
+ ssc = ( ServerSocketChannel ) channels.remove( request.address );
+ }
+
+ // close the channel
+ try
+ {
+ if( ssc == null )
+ {
+ request.exception = new IllegalArgumentException( "Address not bound: " + request.address );
+ }
+ else
+ {
+ SelectionKey key = ssc.keyFor( selector );
+ request.registrationRequest = ( RegistrationRequest ) key.attachment();
+ key.cancel();
+
+ selector.wakeup(); // wake up again to trigger thread death
+
+ ssc.close();
+ }
+ }
+ catch( IOException e )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( e );
+ }
+ finally
+ {
+ synchronized( request )
+ {
+ request.done = true;
+ request.notifyAll();
+ }
+
+ if( request.exception == null )
+ {
+ getListeners().fireServiceDeactivated(
+ this, request.address,
+ request.registrationRequest.handler,
+ request.registrationRequest.config );
+ }
+ }
+ }
+ }
+
+ private static class RegistrationRequest
+ {
+ private InetSocketAddress address;
+ private final IoHandler handler;
+ private final IoServiceConfig config;
+ private IOException exception;
+ private boolean done;
+
+ private RegistrationRequest( SocketAddress address, IoHandler handler, IoServiceConfig config )
+ {
+ this.address = ( InetSocketAddress ) address;
+ this.handler = handler;
+ this.config = config;
+ }
+ }
+
+
+ private static class CancellationRequest
+ {
+ private final SocketAddress address;
+ private boolean done;
+ private RegistrationRequest registrationRequest;
+ private RuntimeException exception;
+
+ private CancellationRequest( SocketAddress address )
+ {
+ this.address = address;
+ }
+ }
+}
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java
new file mode 100644
index 0000000000..b1612840db
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java
@@ -0,0 +1,486 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.socket.nio;
+
+import edu.emory.mathcs.backport.java.util.concurrent.Executor;
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.ExceptionMonitor;
+import org.apache.mina.common.IoConnector;
+import org.apache.mina.common.IoConnectorConfig;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.support.AbstractIoFilterChain;
+import org.apache.mina.common.support.DefaultConnectFuture;
+import org.apache.mina.util.NamePreservingRunnable;
+import org.apache.mina.util.NewThreadExecutor;
+import org.apache.mina.util.Queue;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * {@link IoConnector} for socket transport (TCP/IP).
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $
+ */
+public class MultiThreadSocketConnector extends SocketConnector
+{
+ /** @noinspection StaticNonFinalField */
+ private static volatile int nextId = 0;
+
+ private final Object lock = new Object();
+ private final int id = nextId++;
+ private final String threadName = "SocketConnector-" + id;
+ private SocketConnectorConfig defaultConfig = new SocketConnectorConfig();
+ private final Queue connectQueue = new Queue();
+ private final MultiThreadSocketIoProcessor[] ioProcessors;
+ private final int processorCount;
+ private final Executor executor;
+
+ /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */
+ private Selector selector;
+ private Worker worker;
+ private int processorDistributor = 0;
+ private int workerTimeout = 60; // 1 min.
+
+ /** Create a connector with a single processing thread using a NewThreadExecutor */
+ public MultiThreadSocketConnector()
+ {
+ this(1, new NewThreadExecutor());
+ }
+
+ /**
+ * Create a connector with the desired number of processing threads
+ *
+ * @param processorCount Number of processing threads
+ * @param executor Executor to use for launching threads
+ */
+ public MultiThreadSocketConnector(int processorCount, Executor executor)
+ {
+ if (processorCount < 1)
+ {
+ throw new IllegalArgumentException("Must have at least one processor");
+ }
+
+ this.executor = executor;
+ this.processorCount = processorCount;
+ ioProcessors = new MultiThreadSocketIoProcessor[processorCount];
+
+ for (int i = 0; i < processorCount; i++)
+ {
+ ioProcessors[i] = new MultiThreadSocketIoProcessor("SocketConnectorIoProcessor-" + id + "." + i, executor);
+ }
+ }
+
+ /**
+ * How many seconds to keep the connection thread alive between connection requests
+ *
+ * @return Number of seconds to keep connection thread alive
+ */
+ public int getWorkerTimeout()
+ {
+ return workerTimeout;
+ }
+
+ /**
+ * Set how many seconds the connection worker thread should remain alive once idle before terminating itself.
+ *
+ * @param workerTimeout Number of seconds to keep thread alive. Must be >=0
+ */
+ public void setWorkerTimeout(int workerTimeout)
+ {
+ if (workerTimeout < 0)
+ {
+ throw new IllegalArgumentException("Must be >= 0");
+ }
+ this.workerTimeout = workerTimeout;
+ }
+
+ public ConnectFuture connect(SocketAddress address, IoHandler handler, IoServiceConfig config)
+ {
+ return connect(address, null, handler, config);
+ }
+
+ public ConnectFuture connect(SocketAddress address, SocketAddress localAddress,
+ IoHandler handler, IoServiceConfig config)
+ {
+ if (address == null)
+ {
+ throw new NullPointerException("address");
+ }
+ if (handler == null)
+ {
+ throw new NullPointerException("handler");
+ }
+
+ if (!(address instanceof InetSocketAddress))
+ {
+ throw new IllegalArgumentException("Unexpected address type: "
+ + address.getClass());
+ }
+
+ if (localAddress != null && !(localAddress instanceof InetSocketAddress))
+ {
+ throw new IllegalArgumentException("Unexpected local address type: "
+ + localAddress.getClass());
+ }
+
+ if (config == null)
+ {
+ config = getDefaultConfig();
+ }
+
+ SocketChannel ch = null;
+ boolean success = false;
+ try
+ {
+ ch = SocketChannel.open();
+ ch.socket().setReuseAddress(true);
+ if (localAddress != null)
+ {
+ ch.socket().bind(localAddress);
+ }
+
+ ch.configureBlocking(false);
+
+ if (ch.connect(address))
+ {
+ DefaultConnectFuture future = new DefaultConnectFuture();
+ newSession(ch, handler, config, future);
+ success = true;
+ return future;
+ }
+
+ success = true;
+ }
+ catch (IOException e)
+ {
+ return DefaultConnectFuture.newFailedFuture(e);
+ }
+ finally
+ {
+ if (!success && ch != null)
+ {
+ try
+ {
+ ch.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ }
+ }
+
+ ConnectionRequest request = new ConnectionRequest(ch, handler, config);
+ synchronized (lock)
+ {
+ try
+ {
+ startupWorker();
+ }
+ catch (IOException e)
+ {
+ try
+ {
+ ch.close();
+ }
+ catch (IOException e2)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e2);
+ }
+
+ return DefaultConnectFuture.newFailedFuture(e);
+ }
+ }
+
+ synchronized (connectQueue)
+ {
+ connectQueue.push(request);
+ }
+ selector.wakeup();
+
+ return request;
+ }
+
+ private synchronized void startupWorker() throws IOException
+ {
+ if (worker == null)
+ {
+ selector = Selector.open();
+ worker = new Worker();
+ executor.execute(new NamePreservingRunnable(worker));
+ }
+ }
+
+ private void registerNew()
+ {
+ if (connectQueue.isEmpty())
+ {
+ return;
+ }
+
+ for (; ;)
+ {
+ ConnectionRequest req;
+ synchronized (connectQueue)
+ {
+ req = (ConnectionRequest) connectQueue.pop();
+ }
+
+ if (req == null)
+ {
+ break;
+ }
+
+ SocketChannel ch = req.channel;
+ try
+ {
+ ch.register(selector, SelectionKey.OP_CONNECT, req);
+ }
+ catch (IOException e)
+ {
+ req.setException(e);
+ }
+ }
+ }
+
+ private void processSessions(Set keys)
+ {
+ Iterator it = keys.iterator();
+
+ while (it.hasNext())
+ {
+ SelectionKey key = (SelectionKey) it.next();
+
+ if (!key.isConnectable())
+ {
+ continue;
+ }
+
+ SocketChannel ch = (SocketChannel) key.channel();
+ ConnectionRequest entry = (ConnectionRequest) key.attachment();
+
+ boolean success = false;
+ try
+ {
+ ch.finishConnect();
+ newSession(ch, entry.handler, entry.config, entry);
+ success = true;
+ }
+ catch (Throwable e)
+ {
+ entry.setException(e);
+ }
+ finally
+ {
+ key.cancel();
+ if (!success)
+ {
+ try
+ {
+ ch.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ }
+ }
+ }
+
+ keys.clear();
+ }
+
+ private void processTimedOutSessions(Set keys)
+ {
+ long currentTime = System.currentTimeMillis();
+ Iterator it = keys.iterator();
+
+ while (it.hasNext())
+ {
+ SelectionKey key = (SelectionKey) it.next();
+
+ if (!key.isValid())
+ {
+ continue;
+ }
+
+ ConnectionRequest entry = (ConnectionRequest) key.attachment();
+
+ if (currentTime >= entry.deadline)
+ {
+ entry.setException(new ConnectException());
+ try
+ {
+ key.channel().close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ finally
+ {
+ key.cancel();
+ }
+ }
+ }
+ }
+
+ private void newSession(SocketChannel ch, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture)
+ throws IOException
+ {
+ MultiThreadSocketSessionImpl session =
+ new MultiThreadSocketSessionImpl(this, nextProcessor(), getListeners(),
+ config, ch, handler, ch.socket().getRemoteSocketAddress());
+
+ //new interface
+// SocketSessionImpl session = new SocketSessionImpl(
+// this, nextProcessor(), getListeners(),
+// config, ch, handler, ch.socket().getRemoteSocketAddress() );
+ try
+ {
+ getFilterChainBuilder().buildFilterChain(session.getFilterChain());
+ config.getFilterChainBuilder().buildFilterChain(session.getFilterChain());
+ config.getThreadModel().buildFilterChain(session.getFilterChain());
+ }
+ catch (Throwable e)
+ {
+ throw (IOException) new IOException("Failed to create a session.").initCause(e);
+ }
+
+ // Set the ConnectFuture of the specified session, which will be
+ // removed and notified by AbstractIoFilterChain eventually.
+ session.setAttribute( AbstractIoFilterChain.CONNECT_FUTURE, connectFuture );
+
+ // Forward the remaining process to the SocketIoProcessor.
+ session.getIoProcessor().addNew(session);
+ }
+
+ private MultiThreadSocketIoProcessor nextProcessor()
+ {
+ return ioProcessors[processorDistributor++ % processorCount];
+ }
+
+ private class Worker implements Runnable
+ {
+ private long lastActive = System.currentTimeMillis();
+
+ public void run()
+ {
+ Thread.currentThread().setName(MultiThreadSocketConnector.this.threadName);
+
+ for (; ;)
+ {
+ try
+ {
+ int nKeys = selector.select(1000);
+
+ registerNew();
+
+ if (nKeys > 0)
+ {
+ processSessions(selector.selectedKeys());
+ }
+
+ processTimedOutSessions(selector.keys());
+
+ if (selector.keys().isEmpty())
+ {
+ if (System.currentTimeMillis() - lastActive > workerTimeout * 1000L)
+ {
+ synchronized (lock)
+ {
+ if (selector.keys().isEmpty() &&
+ connectQueue.isEmpty())
+ {
+ worker = null;
+ try
+ {
+ selector.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ finally
+ {
+ selector = null;
+ }
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ lastActive = System.currentTimeMillis();
+ }
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e1)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e1);
+ }
+ }
+ }
+ }
+ }
+
+ private class ConnectionRequest extends DefaultConnectFuture
+ {
+ private final SocketChannel channel;
+ private final long deadline;
+ private final IoHandler handler;
+ private final IoServiceConfig config;
+
+ private ConnectionRequest(SocketChannel channel, IoHandler handler, IoServiceConfig config)
+ {
+ this.channel = channel;
+ long timeout;
+ if (config instanceof IoConnectorConfig)
+ {
+ timeout = ((IoConnectorConfig) config).getConnectTimeoutMillis();
+ }
+ else
+ {
+ timeout = ((IoConnectorConfig) getDefaultConfig()).getConnectTimeoutMillis();
+ }
+ this.deadline = System.currentTimeMillis() + timeout;
+ this.handler = handler;
+ this.config = config;
+ }
+ }
+}
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java
new file mode 100644
index 0000000000..67b8c8d820
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.socket.nio;
+
+import java.io.IOException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.IoFilter.WriteRequest;
+import org.apache.mina.common.support.AbstractIoFilterChain;
+import org.apache.mina.util.Queue;
+
+/**
+ * An {@link IoFilterChain} for socket transport (TCP/IP).
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ */
+class MultiThreadSocketFilterChain extends AbstractIoFilterChain {
+
+ MultiThreadSocketFilterChain( IoSession parent )
+ {
+ super( parent );
+ }
+
+ protected void doWrite( IoSession session, WriteRequest writeRequest )
+ {
+ MultiThreadSocketSessionImpl s = (MultiThreadSocketSessionImpl) session;
+ Queue writeRequestQueue = s.getWriteRequestQueue();
+
+ // SocketIoProcessor.doFlush() will reset it after write is finished
+ // because the buffer will be passed with messageSent event.
+ ( ( ByteBuffer ) writeRequest.getMessage() ).mark();
+ synchronized( writeRequestQueue )
+ {
+ writeRequestQueue.push( writeRequest );
+ if( writeRequestQueue.size() == 1 && session.getTrafficMask().isWritable() )
+ {
+ // Notify SocketIoProcessor only when writeRequestQueue was empty.
+ s.getIoProcessor().flush( s );
+ }
+ }
+ }
+
+ protected void doClose( IoSession session ) throws IOException
+ {
+ MultiThreadSocketSessionImpl s = (MultiThreadSocketSessionImpl) session;
+ s.getIoProcessor().remove( s );
+ }
+}
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java
new file mode 100644
index 0000000000..c23ad8686f
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java
@@ -0,0 +1,1026 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.socket.nio;
+
+import edu.emory.mathcs.backport.java.util.concurrent.Executor;
+import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.ExceptionMonitor;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoFilter.WriteRequest;
+import org.apache.mina.common.WriteTimeoutException;
+import org.apache.mina.util.IdentityHashSet;
+import org.apache.mina.util.NamePreservingRunnable;
+import org.apache.mina.util.Queue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Performs all I/O operations for sockets which is connected or bound. This class is used by MINA internally.
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $,
+ */
+class MultiThreadSocketIoProcessor extends SocketIoProcessor
+{
+ Logger _logger = LoggerFactory.getLogger(MultiThreadSocketIoProcessor.class);
+ Logger _loggerRead = LoggerFactory.getLogger(MultiThreadSocketIoProcessor.class + ".Reader");
+ Logger _loggerWrite = LoggerFactory.getLogger(MultiThreadSocketIoProcessor.class + ".Writer");
+
+ private static final long SELECTOR_TIMEOUT = 1000L;
+
+ private int MAX_READ_BYTES_PER_SESSION = 524288; //512K
+ private int MAX_FLUSH_BYTES_PER_SESSION = 524288; //512K
+
+ private final Object readLock = new Object();
+ private final Object writeLock = new Object();
+
+ private final String threadName;
+ private final Executor executor;
+
+ private ReentrantLock trafficMaskUpdateLock = new ReentrantLock();
+
+ /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */
+ private volatile Selector selector, writeSelector;
+
+ private final Queue newSessions = new Queue();
+ private final Queue removingSessions = new Queue();
+ private final BlockingQueue flushingSessions = new LinkedBlockingQueue();
+ private final IdentityHashSet flushingSessionsSet = new IdentityHashSet();
+
+ private final Queue trafficControllingSessions = new Queue();
+
+ private ReadWorker readWorker;
+ private WriteWorker writeWorker;
+ private long lastIdleReadCheckTime = System.currentTimeMillis();
+ private long lastIdleWriteCheckTime = System.currentTimeMillis();
+
+ MultiThreadSocketIoProcessor(String threadName, Executor executor)
+ {
+ super(threadName, executor);
+ this.threadName = threadName;
+ this.executor = executor;
+ }
+
+ void addNew(SocketSessionImpl session) throws IOException
+ {
+ synchronized (newSessions)
+ {
+ newSessions.push(session);
+ }
+
+ startupWorker();
+
+ selector.wakeup();
+ writeSelector.wakeup();
+ }
+
+ void remove(SocketSessionImpl session) throws IOException
+ {
+ scheduleRemove(session);
+ startupWorker();
+ selector.wakeup();
+ }
+
+ private void startupWorker() throws IOException
+ {
+ synchronized (readLock)
+ {
+ if (readWorker == null)
+ {
+ selector = Selector.open();
+ readWorker = new ReadWorker();
+ executor.execute(new NamePreservingRunnable(readWorker));
+ }
+ }
+
+ synchronized (writeLock)
+ {
+ if (writeWorker == null)
+ {
+ writeSelector = Selector.open();
+ writeWorker = new WriteWorker();
+ executor.execute(new NamePreservingRunnable(writeWorker));
+ }
+ }
+
+ }
+
+ void flush(SocketSessionImpl session)
+ {
+ scheduleFlush(session);
+ Selector selector = this.writeSelector;
+
+ if (selector != null)
+ {
+ selector.wakeup();
+ }
+ }
+
+ void updateTrafficMask(SocketSessionImpl session)
+ {
+ scheduleTrafficControl(session);
+ Selector selector = this.selector;
+ if (selector != null)
+ {
+ selector.wakeup();
+ }
+ }
+
+ private void scheduleRemove(SocketSessionImpl session)
+ {
+ synchronized (removingSessions)
+ {
+ removingSessions.push(session);
+ }
+ }
+
+ private void scheduleFlush(SocketSessionImpl session)
+ {
+ synchronized (flushingSessionsSet)
+ {
+ //if flushingSessions grows to contain Integer.MAX_VALUE sessions
+ // then this will fail.
+ if (flushingSessionsSet.add(session))
+ {
+ flushingSessions.offer(session);
+ }
+ }
+ }
+
+ private void scheduleTrafficControl(SocketSessionImpl session)
+ {
+ synchronized (trafficControllingSessions)
+ {
+ trafficControllingSessions.push(session);
+ }
+ }
+
+ private void doAddNewReader() throws InterruptedException
+ {
+ if (newSessions.isEmpty())
+ {
+ return;
+ }
+
+ for (; ;)
+ {
+ MultiThreadSocketSessionImpl session;
+
+ synchronized (newSessions)
+ {
+ session = (MultiThreadSocketSessionImpl) newSessions.peek();
+ }
+
+ if (session == null)
+ {
+ break;
+ }
+
+ SocketChannel ch = session.getChannel();
+
+
+ try
+ {
+
+ ch.configureBlocking(false);
+ session.setSelectionKey(ch.register(selector,
+ SelectionKey.OP_READ,
+ session));
+
+ //System.out.println("ReadDebug:"+"Awaiting Registration");
+ session.awaitRegistration();
+ sessionCreated(session);
+ }
+ catch (IOException e)
+ {
+ // Clear the AbstractIoFilterChain.CONNECT_FUTURE attribute
+ // and call ConnectFuture.setException().
+ session.getFilterChain().fireExceptionCaught(session, e);
+ }
+ }
+ }
+
+
+ private void doAddNewWrite() throws InterruptedException
+ {
+ if (newSessions.isEmpty())
+ {
+ return;
+ }
+
+ for (; ;)
+ {
+ MultiThreadSocketSessionImpl session;
+
+ synchronized (newSessions)
+ {
+ session = (MultiThreadSocketSessionImpl) newSessions.peek();
+ }
+
+ if (session == null)
+ {
+ break;
+ }
+
+ SocketChannel ch = session.getChannel();
+
+ try
+ {
+ ch.configureBlocking(false);
+ synchronized (flushingSessionsSet)
+ {
+ flushingSessionsSet.add(session);
+ }
+
+ session.setWriteSelectionKey(ch.register(writeSelector,
+ SelectionKey.OP_WRITE,
+ session));
+
+ //System.out.println("WriteDebug:"+"Awaiting Registration");
+ session.awaitRegistration();
+ sessionCreated(session);
+ }
+ catch (IOException e)
+ {
+
+ // Clear the AbstractIoFilterChain.CONNECT_FUTURE attribute
+ // and call ConnectFuture.setException().
+ session.getFilterChain().fireExceptionCaught(session, e);
+ }
+ }
+ }
+
+
+ private void sessionCreated(SocketSessionImpl sessionParam) throws InterruptedException
+ {
+ MultiThreadSocketSessionImpl session = (MultiThreadSocketSessionImpl) sessionParam;
+ synchronized (newSessions)
+ {
+ if (!session.created())
+ {
+ _logger.debug("Popping new session");
+ newSessions.pop();
+
+ // AbstractIoFilterChain.CONNECT_FUTURE is cleared inside here
+ // in AbstractIoFilterChain.fireSessionOpened().
+ session.getServiceListeners().fireSessionCreated(session);
+
+ session.doneCreation();
+ }
+ }
+ }
+
+ private void doRemove()
+ {
+ if (removingSessions.isEmpty())
+ {
+ return;
+ }
+
+ for (; ;)
+ {
+ MultiThreadSocketSessionImpl session;
+
+ synchronized (removingSessions)
+ {
+ session = (MultiThreadSocketSessionImpl) removingSessions.pop();
+ }
+
+ if (session == null)
+ {
+ break;
+ }
+
+ SocketChannel ch = session.getChannel();
+ SelectionKey key = session.getReadSelectionKey();
+ SelectionKey writeKey = session.getWriteSelectionKey();
+
+ // Retry later if session is not yet fully initialized.
+ // (In case that Session.close() is called before addSession() is processed)
+ if (key == null || writeKey == null)
+ {
+ scheduleRemove(session);
+ break;
+ }
+ // skip if channel is already closed
+ if (!key.isValid() || !writeKey.isValid())
+ {
+ continue;
+ }
+
+ try
+ {
+ //System.out.println("ReadDebug:"+"Removing Session: " + System.identityHashCode(session));
+ synchronized (readLock)
+ {
+ key.cancel();
+ }
+ synchronized (writeLock)
+ {
+ writeKey.cancel();
+ }
+ ch.close();
+ }
+ catch (IOException e)
+ {
+ session.getFilterChain().fireExceptionCaught(session, e);
+ }
+ finally
+ {
+ releaseWriteBuffers(session);
+ session.getServiceListeners().fireSessionDestroyed(session);
+ }
+ }
+ }
+
+ private void processRead(Set selectedKeys)
+ {
+ Iterator it = selectedKeys.iterator();
+
+ while (it.hasNext())
+ {
+ SelectionKey key = (SelectionKey) it.next();
+ MultiThreadSocketSessionImpl session = (MultiThreadSocketSessionImpl) key.attachment();
+
+ synchronized (readLock)
+ {
+ if (key.isValid() && key.isReadable() && session.getTrafficMask().isReadable())
+ {
+ read(session);
+ }
+ }
+
+ }
+
+ selectedKeys.clear();
+ }
+
+ private void processWrite(Set selectedKeys)
+ {
+ Iterator it = selectedKeys.iterator();
+
+ while (it.hasNext())
+ {
+ SelectionKey key = (SelectionKey) it.next();
+ SocketSessionImpl session = (SocketSessionImpl) key.attachment();
+
+ synchronized (writeLock)
+ {
+ if (key.isValid() && key.isWritable() && session.getTrafficMask().isWritable())
+ {
+
+ // Clear OP_WRITE
+ key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
+
+ synchronized (flushingSessionsSet)
+ {
+ flushingSessions.offer(session);
+ }
+ }
+ }
+ }
+
+ selectedKeys.clear();
+ }
+
+ private void read(SocketSessionImpl session)
+ {
+
+ //if (_loggerWrite.isDebugEnabled())
+ {
+ //System.out.println("WriteDebug:"+"Starting read for Session:" + System.identityHashCode(session));
+ }
+
+ int totalReadBytes = 0;
+
+ while (totalReadBytes <= MAX_READ_BYTES_PER_SESSION)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(session.getReadBufferSize());
+ SocketChannel ch = session.getChannel();
+
+ try
+ {
+ buf.clear();
+
+ int readBytes = 0;
+ int ret;
+
+ try
+ {
+ while ((ret = ch.read(buf.buf())) > 0)
+ {
+ readBytes += ret;
+ totalReadBytes += ret;
+ }
+ }
+ finally
+ {
+ buf.flip();
+ }
+
+
+ if (readBytes > 0)
+ {
+ session.increaseReadBytes(readBytes);
+
+ session.getFilterChain().fireMessageReceived(session, buf);
+ buf = null;
+ }
+
+ if (ret <= 0)
+ {
+ if (ret == 0)
+ {
+ if (readBytes == session.getReadBufferSize())
+ {
+ continue;
+ }
+ }
+ else
+ {
+ scheduleRemove(session);
+ }
+
+ break;
+ }
+ }
+ catch (Throwable e)
+ {
+ if (e instanceof IOException)
+ {
+ scheduleRemove(session);
+ }
+ session.getFilterChain().fireExceptionCaught(session, e);
+
+ //Stop Reading this session.
+ return;
+ }
+ finally
+ {
+ if (buf != null)
+ {
+ buf.release();
+ }
+ }
+ }//for
+
+ // if (_loggerWrite.isDebugEnabled())
+ {
+ //System.out.println("WriteDebug:"+"Read for Session:" + System.identityHashCode(session) + " got: " + totalReadBytes);
+ }
+ }
+
+
+ private void notifyReadIdleness()
+ {
+ // process idle sessions
+ long currentTime = System.currentTimeMillis();
+ if ((currentTime - lastIdleReadCheckTime) >= 1000)
+ {
+ lastIdleReadCheckTime = currentTime;
+ Set keys = selector.keys();
+ if (keys != null)
+ {
+ for (Iterator it = keys.iterator(); it.hasNext();)
+ {
+ SelectionKey key = (SelectionKey) it.next();
+ SocketSessionImpl session = (SocketSessionImpl) key.attachment();
+ notifyReadIdleness(session, currentTime);
+ }
+ }
+ }
+ }
+
+ private void notifyWriteIdleness()
+ {
+ // process idle sessions
+ long currentTime = System.currentTimeMillis();
+ if ((currentTime - lastIdleWriteCheckTime) >= 1000)
+ {
+ lastIdleWriteCheckTime = currentTime;
+ Set keys = writeSelector.keys();
+ if (keys != null)
+ {
+ for (Iterator it = keys.iterator(); it.hasNext();)
+ {
+ SelectionKey key = (SelectionKey) it.next();
+ SocketSessionImpl session = (SocketSessionImpl) key.attachment();
+ notifyWriteIdleness(session, currentTime);
+ }
+ }
+ }
+ }
+
+ private void notifyReadIdleness(SocketSessionImpl session, long currentTime)
+ {
+ notifyIdleness0(
+ session, currentTime,
+ session.getIdleTimeInMillis(IdleStatus.BOTH_IDLE),
+ IdleStatus.BOTH_IDLE,
+ Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE)));
+ notifyIdleness0(
+ session, currentTime,
+ session.getIdleTimeInMillis(IdleStatus.READER_IDLE),
+ IdleStatus.READER_IDLE,
+ Math.max(session.getLastReadTime(), session.getLastIdleTime(IdleStatus.READER_IDLE)));
+
+ notifyWriteTimeout(session, currentTime, session
+ .getWriteTimeoutInMillis(), session.getLastWriteTime());
+ }
+
+ private void notifyWriteIdleness(SocketSessionImpl session, long currentTime)
+ {
+ notifyIdleness0(
+ session, currentTime,
+ session.getIdleTimeInMillis(IdleStatus.BOTH_IDLE),
+ IdleStatus.BOTH_IDLE,
+ Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE)));
+ notifyIdleness0(
+ session, currentTime,
+ session.getIdleTimeInMillis(IdleStatus.WRITER_IDLE),
+ IdleStatus.WRITER_IDLE,
+ Math.max(session.getLastWriteTime(), session.getLastIdleTime(IdleStatus.WRITER_IDLE)));
+
+ notifyWriteTimeout(session, currentTime, session
+ .getWriteTimeoutInMillis(), session.getLastWriteTime());
+ }
+
+ private void notifyIdleness0(SocketSessionImpl session, long currentTime,
+ long idleTime, IdleStatus status,
+ long lastIoTime)
+ {
+ if (idleTime > 0 && lastIoTime != 0
+ && (currentTime - lastIoTime) >= idleTime)
+ {
+ session.increaseIdleCount(status);
+ session.getFilterChain().fireSessionIdle(session, status);
+ }
+ }
+
+ private void notifyWriteTimeout(SocketSessionImpl session,
+ long currentTime,
+ long writeTimeout, long lastIoTime)
+ {
+
+ MultiThreadSocketSessionImpl sesh = (MultiThreadSocketSessionImpl) session;
+ SelectionKey key = sesh.getWriteSelectionKey();
+
+ synchronized (writeLock)
+ {
+ if (writeTimeout > 0
+ && (currentTime - lastIoTime) >= writeTimeout
+ && key != null && key.isValid()
+ && (key.interestOps() & SelectionKey.OP_WRITE) != 0)
+ {
+ session.getFilterChain().fireExceptionCaught(session, new WriteTimeoutException());
+ }
+ }
+ }
+
+ private SocketSessionImpl getNextFlushingSession()
+ {
+ return (SocketSessionImpl) flushingSessions.poll();
+ }
+
+ private void releaseSession(SocketSessionImpl session)
+ {
+ synchronized (session.getWriteRequestQueue())
+ {
+ synchronized (flushingSessionsSet)
+ {
+ if (session.getScheduledWriteRequests() > 0)
+ {
+ if (_loggerWrite.isDebugEnabled())
+ {
+ //System.out.println("WriteDebug:"+"Reflush" + System.identityHashCode(session));
+ }
+ flushingSessions.offer(session);
+ }
+ else
+ {
+ if (_loggerWrite.isDebugEnabled())
+ {
+ //System.out.println("WriteDebug:"+"Releasing session " + System.identityHashCode(session));
+ }
+ flushingSessionsSet.remove(session);
+ }
+ }
+ }
+ }
+
+ private void releaseWriteBuffers(SocketSessionImpl session)
+ {
+ Queue writeRequestQueue = session.getWriteRequestQueue();
+ WriteRequest req;
+
+ //Should this be synchronized?
+ synchronized (writeRequestQueue)
+ {
+ while ((req = (WriteRequest) writeRequestQueue.pop()) != null)
+ {
+ try
+ {
+ ((ByteBuffer) req.getMessage()).release();
+ }
+ catch (IllegalStateException e)
+ {
+ session.getFilterChain().fireExceptionCaught(session, e);
+ }
+ finally
+ {
+ req.getFuture().setWritten(false);
+ }
+ }
+ }
+ }
+
+ private void doFlush()
+ {
+ MultiThreadSocketSessionImpl session;
+
+ while ((session = (MultiThreadSocketSessionImpl) getNextFlushingSession()) != null)
+ {
+ if (!session.isConnected())
+ {
+ releaseWriteBuffers(session);
+ releaseSession(session);
+ continue;
+ }
+
+ SelectionKey key = session.getWriteSelectionKey();
+ // Retry later if session is not yet fully initialized.
+ // (In case that Session.write() is called before addSession() is processed)
+ if (key == null)
+ {
+ scheduleFlush(session);
+ releaseSession(session);
+ continue;
+ }
+ // skip if channel is already closed
+ if (!key.isValid())
+ {
+ releaseSession(session);
+ continue;
+ }
+
+ try
+ {
+ if (doFlush(session))
+ {
+ releaseSession(session);
+ }
+ }
+ catch (IOException e)
+ {
+ releaseSession(session);
+ scheduleRemove(session);
+ session.getFilterChain().fireExceptionCaught(session, e);
+ }
+
+ }
+
+ }
+
+ private boolean doFlush(SocketSessionImpl sessionParam) throws IOException
+ {
+ MultiThreadSocketSessionImpl session = (MultiThreadSocketSessionImpl) sessionParam;
+ // Clear OP_WRITE
+ SelectionKey key = session.getWriteSelectionKey();
+ synchronized (writeLock)
+ {
+ key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
+ }
+ SocketChannel ch = session.getChannel();
+ Queue writeRequestQueue = session.getWriteRequestQueue();
+
+ long totalFlushedBytes = 0;
+ while (true)
+ {
+ WriteRequest req;
+
+ synchronized (writeRequestQueue)
+ {
+ req = (WriteRequest) writeRequestQueue.first();
+ }
+
+ if (req == null)
+ {
+ break;
+ }
+
+ ByteBuffer buf = (ByteBuffer) req.getMessage();
+ if (buf.remaining() == 0)
+ {
+ synchronized (writeRequestQueue)
+ {
+ writeRequestQueue.pop();
+ }
+
+ session.increaseWrittenMessages();
+
+ buf.reset();
+ session.getFilterChain().fireMessageSent(session, req);
+ continue;
+ }
+
+
+ int writtenBytes = 0;
+
+ // Reported as DIRMINA-362
+ //note: todo: fixme: Not sure it is important but if we see NoyYetConnected exceptions or 100% CPU in the kernel then this is it.
+ if (key.isWritable())
+ {
+ writtenBytes = ch.write(buf.buf());
+ totalFlushedBytes += writtenBytes;
+ }
+
+ if (writtenBytes > 0)
+ {
+ session.increaseWrittenBytes(writtenBytes);
+ }
+
+ if (buf.hasRemaining() || (totalFlushedBytes <= MAX_FLUSH_BYTES_PER_SESSION))
+ {
+ // Kernel buffer is full
+ synchronized (writeLock)
+ {
+ key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
+ }
+ if (_loggerWrite.isDebugEnabled())
+ {
+ //System.out.println("WriteDebug:"+"Written BF: " + (session.getWrittenBytes() - totalFlushedBytes) + " bytes");
+ }
+ return false;
+ }
+ }
+
+ if (_loggerWrite.isDebugEnabled())
+ {
+ //System.out.println("WriteDebug:"+"Written : " + (session.getWrittenBytes() - totalFlushedBytes) + " bytes");
+ }
+ return true;
+ }
+
+ private void doUpdateTrafficMask()
+ {
+ if (trafficControllingSessions.isEmpty() || trafficMaskUpdateLock.isLocked())
+ {
+ return;
+ }
+
+ // Synchronize over entire operation as this method should be called
+ // from both read and write thread and we don't want the order of the
+ // updates to get changed.
+ trafficMaskUpdateLock.lock();
+ try
+ {
+ for (; ;)
+ {
+ MultiThreadSocketSessionImpl session;
+
+ session = (MultiThreadSocketSessionImpl) trafficControllingSessions.pop();
+
+ if (session == null)
+ {
+ break;
+ }
+
+ SelectionKey key = session.getReadSelectionKey();
+ // Retry later if session is not yet fully initialized.
+ // (In case that Session.suspend??() or session.resume??() is
+ // called before addSession() is processed)
+ if (key == null)
+ {
+ scheduleTrafficControl(session);
+ break;
+ }
+ // skip if channel is already closed
+ if (!key.isValid())
+ {
+ continue;
+ }
+
+ // The normal is OP_READ and, if there are write requests in the
+ // session's write queue, set OP_WRITE to trigger flushing.
+
+ //Sset to Read and Write if there is nothing then the cost
+ // is one loop through the flusher.
+ int ops = SelectionKey.OP_READ;
+
+ // Now mask the preferred ops with the mask of the current session
+ int mask = session.getTrafficMask().getInterestOps();
+ synchronized (readLock)
+ {
+ key.interestOps(ops & mask);
+ }
+ //Change key to the WriteSelection Key
+ key = session.getWriteSelectionKey();
+ if (key != null && key.isValid())
+ {
+ Queue writeRequestQueue = session.getWriteRequestQueue();
+ synchronized (writeRequestQueue)
+ {
+ if (!writeRequestQueue.isEmpty())
+ {
+ ops = SelectionKey.OP_WRITE;
+ synchronized (writeLock)
+ {
+ key.interestOps(ops & mask);
+ }
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ trafficMaskUpdateLock.unlock();
+ }
+
+ }
+
+ private class WriteWorker implements Runnable
+ {
+
+ public void run()
+ {
+ Thread.currentThread().setName(MultiThreadSocketIoProcessor.this.threadName + "Writer");
+
+ //System.out.println("WriteDebug:"+"Startup");
+ for (; ;)
+ {
+ try
+ {
+ int nKeys = writeSelector.select(SELECTOR_TIMEOUT);
+
+ doAddNewWrite();
+ doUpdateTrafficMask();
+
+ if (nKeys > 0)
+ {
+ //System.out.println("WriteDebug:"+nKeys + " keys from writeselector");
+ processWrite(writeSelector.selectedKeys());
+ }
+ else
+ {
+ //System.out.println("WriteDebug:"+"No keys from writeselector");
+ }
+
+ doRemove();
+ notifyWriteIdleness();
+
+ if (flushingSessionsSet.size() > 0)
+ {
+ doFlush();
+ }
+
+ if (writeSelector.keys().isEmpty())
+ {
+ synchronized (writeLock)
+ {
+
+ if (writeSelector.keys().isEmpty() && newSessions.isEmpty())
+ {
+ writeWorker = null;
+ try
+ {
+ writeSelector.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ finally
+ {
+ writeSelector = null;
+ }
+
+ break;
+ }
+ }
+ }
+
+ }
+ catch (Throwable t)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(t);
+
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e1)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e1);
+ }
+ }
+ }
+ //System.out.println("WriteDebug:"+"Shutdown");
+ }
+
+ }
+
+ private class ReadWorker implements Runnable
+ {
+
+ public void run()
+ {
+ Thread.currentThread().setName(MultiThreadSocketIoProcessor.this.threadName + "Reader");
+
+ //System.out.println("ReadDebug:"+"Startup");
+ for (; ;)
+ {
+ try
+ {
+ int nKeys = selector.select(SELECTOR_TIMEOUT);
+
+ doAddNewReader();
+ doUpdateTrafficMask();
+
+ if (nKeys > 0)
+ {
+ //System.out.println("ReadDebug:"+nKeys + " keys from selector");
+
+ processRead(selector.selectedKeys());
+ }
+ else
+ {
+ //System.out.println("ReadDebug:"+"No keys from selector");
+ }
+
+
+ doRemove();
+ notifyReadIdleness();
+
+ if (selector.keys().isEmpty())
+ {
+
+ synchronized (readLock)
+ {
+ if (selector.keys().isEmpty() && newSessions.isEmpty())
+ {
+ readWorker = null;
+ try
+ {
+ selector.close();
+ }
+ catch (IOException e)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ finally
+ {
+ selector = null;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ catch (Throwable t)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(t);
+
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e1)
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e1);
+ }
+ }
+ }
+ //System.out.println("ReadDebug:"+"Shutdown");
+ }
+
+ }
+}
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java
new file mode 100644
index 0000000000..043d4800b6
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java
@@ -0,0 +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.mina.transport.socket.nio;
+
+import org.apache.mina.common.ExceptionMonitor;
+import org.apache.mina.common.IoConnectorConfig;
+import org.apache.mina.common.support.BaseIoSessionConfig;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * An {@link IoConnectorConfig} for {@link SocketConnector}.
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $
+ */
+public class MultiThreadSocketSessionConfigImpl extends org.apache.mina.transport.socket.nio.SocketSessionConfigImpl
+{
+ private static boolean SET_RECEIVE_BUFFER_SIZE_AVAILABLE = false;
+ private static boolean SET_SEND_BUFFER_SIZE_AVAILABLE = false;
+ private static boolean GET_TRAFFIC_CLASS_AVAILABLE = false;
+ private static boolean SET_TRAFFIC_CLASS_AVAILABLE = false;
+
+ private static boolean DEFAULT_REUSE_ADDRESS;
+ private static int DEFAULT_RECEIVE_BUFFER_SIZE;
+ private static int DEFAULT_SEND_BUFFER_SIZE;
+ private static int DEFAULT_TRAFFIC_CLASS;
+ private static boolean DEFAULT_KEEP_ALIVE;
+ private static boolean DEFAULT_OOB_INLINE;
+ private static int DEFAULT_SO_LINGER;
+ private static boolean DEFAULT_TCP_NO_DELAY;
+
+ static
+ {
+ initialize();
+ }
+
+ private static void initialize()
+ {
+ Socket socket = null;
+
+ socket = new Socket();
+
+ try
+ {
+ DEFAULT_REUSE_ADDRESS = socket.getReuseAddress();
+ DEFAULT_RECEIVE_BUFFER_SIZE = socket.getReceiveBufferSize();
+ DEFAULT_SEND_BUFFER_SIZE = socket.getSendBufferSize();
+ DEFAULT_KEEP_ALIVE = socket.getKeepAlive();
+ DEFAULT_OOB_INLINE = socket.getOOBInline();
+ DEFAULT_SO_LINGER = socket.getSoLinger();
+ DEFAULT_TCP_NO_DELAY = socket.getTcpNoDelay();
+
+ // Check if setReceiveBufferSize is supported.
+ try
+ {
+ socket.setReceiveBufferSize(DEFAULT_RECEIVE_BUFFER_SIZE);
+ SET_RECEIVE_BUFFER_SIZE_AVAILABLE = true;
+ }
+ catch( SocketException e )
+ {
+ SET_RECEIVE_BUFFER_SIZE_AVAILABLE = false;
+ }
+
+ // Check if setSendBufferSize is supported.
+ try
+ {
+ socket.setSendBufferSize(DEFAULT_SEND_BUFFER_SIZE);
+ SET_SEND_BUFFER_SIZE_AVAILABLE = true;
+ }
+ catch( SocketException e )
+ {
+ SET_SEND_BUFFER_SIZE_AVAILABLE = false;
+ }
+
+ // Check if getTrafficClass is supported.
+ try
+ {
+ DEFAULT_TRAFFIC_CLASS = socket.getTrafficClass();
+ GET_TRAFFIC_CLASS_AVAILABLE = true;
+ }
+ catch( SocketException e )
+ {
+ GET_TRAFFIC_CLASS_AVAILABLE = false;
+ DEFAULT_TRAFFIC_CLASS = 0;
+ }
+ }
+ catch( SocketException e )
+ {
+ throw new ExceptionInInitializerError(e);
+ }
+ finally
+ {
+ if( socket != null )
+ {
+ try
+ {
+ socket.close();
+ }
+ catch( IOException e )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught(e);
+ }
+ }
+ }
+ }
+
+ public static boolean isSetReceiveBufferSizeAvailable() {
+ return SET_RECEIVE_BUFFER_SIZE_AVAILABLE;
+ }
+
+ public static boolean isSetSendBufferSizeAvailable() {
+ return SET_SEND_BUFFER_SIZE_AVAILABLE;
+ }
+
+ public static boolean isGetTrafficClassAvailable() {
+ return GET_TRAFFIC_CLASS_AVAILABLE;
+ }
+
+ public static boolean isSetTrafficClassAvailable() {
+ return SET_TRAFFIC_CLASS_AVAILABLE;
+ }
+
+ private boolean reuseAddress = DEFAULT_REUSE_ADDRESS;
+ private int receiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE;
+ private int sendBufferSize = DEFAULT_SEND_BUFFER_SIZE;
+ private int trafficClass = DEFAULT_TRAFFIC_CLASS;
+ private boolean keepAlive = DEFAULT_KEEP_ALIVE;
+ private boolean oobInline = DEFAULT_OOB_INLINE;
+ private int soLinger = DEFAULT_SO_LINGER;
+ private boolean tcpNoDelay = DEFAULT_TCP_NO_DELAY;
+
+ /**
+ * Creates a new instance.
+ */
+ MultiThreadSocketSessionConfigImpl()
+ {
+ }
+
+ public boolean isReuseAddress()
+ {
+ return reuseAddress;
+ }
+
+ public void setReuseAddress( boolean reuseAddress )
+ {
+ this.reuseAddress = reuseAddress;
+ }
+
+ public int getReceiveBufferSize()
+ {
+ return receiveBufferSize;
+ }
+
+ public void setReceiveBufferSize( int receiveBufferSize )
+ {
+ this.receiveBufferSize = receiveBufferSize;
+ }
+
+ public int getSendBufferSize()
+ {
+ return sendBufferSize;
+ }
+
+ public void setSendBufferSize( int sendBufferSize )
+ {
+ this.sendBufferSize = sendBufferSize;
+ }
+
+ public int getTrafficClass()
+ {
+ return trafficClass;
+ }
+
+ public void setTrafficClass( int trafficClass )
+ {
+ this.trafficClass = trafficClass;
+ }
+
+ public boolean isKeepAlive()
+ {
+ return keepAlive;
+ }
+
+ public void setKeepAlive( boolean keepAlive )
+ {
+ this.keepAlive = keepAlive;
+ }
+
+ public boolean isOobInline()
+ {
+ return oobInline;
+ }
+
+ public void setOobInline( boolean oobInline )
+ {
+ this.oobInline = oobInline;
+ }
+
+ public int getSoLinger()
+ {
+ return soLinger;
+ }
+
+ public void setSoLinger( int soLinger )
+ {
+ this.soLinger = soLinger;
+ }
+
+ public boolean isTcpNoDelay()
+ {
+ return tcpNoDelay;
+ }
+
+ public void setTcpNoDelay( boolean tcpNoDelay )
+ {
+ this.tcpNoDelay = tcpNoDelay;
+ }
+
+
+}
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java
new file mode 100644
index 0000000000..be4a2d289d
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java
@@ -0,0 +1,488 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.socket.nio;
+
+import org.apache.mina.common.IoFilter.WriteRequest;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoService;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.IoSessionConfig;
+import org.apache.mina.common.RuntimeIOException;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.common.support.BaseIoSessionConfig;
+import org.apache.mina.common.support.IoServiceListenerSupport;
+import org.apache.mina.util.Queue;
+
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * An {@link IoSession} for socket transport (TCP/IP).
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $
+ */
+class MultiThreadSocketSessionImpl extends SocketSessionImpl
+{
+ private final IoService manager;
+ private final IoServiceConfig serviceConfig;
+ private final SocketSessionConfig config = new SessionConfigImpl();
+ private final MultiThreadSocketIoProcessor ioProcessor;
+ private final MultiThreadSocketFilterChain filterChain;
+ private final SocketChannel ch;
+ private final Queue writeRequestQueue;
+ private final IoHandler handler;
+ private final SocketAddress remoteAddress;
+ private final SocketAddress localAddress;
+ private final SocketAddress serviceAddress;
+ private final IoServiceListenerSupport serviceListeners;
+ private SelectionKey readKey, writeKey;
+ private int readBufferSize;
+ private CountDownLatch registeredReadyLatch = new CountDownLatch(2);
+ private AtomicBoolean created = new AtomicBoolean(false);
+
+ /**
+ * Creates a new instance.
+ */
+ MultiThreadSocketSessionImpl( IoService manager,
+ SocketIoProcessor ioProcessor,
+ IoServiceListenerSupport listeners,
+ IoServiceConfig serviceConfig,
+ SocketChannel ch,
+ IoHandler defaultHandler,
+ SocketAddress serviceAddress )
+ {
+ super(manager, ioProcessor, listeners, serviceConfig, ch,defaultHandler,serviceAddress);
+ this.manager = manager;
+ this.serviceListeners = listeners;
+ this.ioProcessor = (MultiThreadSocketIoProcessor) ioProcessor;
+ this.filterChain = new MultiThreadSocketFilterChain(this);
+ this.ch = ch;
+ this.writeRequestQueue = new Queue();
+ this.handler = defaultHandler;
+ this.remoteAddress = ch.socket().getRemoteSocketAddress();
+ this.localAddress = ch.socket().getLocalSocketAddress();
+ this.serviceAddress = serviceAddress;
+ this.serviceConfig = serviceConfig;
+
+ // Apply the initial session settings
+ IoSessionConfig sessionConfig = serviceConfig.getSessionConfig();
+ if( sessionConfig instanceof SocketSessionConfig )
+ {
+ SocketSessionConfig cfg = ( SocketSessionConfig ) sessionConfig;
+ this.config.setKeepAlive( cfg.isKeepAlive() );
+ this.config.setOobInline( cfg.isOobInline() );
+ this.config.setReceiveBufferSize( cfg.getReceiveBufferSize() );
+ this.readBufferSize = cfg.getReceiveBufferSize();
+ this.config.setReuseAddress( cfg.isReuseAddress() );
+ this.config.setSendBufferSize( cfg.getSendBufferSize() );
+ this.config.setSoLinger( cfg.getSoLinger() );
+ this.config.setTcpNoDelay( cfg.isTcpNoDelay() );
+
+ if( this.config.getTrafficClass() != cfg.getTrafficClass() )
+ {
+ this.config.setTrafficClass( cfg.getTrafficClass() );
+ }
+ }
+ }
+
+ void awaitRegistration() throws InterruptedException
+ {
+ registeredReadyLatch.countDown();
+
+ registeredReadyLatch.await();
+ }
+
+ boolean created() throws InterruptedException
+ {
+ return created.get();
+ }
+
+ void doneCreation()
+ {
+ created.getAndSet(true);
+ }
+
+ public IoService getService()
+ {
+ return manager;
+ }
+
+ public IoServiceConfig getServiceConfig()
+ {
+ return serviceConfig;
+ }
+
+ public IoSessionConfig getConfig()
+ {
+ return config;
+ }
+
+ SocketIoProcessor getIoProcessor()
+ {
+ return ioProcessor;
+ }
+
+ public IoFilterChain getFilterChain()
+ {
+ return filterChain;
+ }
+
+ SocketChannel getChannel()
+ {
+ return ch;
+ }
+
+ IoServiceListenerSupport getServiceListeners()
+ {
+ return serviceListeners;
+ }
+
+ SelectionKey getSelectionKey()
+ {
+ return readKey;
+ }
+
+ SelectionKey getReadSelectionKey()
+ {
+ return readKey;
+ }
+
+ SelectionKey getWriteSelectionKey()
+ {
+ return writeKey;
+ }
+
+ void setSelectionKey(SelectionKey key)
+ {
+ this.readKey = key;
+ }
+
+ void setWriteSelectionKey(SelectionKey key)
+ {
+ this.writeKey = key;
+ }
+
+ public IoHandler getHandler()
+ {
+ return handler;
+ }
+
+ protected void close0()
+ {
+ filterChain.fireFilterClose( this );
+ }
+
+ Queue getWriteRequestQueue()
+ {
+ return writeRequestQueue;
+ }
+
+ /**
+ @return int Number of write scheduled write requests
+ @deprecated
+ */
+ public int getScheduledWriteMessages()
+ {
+ return getScheduledWriteRequests();
+ }
+
+ public int getScheduledWriteRequests()
+ {
+ synchronized( writeRequestQueue )
+ {
+ return writeRequestQueue.size();
+ }
+ }
+
+ public int getScheduledWriteBytes()
+ {
+ synchronized( writeRequestQueue )
+ {
+ return writeRequestQueue.byteSize();
+ }
+ }
+
+ protected void write0( WriteRequest writeRequest )
+ {
+ filterChain.fireFilterWrite( this, writeRequest );
+ }
+
+ public TransportType getTransportType()
+ {
+ return TransportType.SOCKET;
+ }
+
+ public SocketAddress getRemoteAddress()
+ {
+ //This is what I had previously
+// return ch.socket().getRemoteSocketAddress();
+ return remoteAddress;
+ }
+
+ public SocketAddress getLocalAddress()
+ {
+ //This is what I had previously
+// return ch.socket().getLocalSocketAddress();
+ return localAddress;
+ }
+
+ public SocketAddress getServiceAddress()
+ {
+ return serviceAddress;
+ }
+
+ protected void updateTrafficMask()
+ {
+ this.ioProcessor.updateTrafficMask( this );
+ }
+
+ int getReadBufferSize()
+ {
+ return readBufferSize;
+ }
+
+ private class SessionConfigImpl extends BaseIoSessionConfig implements SocketSessionConfig
+ {
+ public boolean isKeepAlive()
+ {
+ try
+ {
+ return ch.socket().getKeepAlive();
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public void setKeepAlive( boolean on )
+ {
+ try
+ {
+ ch.socket().setKeepAlive( on );
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public boolean isOobInline()
+ {
+ try
+ {
+ return ch.socket().getOOBInline();
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public void setOobInline( boolean on )
+ {
+ try
+ {
+ ch.socket().setOOBInline( on );
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public boolean isReuseAddress()
+ {
+ try
+ {
+ return ch.socket().getReuseAddress();
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public void setReuseAddress( boolean on )
+ {
+ try
+ {
+ ch.socket().setReuseAddress( on );
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public int getSoLinger()
+ {
+ try
+ {
+ return ch.socket().getSoLinger();
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public void setSoLinger( int linger )
+ {
+ try
+ {
+ if( linger < 0 )
+ {
+ ch.socket().setSoLinger( false, 0 );
+ }
+ else
+ {
+ ch.socket().setSoLinger( true, linger );
+ }
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public boolean isTcpNoDelay()
+ {
+ try
+ {
+ return ch.socket().getTcpNoDelay();
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public void setTcpNoDelay( boolean on )
+ {
+ try
+ {
+ ch.socket().setTcpNoDelay( on );
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public int getTrafficClass()
+ {
+ if( SocketSessionConfigImpl.isGetTrafficClassAvailable() )
+ {
+ try
+ {
+ return ch.socket().getTrafficClass();
+ }
+ catch( SocketException e )
+ {
+ // Throw an exception only when setTrafficClass is also available.
+ if( SocketSessionConfigImpl.isSetTrafficClassAvailable() )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ public void setTrafficClass( int tc )
+ {
+ if( SocketSessionConfigImpl.isSetTrafficClassAvailable() )
+ {
+ try
+ {
+ ch.socket().setTrafficClass( tc );
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+ }
+
+ public int getSendBufferSize()
+ {
+ try
+ {
+ return ch.socket().getSendBufferSize();
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public void setSendBufferSize( int size )
+ {
+ if( SocketSessionConfigImpl.isSetSendBufferSizeAvailable() )
+ {
+ try
+ {
+ ch.socket().setSendBufferSize( size );
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+ }
+
+ public int getReceiveBufferSize()
+ {
+ try
+ {
+ return ch.socket().getReceiveBufferSize();
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+
+ public void setReceiveBufferSize( int size )
+ {
+ if( SocketSessionConfigImpl.isSetReceiveBufferSizeAvailable() )
+ {
+ try
+ {
+ ch.socket().setReceiveBufferSize( size );
+ MultiThreadSocketSessionImpl.this.readBufferSize = size;
+ }
+ catch( SocketException e )
+ {
+ throw new RuntimeIOException( e );
+ }
+ }
+ }
+ }
+}
diff --git a/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java b/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java
new file mode 100644
index 0000000000..a23e546af5
--- /dev/null
+++ b/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java
@@ -0,0 +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.mina.transport.vmpipe;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.ExceptionMonitor;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.IoSessionConfig;
+import org.apache.mina.common.support.AbstractIoFilterChain;
+import org.apache.mina.common.support.BaseIoConnector;
+import org.apache.mina.common.support.BaseIoConnectorConfig;
+import org.apache.mina.common.support.BaseIoSessionConfig;
+import org.apache.mina.common.support.DefaultConnectFuture;
+import org.apache.mina.transport.vmpipe.support.VmPipe;
+import org.apache.mina.transport.vmpipe.support.VmPipeIdleStatusChecker;
+import org.apache.mina.transport.vmpipe.support.VmPipeSessionImpl;
+import org.apache.mina.util.AnonymousSocketAddress;
+
+/**
+ * Connects to {@link IoHandler}s which is bound on the specified
+ * {@link VmPipeAddress}.
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $
+ */
+public class QpidVmPipeConnector extends VmPipeConnector
+{
+ private static final IoSessionConfig CONFIG = new BaseIoSessionConfig() {};
+ private final IoServiceConfig defaultConfig = new BaseIoConnectorConfig()
+ {
+ public IoSessionConfig getSessionConfig()
+ {
+ return CONFIG;
+ }
+ };
+
+ /**
+ * Creates a new instance.
+ */
+ public QpidVmPipeConnector()
+ {
+ }
+
+ public ConnectFuture connect( SocketAddress address, IoHandler handler, IoServiceConfig config )
+ {
+ return connect( address, null, handler, config );
+ }
+
+ public ConnectFuture connect( SocketAddress address, SocketAddress localAddress, IoHandler handler, IoServiceConfig config )
+ {
+ if( address == null )
+ throw new NullPointerException( "address" );
+ if( handler == null )
+ throw new NullPointerException( "handler" );
+ if( ! ( address instanceof VmPipeAddress ) )
+ throw new IllegalArgumentException(
+ "address must be VmPipeAddress." );
+
+ if( config == null )
+ {
+ config = getDefaultConfig();
+ }
+
+ VmPipe entry = ( VmPipe ) VmPipeAcceptor.boundHandlers.get( address );
+ if( entry == null )
+ {
+ return DefaultConnectFuture.newFailedFuture(
+ new IOException( "Endpoint unavailable: " + address ) );
+ }
+
+ DefaultConnectFuture future = new DefaultConnectFuture();
+ VmPipeSessionImpl localSession =
+ new VmPipeSessionImpl(
+ this,
+ config,
+ getListeners(),
+ new Object(), // lock
+ new AnonymousSocketAddress(),
+ handler,
+ entry );
+
+ // initialize acceptor session
+ VmPipeSessionImpl remoteSession = localSession.getRemoteSession();
+ try
+ {
+ IoFilterChain filterChain = remoteSession.getFilterChain();
+ entry.getAcceptor().getFilterChainBuilder().buildFilterChain( filterChain );
+ entry.getConfig().getFilterChainBuilder().buildFilterChain( filterChain );
+ entry.getConfig().getThreadModel().buildFilterChain( filterChain );
+
+ // The following sentences don't throw any exceptions.
+ entry.getListeners().fireSessionCreated( remoteSession );
+ VmPipeIdleStatusChecker.getInstance().addSession( remoteSession );
+ }
+ catch( Throwable t )
+ {
+ ExceptionMonitor.getInstance().exceptionCaught( t );
+ remoteSession.close();
+ }
+
+
+ // initialize connector session
+ try
+ {
+ IoFilterChain filterChain = localSession.getFilterChain();
+ this.getFilterChainBuilder().buildFilterChain( filterChain );
+ config.getFilterChainBuilder().buildFilterChain( filterChain );
+ config.getThreadModel().buildFilterChain( filterChain );
+
+ // The following sentences don't throw any exceptions.
+ localSession.setAttribute( AbstractIoFilterChain.CONNECT_FUTURE, future );
+ getListeners().fireSessionCreated( localSession );
+ VmPipeIdleStatusChecker.getInstance().addSession( localSession);
+ }
+ catch( Throwable t )
+ {
+ future.setException( t );
+ }
+
+
+
+ return future;
+ }
+
+ public IoServiceConfig getDefaultConfig()
+ {
+ return defaultConfig;
+ }
+} \ No newline at end of file
diff --git a/java/common/src/main/java/org/apache/qpid/AMQChannelException.java b/java/common/src/main/java/org/apache/qpid/AMQChannelException.java
index 19f5035e33..f6f596da95 100644
--- a/java/common/src/main/java/org/apache/qpid/AMQChannelException.java
+++ b/java/common/src/main/java/org/apache/qpid/AMQChannelException.java
@@ -20,9 +20,7 @@
*/
package org.apache.qpid;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
/**
@@ -55,7 +53,7 @@ public class AMQChannelException extends AMQException
public AMQFrame getCloseFrame(int channel)
{
- return ChannelCloseBody.createAMQFrame(channel, major, minor, _classId, _methodId, getErrorCode().getCode(),
- new AMQShortString(getMessage()));
+ MethodRegistry reg = MethodRegistry.getMethodRegistry(new ProtocolVersion(major,minor));
+ return new AMQFrame(channel, reg.createChannelCloseBody(getErrorCode().getCode(), new AMQShortString(getMessage()),_classId,_methodId));
}
}
diff --git a/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java b/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java
index ba9f69a05c..afd415b1eb 100644
--- a/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java
+++ b/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java
@@ -21,9 +21,7 @@
package org.apache.qpid;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
/**
@@ -61,7 +59,12 @@ public class AMQConnectionException extends AMQException
public AMQFrame getCloseFrame(int channel)
{
- return ConnectionCloseBody.createAMQFrame(channel, major, minor, _classId, _methodId, getErrorCode().getCode(),
- new AMQShortString(getMessage()));
+ MethodRegistry reg = MethodRegistry.getMethodRegistry(new ProtocolVersion(major,minor));
+ return new AMQFrame(channel,
+ reg.createConnectionCloseBody(getErrorCode().getCode(),
+ new AMQShortString(getMessage()),
+ _classId,
+ _methodId));
+
}
}
diff --git a/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java b/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java
index b60f130652..6cdd57d6f2 100644
--- a/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java
+++ b/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java
@@ -1,40 +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;
-
-/**
- * AMQConnectionFailureException indicates that a connection to a broker could not be formed.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represents failure to connect to a broker.
- * </table>
- *
- * @todo Not an AMQP exception as no status code.
- */
-public class AMQConnectionFailureException extends AMQException
-{
- public AMQConnectionFailureException(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;
+
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQConnectionFailureException indicates that a connection to a broker could not be formed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to connect to a broker.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQConnectionFailureException extends AMQException
+{
+ public AMQConnectionFailureException(String message, Throwable cause)
+ {
+ super(null, message, cause);
+ }
+
+ public AMQConnectionFailureException(AMQConstant errorCode, String message, Throwable cause)
+ {
+ super(errorCode, message, cause);
+ }
+}
diff --git a/java/common/src/main/java/org/apache/qpid/AMQException.java b/java/common/src/main/java/org/apache/qpid/AMQException.java
index 6cbb98fd86..eda532b64e 100644
--- a/java/common/src/main/java/org/apache/qpid/AMQException.java
+++ b/java/common/src/main/java/org/apache/qpid/AMQException.java
@@ -20,6 +20,8 @@
*/
package org.apache.qpid;
+import javax.management.JMException;
+
import org.apache.qpid.protocol.AMQConstant;
/**
@@ -38,7 +40,7 @@ public class AMQException extends Exception
{
/** Holds the AMQ error code constant associated with this exception. */
private AMQConstant _errorCode;
-
+
/**
* Creates an exception with an optional error code, optional message and optional underlying cause.
*
@@ -52,6 +54,28 @@ public class AMQException extends Exception
_errorCode = errorCode;
}
+ /*
+ * Deprecated constructors brought from M2.1
+ */
+ @Deprecated
+ public AMQException (String msg)
+ {
+ this(null, (msg == null) ? "" : msg);
+ }
+
+ @Deprecated
+ public AMQException (AMQConstant errorCode, String msg)
+ {
+ this(errorCode, (msg == null) ? "" : msg, null);
+ }
+
+ @Deprecated
+ public AMQException(String msg, Throwable cause)
+ {
+ this(null, msg, cause);
+ }
+
+
/**
* Gets the AMQ protocol exception code associated with this exception.
*
@@ -61,4 +85,9 @@ public class AMQException extends Exception
{
return _errorCode;
}
+
+ public boolean isHardError()
+ {
+ return true;
+ }
}
diff --git a/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java b/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
index 15c8bea0a4..baca2a4773 100644
--- a/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
+++ b/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
@@ -36,4 +36,10 @@ public class AMQInvalidArgumentException extends AMQException
{
super(AMQConstant.INVALID_ARGUMENT, message, cause);
}
+
+ public boolean isHardError()
+ {
+ return false;
+ }
+
}
diff --git a/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java b/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java
index 1502c0efc5..01a569b693 100644
--- a/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java
+++ b/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java
@@ -45,4 +45,10 @@ public class AMQUndeliveredException extends AMQException
{
return _bounced;
}
+
+ public boolean isHardError()
+ {
+ return false;
+ }
+
}
diff --git a/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java b/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java
index 02ae3cb089..7eef73f337 100644
--- a/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java
+++ b/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java
@@ -22,6 +22,7 @@ package org.apache.qpid.codec;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoSession;
+import org.apache.mina.common.SimpleByteBufferAllocator;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
@@ -48,6 +49,9 @@ import org.apache.qpid.framing.ProtocolInitiation;
*/
public class AMQDecoder extends CumulativeProtocolDecoder
{
+
+ private static final String BUFFER = AMQDecoder.class.getName() + ".Buffer";
+
/** Holds the 'normal' AMQP data decoder. */
private AMQDataBlockDecoder _dataBlockDecoder = new AMQDataBlockDecoder();
@@ -56,6 +60,7 @@ public class AMQDecoder extends CumulativeProtocolDecoder
/** Flag to indicate whether this decoder needs to handle protocol initiation. */
private boolean _expectProtocolInitiation;
+ private boolean firstDecode = true;
/**
* Creates a new AMQP decoder.
@@ -81,14 +86,24 @@ public class AMQDecoder extends CumulativeProtocolDecoder
*/
protected boolean doDecode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception
{
- if (_expectProtocolInitiation)
+
+ boolean decoded;
+ if (_expectProtocolInitiation
+ || (firstDecode
+ && (in.remaining() > 0)
+ && (in.get(in.position()) == (byte)'A')))
{
- return doDecodePI(session, in, out);
+ decoded = doDecodePI(session, in, out);
}
else
{
- return doDecodeDataBlock(session, in, out);
+ decoded = doDecodeDataBlock(session, in, out);
}
+ if(firstDecode && decoded)
+ {
+ firstDecode = false;
+ }
+ return decoded;
}
/**
@@ -160,4 +175,97 @@ public class AMQDecoder extends CumulativeProtocolDecoder
{
_expectProtocolInitiation = expectProtocolInitiation;
}
+
+
+ /**
+ * Cumulates content of <tt>in</tt> into internal buffer and forwards
+ * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}.
+ * <tt>doDecode()</tt> is invoked repeatedly until it returns <tt>false</tt>
+ * and the cumulative buffer is compacted after decoding ends.
+ *
+ * @throws IllegalStateException if your <tt>doDecode()</tt> returned
+ * <tt>true</tt> not consuming the cumulative buffer.
+ */
+ public void decode( IoSession session, ByteBuffer in,
+ ProtocolDecoderOutput out ) throws Exception
+ {
+ ByteBuffer buf = ( ByteBuffer ) session.getAttribute( BUFFER );
+ // if we have a session buffer, append data to that otherwise
+ // use the buffer read from the network directly
+ if( buf != null )
+ {
+ buf.put( in );
+ buf.flip();
+ }
+ else
+ {
+ buf = in;
+ }
+
+ for( ;; )
+ {
+ int oldPos = buf.position();
+ boolean decoded = doDecode( session, buf, out );
+ if( decoded )
+ {
+ if( buf.position() == oldPos )
+ {
+ throw new IllegalStateException(
+ "doDecode() can't return true when buffer is not consumed." );
+ }
+
+ if( !buf.hasRemaining() )
+ {
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // if there is any data left that cannot be decoded, we store
+ // it in a buffer in the session and next time this decoder is
+ // invoked the session buffer gets appended to
+ if ( buf.hasRemaining() )
+ {
+ storeRemainingInSession( buf, session );
+ }
+ else
+ {
+ removeSessionBuffer( session );
+ }
+ }
+
+ /**
+ * Releases the cumulative buffer used by the specified <tt>session</tt>.
+ * Please don't forget to call <tt>super.dispose( session )</tt> when
+ * you override this method.
+ */
+ public void dispose( IoSession session ) throws Exception
+ {
+ removeSessionBuffer( session );
+ }
+
+ private void removeSessionBuffer(IoSession session)
+ {
+ ByteBuffer buf = ( ByteBuffer ) session.getAttribute( BUFFER );
+ if( buf != null )
+ {
+ buf.release();
+ session.removeAttribute( BUFFER );
+ }
+ }
+
+ private static final SimpleByteBufferAllocator SIMPLE_BYTE_BUFFER_ALLOCATOR = new SimpleByteBufferAllocator();
+
+ private void storeRemainingInSession(ByteBuffer buf, IoSession session)
+ {
+ ByteBuffer remainingBuf = SIMPLE_BYTE_BUFFER_ALLOCATOR.allocate( buf.remaining(), false );
+ remainingBuf.setAutoExpand( true );
+ remainingBuf.put( buf );
+ session.setAttribute( BUFFER, remainingBuf );
+ }
+
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java b/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java
index c497717870..fe04155bb8 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java
@@ -1,39 +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.framing;
-
-import org.apache.mina.common.ByteBuffer;
-
-public abstract class AMQBody
-{
- public abstract byte getFrameType();
-
- /**
- * Get the size of the body
- * @return unsigned short
- */
- protected abstract int getSize();
-
- protected abstract void writePayload(ByteBuffer buffer);
-
- protected abstract void populateFromBuffer(ByteBuffer buffer, long size)
- throws AMQFrameDecodingException, AMQProtocolVersionException;
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.protocol.AMQVersionAwareProtocolSession;
+import org.apache.qpid.AMQException;
+
+public interface AMQBody
+{
+ public byte getFrameType();
+
+ /**
+ * Get the size of the body
+ * @return unsigned short
+ */
+ public abstract int getSize();
+
+ public void writePayload(ByteBuffer buffer);
+
+ void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession) throws AMQException;
+}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java b/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java
index 11f505fd4b..02a46f3748 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java
@@ -27,7 +27,7 @@ public class AMQFrame extends AMQDataBlock implements EncodableAMQDataBlock
private final int _channel;
private final AMQBody _bodyFrame;
-
+ public static final byte FRAME_END_BYTE = (byte) 0xCE;
public AMQFrame(final int channel, final AMQBody bodyFrame)
@@ -47,13 +47,19 @@ public class AMQFrame extends AMQDataBlock implements EncodableAMQDataBlock
return 1 + 2 + 4 + _bodyFrame.getSize() + 1;
}
+ public static final int getFrameOverhead()
+ {
+ return 1 + 2 + 4 + 1;
+ }
+
+
public void writePayload(ByteBuffer buffer)
{
buffer.put(_bodyFrame.getFrameType());
EncodingUtils.writeUnsignedShort(buffer, _channel);
EncodingUtils.writeUnsignedInteger(buffer, _bodyFrame.getSize());
_bodyFrame.writePayload(buffer);
- buffer.put((byte) 0xCE);
+ buffer.put(FRAME_END_BYTE);
}
public final int getChannel()
@@ -66,10 +72,54 @@ public class AMQFrame extends AMQDataBlock implements EncodableAMQDataBlock
return _bodyFrame;
}
-
-
public String toString()
{
return "Frame channelId: " + _channel + ", bodyFrame: " + String.valueOf(_bodyFrame);
}
+
+ public static void writeFrame(ByteBuffer buffer, final int channel, AMQBody body)
+ {
+ buffer.put(body.getFrameType());
+ EncodingUtils.writeUnsignedShort(buffer, channel);
+ EncodingUtils.writeUnsignedInteger(buffer, body.getSize());
+ body.writePayload(buffer);
+ buffer.put(FRAME_END_BYTE);
+
+ }
+
+ public static void writeFrames(ByteBuffer buffer, final int channel, AMQBody body1, AMQBody body2)
+ {
+ buffer.put(body1.getFrameType());
+ EncodingUtils.writeUnsignedShort(buffer, channel);
+ EncodingUtils.writeUnsignedInteger(buffer, body1.getSize());
+ body1.writePayload(buffer);
+ buffer.put(FRAME_END_BYTE);
+ buffer.put(body2.getFrameType());
+ EncodingUtils.writeUnsignedShort(buffer, channel);
+ EncodingUtils.writeUnsignedInteger(buffer, body2.getSize());
+ body2.writePayload(buffer);
+ buffer.put(FRAME_END_BYTE);
+
+ }
+
+ public static void writeFrames(ByteBuffer buffer, final int channel, AMQBody body1, AMQBody body2, AMQBody body3)
+ {
+ buffer.put(body1.getFrameType());
+ EncodingUtils.writeUnsignedShort(buffer, channel);
+ EncodingUtils.writeUnsignedInteger(buffer, body1.getSize());
+ body1.writePayload(buffer);
+ buffer.put(FRAME_END_BYTE);
+ buffer.put(body2.getFrameType());
+ EncodingUtils.writeUnsignedShort(buffer, channel);
+ EncodingUtils.writeUnsignedInteger(buffer, body2.getSize());
+ body2.writePayload(buffer);
+ buffer.put(FRAME_END_BYTE);
+ buffer.put(body3.getFrameType());
+ EncodingUtils.writeUnsignedShort(buffer, channel);
+ EncodingUtils.writeUnsignedInteger(buffer, body3.getSize());
+ body3.writePayload(buffer);
+ buffer.put(FRAME_END_BYTE);
+
+ }
+
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java b/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java
index 843b6a1e8c..2373edb478 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java
@@ -38,4 +38,10 @@ public class AMQFrameDecodingException extends AMQException
{
super(errorCode, message, cause);
}
+
+ public AMQFrameDecodingException(AMQConstant errorCode, String message)
+ {
+ super(errorCode, message, null);
+ }
+
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java
index 0982847aac..4763b22290 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java
@@ -1,132 +1,83 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQChannelException;
-import org.apache.qpid.AMQConnectionException;
-import org.apache.qpid.protocol.AMQConstant;
-
-public abstract class AMQMethodBody extends AMQBody
-{
- public static final byte TYPE = 1;
-
- /** AMQP version */
- protected byte major;
- protected byte minor;
-
- public byte getMajor()
- {
- return major;
- }
-
- public byte getMinor()
- {
- return minor;
- }
-
- public AMQMethodBody(byte major, byte minor)
- {
- this.major = major;
- this.minor = minor;
- }
-
- /** unsigned short */
- protected abstract int getBodySize();
-
- /** @return unsigned short */
- protected abstract int getClazz();
-
- /** @return unsigned short */
- protected abstract int getMethod();
-
- protected abstract void writeMethodPayload(ByteBuffer buffer);
-
- public byte getFrameType()
- {
- return TYPE;
- }
-
- protected int getSize()
- {
- return 2 + 2 + getBodySize();
- }
-
- protected void writePayload(ByteBuffer buffer)
- {
- EncodingUtils.writeUnsignedShort(buffer, getClazz());
- EncodingUtils.writeUnsignedShort(buffer, getMethod());
- writeMethodPayload(buffer);
- }
-
- protected abstract void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException;
-
- protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException
- {
- populateMethodBodyFromBuffer(buffer);
- }
-
- public String toString()
- {
- StringBuffer buf = new StringBuffer(getClass().getName());
- buf.append("[ Class: ").append(getClazz());
- buf.append(" Method: ").append(getMethod()).append(']');
- return buf.toString();
- }
-
- /**
- * 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(), major, minor, null);
- }
-
- public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause)
- {
- return new AMQChannelException(code, message, getClazz(), getMethod(), major, minor, cause);
- }
-
- public AMQConnectionException getConnectionException(AMQConstant code, String message)
- {
- return new AMQConnectionException(code, message, getClazz(), getMethod(), major, minor, null);
- }
-
- public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause)
- {
- return new AMQConnectionException(code, message, getClazz(), getMethod(), major, minor, 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.framing;
+
+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;
+
+public interface AMQMethodBody extends AMQBody
+{
+ public static final byte TYPE = 1;
+
+ /** AMQP version */
+ public byte getMajor();
+
+ public byte getMinor();
+
+
+
+ /** @return unsigned short */
+ public int getClazz();
+
+ /** @return unsigned short */
+ public int getMethod();
+
+ public void writeMethodPayload(ByteBuffer buffer);
+
+
+ public int getSize();
+
+ public void writePayload(ByteBuffer buffer);
+
+ //public abstract void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException;
+
+ //public void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException;
+
+ public AMQFrame generateFrame(int channelId);
+
+ public String toString();
+
+
+
+ /**
+ * 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);
+
+ public AMQChannelException getChannelException(AMQConstant code, String message);
+
+ public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause);
+
+ public AMQConnectionException getConnectionException(AMQConstant code, String message);
+
+
+ public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause);
+
+
+ public boolean execute(MethodDispatcher methodDispatcher, int channelId) throws AMQException;
+}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java
index cf85bdab31..1a7022c11b 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java
@@ -40,7 +40,6 @@ public class AMQMethodBodyFactory implements BodyFactory
public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException
{
- return _protocolSession.getRegistry().get((short) in.getUnsignedShort(), (short) in.getUnsignedShort(), in,
- bodySize);
+ return _protocolSession.getMethodRegistry().convertToBody(in, bodySize);
}
}
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
new file mode 100644
index 0000000000..64af717342
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java
@@ -0,0 +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);
+ }
+
+}
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 359efe7eb7..0030742e94 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
@@ -26,6 +26,5 @@ import org.apache.mina.common.ByteBuffer;
public abstract interface AMQMethodBodyInstanceFactory
{
- public AMQMethodBody newInstance(byte major, byte minor, ByteBuffer buffer, long size) throws AMQFrameDecodingException;
- public AMQMethodBody newInstance(byte major, byte minor, int clazzID, int methodID, ByteBuffer buffer, long size) throws AMQFrameDecodingException;
+ public AMQMethodBody newInstance(ByteBuffer buffer, long size) throws AMQFrameDecodingException;
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
index ee63f2c83d..a747aaeda7 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
@@ -1,444 +1,721 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.lang.ref.WeakReference;
-
-/**
- * A short string is a representation of an AMQ Short String
- * Short strings differ from the Java String class by being limited to on ASCII characters (0-127)
- * and thus can be held more effectively in a byte buffer.
- *
- */
-public final class AMQShortString implements CharSequence, Comparable<AMQShortString>
-{
-
- private static final ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>> _localInternMap =
- new ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>>()
- {
- protected Map<AMQShortString, WeakReference<AMQShortString>> initialValue()
- {
- return new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
- };
- };
-
- private static final Map<AMQShortString, WeakReference<AMQShortString>> _globalInternMap =
- new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
-
- private static final Logger _logger = LoggerFactory.getLogger(AMQShortString.class);
-
- private final ByteBuffer _data;
- private int _hashCode;
- private final int _length;
- private static final char[] EMPTY_CHAR_ARRAY = new char[0];
- private char[] chars;
- private String str;
-
- public AMQShortString(byte[] data)
- {
-
- _data = ByteBuffer.wrap(data);
- _length = data.length;
- }
-
- public AMQShortString(String data)
- {
- this((data == null) ? EMPTY_CHAR_ARRAY : data.toCharArray());
- if (data != null)
- {
- _hashCode = data.hashCode();
- }
- }
-
- public AMQShortString(char[] data)
- {
- if (data == null)
- {
- throw new NullPointerException("Cannot create AMQShortString with null char[]");
- }
-
- final int length = data.length;
- final byte[] stringBytes = new byte[length];
- for (int i = 0; i < length; i++)
- {
- stringBytes[i] = (byte) (0xFF & data[i]);
- }
-
- _data = ByteBuffer.wrap(stringBytes);
- _data.rewind();
- _length = length;
-
- }
-
- public AMQShortString(CharSequence charSequence)
- {
- final int length = charSequence.length();
- final byte[] stringBytes = new byte[length];
- int hash = 0;
- for (int i = 0; i < length; i++)
- {
- stringBytes[i] = ((byte) (0xFF & charSequence.charAt(i)));
- hash = (31 * hash) + stringBytes[i];
-
- }
-
- _data = ByteBuffer.wrap(stringBytes);
- _data.rewind();
- _hashCode = hash;
- _length = length;
-
- }
-
- private AMQShortString(ByteBuffer data)
- {
- _data = data;
- _length = data.limit();
-
- }
-
- /**
- * Get the length of the short string
- * @return length of the underlying byte array
- */
- public int length()
- {
- return _length;
- }
-
- public char charAt(int index)
- {
-
- return (char) _data.get(index);
-
- }
-
- public CharSequence subSequence(int start, int end)
- {
- return new CharSubSequence(start, end);
- }
-
- public int writeToByteArray(byte[] encoding, int pos)
- {
- final int size = length();
- encoding[pos++] = (byte) length();
- for (int i = 0; i < size; i++)
- {
- encoding[pos++] = _data.get(i);
- }
-
- return pos;
- }
-
- public static AMQShortString readFromByteArray(byte[] byteEncodedDestination, int pos)
- {
-
- final byte len = byteEncodedDestination[pos];
- if (len == 0)
- {
- return null;
- }
-
- ByteBuffer data = ByteBuffer.wrap(byteEncodedDestination, pos + 1, len).slice();
-
- return new AMQShortString(data);
- }
-
- public static AMQShortString readFromBuffer(ByteBuffer buffer)
- {
- final short length = buffer.getUnsigned();
- if (length == 0)
- {
- return null;
- }
- else
- {
- ByteBuffer data = buffer.slice();
- data.limit(length);
- data.rewind();
- buffer.skip(length);
-
- return new AMQShortString(data);
- }
- }
-
- public byte[] getBytes()
- {
-
- if (_data.buf().hasArray() && (_data.arrayOffset() == 0))
- {
- return _data.array();
- }
- else
- {
- final int size = length();
- byte[] b = new byte[size];
- ByteBuffer buf = _data.duplicate();
- buf.rewind();
- buf.get(b);
-
- return b;
- }
-
- }
-
- public void writeToBuffer(ByteBuffer buffer)
- {
-
- final int size = length();
- if (size != 0)
- {
-
- buffer.put((byte) size);
- if (_data.buf().hasArray())
- {
- buffer.put(_data.array(), _data.arrayOffset(), length());
- }
- else
- {
-
- for (int i = 0; i < size; i++)
- {
-
- buffer.put(_data.get(i));
- }
- }
- }
- else
- {
- // really writing out unsigned byte
- buffer.put((byte) 0);
- }
-
- }
-
- private final class CharSubSequence implements CharSequence
- {
- private final int _offset;
- private final int _end;
-
- public CharSubSequence(final int offset, final int end)
- {
- _offset = offset;
- _end = end;
- }
-
- public int length()
- {
- return _end - _offset;
- }
-
- public char charAt(int index)
- {
- return AMQShortString.this.charAt(index + _offset);
- }
-
- public CharSequence subSequence(int start, int end)
- {
- return new CharSubSequence(start + _offset, end + _offset);
- }
- }
-
- public char[] asChars()
- {
- if (chars == null)
- {
- final int size = length();
- chars = new char[size];
-
- for (int i = 0; i < size; i++)
- {
- chars[i] = (char) _data.get(i);
- }
- }
-
- return chars;
- }
-
- public String asString()
- {
- if (str == null)
- {
- str = new String(asChars());
- }
-
- return str;
- }
-
- public boolean equals(Object o)
- {
- if (o == null)
- {
- return false;
- }
-
- if (o == this)
- {
- return true;
- }
-
- if (o instanceof AMQShortString)
- {
-
- final AMQShortString otherString = (AMQShortString) o;
-
- if ((_hashCode != 0) && (otherString._hashCode != 0) && (_hashCode != otherString._hashCode))
- {
- return false;
- }
-
- return _data.equals(otherString._data);
-
- }
-
- return (o instanceof CharSequence) && equals((CharSequence) o);
-
- }
-
- public boolean equals(CharSequence s)
- {
- if (s == null)
- {
- return false;
- }
-
- if (s.length() != length())
- {
- return false;
- }
-
- for (int i = 0; i < length(); i++)
- {
- if (charAt(i) != s.charAt(i))
- {
- return false;
- }
- }
-
- return true;
- }
-
- public int hashCode()
- {
- int hash = _hashCode;
- if (hash == 0)
- {
- final int size = length();
-
- for (int i = 0; i < size; i++)
- {
- hash = (31 * hash) + _data.get(i);
- }
-
- _hashCode = hash;
- }
-
- return hash;
- }
-
- public void setDirty()
- {
- _hashCode = 0;
- }
-
- public String toString()
- {
- return asString();
- }
-
- public int compareTo(AMQShortString name)
- {
- if (name == null)
- {
- return 1;
- }
- else
- {
-
- if (name.length() < length())
- {
- return -name.compareTo(this);
- }
-
- for (int i = 0; i < length(); i++)
- {
- final byte d = _data.get(i);
- final byte n = name._data.get(i);
- if (d < n)
- {
- return -1;
- }
-
- if (d > n)
- {
- return 1;
- }
- }
-
- return (length() == name.length()) ? 0 : -1;
- }
- }
-
- public AMQShortString intern()
- {
-
- hashCode();
-
- Map<AMQShortString, WeakReference<AMQShortString>> localMap =
- _localInternMap.get();
-
- WeakReference<AMQShortString> ref = localMap.get(this);
- AMQShortString internString;
-
- if(ref != null)
- {
- internString = ref.get();
- if(internString != null)
- {
- return internString;
- }
- }
-
-
- synchronized(_globalInternMap)
- {
-
- ref = _globalInternMap.get(this);
- if((ref == null) || ((internString = ref.get()) == null))
- {
- internString = new AMQShortString(getBytes());
- ref = new WeakReference(internString);
- _globalInternMap.put(internString, ref);
- }
-
- }
- localMap.put(internString, ref);
- return internString;
-
- }
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.lang.ref.WeakReference;
+
+/**
+ * A short string is a representation of an AMQ Short String
+ * Short strings differ from the Java String class by being limited to on ASCII characters (0-127)
+ * and thus can be held more effectively in a byte buffer.
+ *
+ */
+public final class AMQShortString implements CharSequence, Comparable<AMQShortString>
+{
+ private static final byte MINUS = (byte)'-';
+ private static final byte ZERO = (byte) '0';
+
+
+
+ private final class TokenizerImpl implements AMQShortStringTokenizer
+ {
+ private final byte _delim;
+ private int _count = -1;
+ private int _pos = 0;
+
+ public TokenizerImpl(final byte delim)
+ {
+ _delim = delim;
+ }
+
+ public int countTokens()
+ {
+ if(_count == -1)
+ {
+ _count = 1 + AMQShortString.this.occurences(_delim);
+ }
+ return _count;
+ }
+
+ public AMQShortString nextToken()
+ {
+ if(_pos <= AMQShortString.this.length())
+ {
+ int nextDelim = AMQShortString.this.indexOf(_delim, _pos);
+ if(nextDelim == -1)
+ {
+ nextDelim = AMQShortString.this.length();
+ }
+
+ AMQShortString nextToken = AMQShortString.this.substring(_pos, nextDelim++);
+ _pos = nextDelim;
+ return nextToken;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return _pos <= AMQShortString.this.length();
+ }
+ }
+
+ private AMQShortString substring(final int from, final int to)
+ {
+ return new AMQShortString(_data, from+_offset, to+_offset);
+ }
+
+
+ private static final ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>> _localInternMap =
+ new ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>>()
+ {
+ protected Map<AMQShortString, WeakReference<AMQShortString>> initialValue()
+ {
+ return new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
+ };
+ };
+
+ private static final Map<AMQShortString, WeakReference<AMQShortString>> _globalInternMap =
+ new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
+
+ private static final Logger _logger = LoggerFactory.getLogger(AMQShortString.class);
+
+ private final byte[] _data;
+ private final int _offset;
+ private int _hashCode;
+ private final int _length;
+ private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+
+ public static final AMQShortString EMPTY_STRING = new AMQShortString((String)null);
+
+ public AMQShortString(byte[] data)
+ {
+
+ _data = data.clone();
+ _length = data.length;
+ _offset = 0;
+ }
+
+ public AMQShortString(byte[] data, int pos)
+ {
+ final int size = data[pos++];
+ final byte[] dataCopy = new byte[size];
+ System.arraycopy(data,pos,dataCopy,0,size);
+ _length = size;
+ _data = dataCopy;
+ _offset = 0;
+ }
+
+ public AMQShortString(String data)
+ {
+ this((data == null) ? EMPTY_CHAR_ARRAY : data.toCharArray());
+
+ }
+
+ public AMQShortString(char[] data)
+ {
+ if (data == null)
+ {
+ throw new NullPointerException("Cannot create AMQShortString with null char[]");
+ }
+
+ final int length = data.length;
+ final byte[] stringBytes = new byte[length];
+ int hash = 0;
+ for (int i = 0; i < length; i++)
+ {
+ stringBytes[i] = (byte) (0xFF & data[i]);
+ hash = (31 * hash) + stringBytes[i];
+ }
+ _hashCode = hash;
+ _data = stringBytes;
+
+ _length = length;
+ _offset = 0;
+
+ }
+
+ public AMQShortString(CharSequence charSequence)
+ {
+ final int length = charSequence.length();
+ final byte[] stringBytes = new byte[length];
+ int hash = 0;
+ for (int i = 0; i < length; i++)
+ {
+ stringBytes[i] = ((byte) (0xFF & charSequence.charAt(i)));
+ hash = (31 * hash) + stringBytes[i];
+
+ }
+
+ _data = stringBytes;
+ _hashCode = hash;
+ _length = length;
+ _offset = 0;
+
+ }
+
+ private AMQShortString(ByteBuffer data, final int length)
+ {
+ if(data.isDirect() || data.isReadOnly())
+ {
+ byte[] dataBytes = new byte[length];
+ data.get(dataBytes);
+ _data = dataBytes;
+ _offset = 0;
+ }
+ else
+ {
+
+ _data = data.array();
+ _offset = data.arrayOffset() + data.position();
+ data.skip(length);
+
+ }
+ _length = length;
+
+ }
+
+ private AMQShortString(final byte[] data, final int from, final int to)
+ {
+ _offset = from;
+ _length = to - from;
+ _data = data;
+ }
+
+ public AMQShortString shrink()
+ {
+ if(_data.length != _length)
+ {
+ byte[] dataBytes = new byte[_length];
+ System.arraycopy(_data,_offset,dataBytes,0,_length);
+ return new AMQShortString(dataBytes,0,_length);
+ }
+ else
+ {
+ return this;
+ }
+ }
+
+
+ /**
+ * Get the length of the short string
+ * @return length of the underlying byte array
+ */
+ public int length()
+ {
+ return _length;
+ }
+
+ public char charAt(int index)
+ {
+
+ return (char) _data[_offset + index];
+
+ }
+
+ public CharSequence subSequence(int start, int end)
+ {
+ return new CharSubSequence(start, end);
+ }
+
+ public int writeToByteArray(byte[] encoding, int pos)
+ {
+ final int size = length();
+ encoding[pos++] = (byte) size;
+ System.arraycopy(_data,_offset,encoding,pos,size);
+ return pos+size;
+ }
+
+ public static AMQShortString readFromByteArray(byte[] byteEncodedDestination, int pos)
+ {
+
+
+ final AMQShortString shortString = new AMQShortString(byteEncodedDestination, pos);
+ if(shortString.length() == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return shortString;
+ }
+ }
+
+ public static AMQShortString readFromBuffer(ByteBuffer buffer)
+ {
+ final short length = buffer.getUnsigned();
+ if (length == 0)
+ {
+ return null;
+ }
+ else
+ {
+
+ return new AMQShortString(buffer, length);
+ }
+ }
+
+ public byte[] getBytes()
+ {
+ if(_offset == 0 && _length == _data.length)
+ {
+ return _data.clone();
+ }
+ else
+ {
+ byte[] data = new byte[_length];
+ System.arraycopy(_data,_offset,data,0,_length);
+ return data;
+ }
+ }
+
+ public void writeToBuffer(ByteBuffer buffer)
+ {
+
+ final int size = length();
+ //buffer.setAutoExpand(true);
+ buffer.put((byte) size);
+ buffer.put(_data, _offset, size);
+
+ }
+
+ public boolean endsWith(String s)
+ {
+ return endsWith(new AMQShortString(s));
+ }
+
+
+ public boolean endsWith(AMQShortString otherString)
+ {
+
+ if (otherString.length() > length())
+ {
+ return false;
+ }
+
+
+ int thisLength = length();
+ int otherLength = otherString.length();
+
+ for (int i = 1; i <= otherLength; i++)
+ {
+ if (charAt(thisLength - i) != otherString.charAt(otherLength - i))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean startsWith(String s)
+ {
+ return startsWith(new AMQShortString(s));
+ }
+
+ public boolean startsWith(AMQShortString otherString)
+ {
+
+ if (otherString.length() > length())
+ {
+ return false;
+ }
+
+ for (int i = 0; i < otherString.length(); i++)
+ {
+ if (charAt(i) != otherString.charAt(i))
+ {
+ return false;
+ }
+ }
+
+ return true;
+
+ }
+
+ public boolean startsWith(CharSequence otherString)
+ {
+ if (otherString.length() > length())
+ {
+ return false;
+ }
+
+ for (int i = 0; i < otherString.length(); i++)
+ {
+ if (charAt(i) != otherString.charAt(i))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ private final class CharSubSequence implements CharSequence
+ {
+ private final int _sequenceOffset;
+ private final int _end;
+
+ public CharSubSequence(final int offset, final int end)
+ {
+ _sequenceOffset = offset;
+ _end = end;
+ }
+
+ public int length()
+ {
+ return _end - _sequenceOffset;
+ }
+
+ public char charAt(int index)
+ {
+ return AMQShortString.this.charAt(index + _sequenceOffset);
+ }
+
+ public CharSequence subSequence(int start, int end)
+ {
+ return new CharSubSequence(start + _sequenceOffset, end + _sequenceOffset);
+ }
+ }
+
+ public char[] asChars()
+ {
+ final int size = length();
+ final char[] chars = new char[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ chars[i] = (char) _data[i + _offset];
+ }
+
+ return chars;
+ }
+
+ public String asString()
+ {
+ return new String(asChars());
+ }
+
+ public boolean equals(Object o)
+ {
+
+
+ if(o instanceof AMQShortString)
+ {
+ return equals((AMQShortString)o);
+ }
+ if(o instanceof CharSequence)
+ {
+ return equals((CharSequence)o);
+ }
+
+ if (o == null)
+ {
+ return false;
+ }
+
+ if (o == this)
+ {
+ return true;
+ }
+
+
+ return false;
+
+ }
+
+ public boolean equals(final AMQShortString otherString)
+ {
+ if (otherString == this)
+ {
+ return true;
+ }
+
+ if (otherString == null)
+ {
+ return false;
+ }
+
+ if ((_hashCode != 0) && (otherString._hashCode != 0) && (_hashCode != otherString._hashCode))
+ {
+ return false;
+ }
+
+ return (_offset == 0 && otherString._offset == 0 && _length == _data.length && otherString._length == otherString._data.length && Arrays.equals(_data,otherString._data))
+ || Arrays.equals(getBytes(),otherString.getBytes());
+
+ }
+
+ public boolean equals(CharSequence s)
+ {
+ if(s instanceof AMQShortString)
+ {
+ return equals((AMQShortString)s);
+ }
+
+ if (s == null)
+ {
+ return false;
+ }
+
+ if (s.length() != length())
+ {
+ return false;
+ }
+
+ for (int i = 0; i < length(); i++)
+ {
+ if (charAt(i) != s.charAt(i))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int hash = _hashCode;
+ if (hash == 0)
+ {
+ final int size = length();
+
+ for (int i = 0; i < size; i++)
+ {
+ hash = (31 * hash) + _data[i+_offset];
+ }
+
+ _hashCode = hash;
+ }
+
+ return hash;
+ }
+
+ public void setDirty()
+ {
+ _hashCode = 0;
+ }
+
+ public String toString()
+ {
+ return asString();
+ }
+
+ public int compareTo(AMQShortString name)
+ {
+ if (name == null)
+ {
+ return 1;
+ }
+ else
+ {
+
+ if (name.length() < length())
+ {
+ return -name.compareTo(this);
+ }
+
+ for (int i = 0; i < length(); i++)
+ {
+ final byte d = _data[i+_offset];
+ final byte n = name._data[i+name._offset];
+ if (d < n)
+ {
+ return -1;
+ }
+
+ if (d > n)
+ {
+ return 1;
+ }
+ }
+
+ return (length() == name.length()) ? 0 : -1;
+ }
+ }
+
+
+ public AMQShortStringTokenizer tokenize(byte delim)
+ {
+ return new TokenizerImpl(delim);
+ }
+
+
+ public AMQShortString intern()
+ {
+
+ hashCode();
+
+ Map<AMQShortString, WeakReference<AMQShortString>> localMap =
+ _localInternMap.get();
+
+ WeakReference<AMQShortString> ref = localMap.get(this);
+ AMQShortString internString;
+
+ if(ref != null)
+ {
+ internString = ref.get();
+ if(internString != null)
+ {
+ return internString;
+ }
+ }
+
+
+ synchronized(_globalInternMap)
+ {
+
+ ref = _globalInternMap.get(this);
+ if((ref == null) || ((internString = ref.get()) == null))
+ {
+ internString = shrink();
+ ref = new WeakReference(internString);
+ _globalInternMap.put(internString, ref);
+ }
+
+ }
+ localMap.put(internString, ref);
+ return internString;
+
+ }
+
+ private int occurences(final byte delim)
+ {
+ int count = 0;
+ final int end = _offset + _length;
+ for(int i = _offset ; i < end ; i++ )
+ {
+ if(_data[i] == delim)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private int indexOf(final byte val, final int pos)
+ {
+
+ for(int i = pos; i < length(); i++)
+ {
+ if(_data[_offset+i] == val)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+
+ public static AMQShortString join(final Collection<AMQShortString> terms,
+ final AMQShortString delim)
+ {
+ if(terms.size() == 0)
+ {
+ return EMPTY_STRING;
+ }
+
+ int size = delim.length() * (terms.size() - 1);
+ for(AMQShortString term : terms)
+ {
+ size += term.length();
+ }
+
+ byte[] data = new byte[size];
+ int pos = 0;
+ final byte[] delimData = delim._data;
+ final int delimOffset = delim._offset;
+ final int delimLength = delim._length;
+
+
+ for(AMQShortString term : terms)
+ {
+
+ if(pos!=0)
+ {
+ System.arraycopy(delimData, delimOffset,data,pos, delimLength);
+ pos+=delimLength;
+ }
+ System.arraycopy(term._data,term._offset,data,pos,term._length);
+ pos+=term._length;
+ }
+
+
+
+ return new AMQShortString(data,0,size);
+ }
+
+ public int toIntValue()
+ {
+ int pos = _offset;
+ int val = 0;
+
+
+ boolean isNegative = (_data[pos] == MINUS);
+ if(isNegative)
+ {
+ pos++;
+ }
+
+ final int end = _length + _offset;
+
+ while(pos < end)
+ {
+ int digit = (int) (_data[pos++] - ZERO);
+ if((digit < 0) || (digit > 9))
+ {
+ throw new NumberFormatException("\""+toString()+"\" is not a valid number");
+ }
+ val = val * 10;
+ val += digit;
+ }
+ if(isNegative)
+ {
+ val = val * -1;
+ }
+ return val;
+ }
+
+ public boolean contains(final byte b)
+ {
+ final int end = _length + _offset;
+ for(int i = _offset; i < end; i++)
+ {
+ if(_data[i] == b)
+ {
+ return true;
+ }
+ }
+ return false; //To change body of created methods use File | Settings | File Templates.
+ }
+
+}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java b/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java
new file mode 100644
index 0000000000..e2db8906a1
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java
@@ -0,0 +1,31 @@
+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.
+*
+*/
+public interface AMQShortStringTokenizer
+{
+
+ public int countTokens();
+
+ public AMQShortString nextToken();
+
+ boolean hasMoreTokens();
+}
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 6dda91a488..2c356d072c 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
@@ -23,12 +23,24 @@ package org.apache.qpid.framing;
import org.apache.mina.common.ByteBuffer;
import java.math.BigDecimal;
-import java.math.BigInteger;
+/**
+ * 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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Get the equivalent one byte identifier for a type.
+ * <tr><td> Calculate the size of an instance of an AMQP parameter type. <td> {@link EncodingUtils}
+ * <tr><td> Convert an instance of an AMQP parameter into a compatable Java object tagged with its AMQP type.
+ * <td> {@link AMQTypedValue}
+ * <tr><td> Write an instance of an AMQP parameter type to a byte buffer. <td> {@link EncodingUtils}
+ * <tr><td> Read an instance of an AMQP parameter from a byte buffer. <td> {@link EncodingUtils}
+ * </table>
+ */
public enum AMQType
{
- //AMQP FieldTable Wire Types
-
LONG_STRING('S')
{
public int getEncodingSize(Object value)
@@ -36,7 +48,6 @@ public enum AMQType
return EncodingUtils.encodedLongStringLength((String) value);
}
-
public String toNativeValue(Object value)
{
if (value != null)
@@ -58,12 +69,10 @@ public enum AMQType
{
return EncodingUtils.readLongString(buffer);
}
-
},
INTEGER('i')
{
-
public int getEncodingSize(Object value)
{
return EncodingUtils.unsignedIntegerLength();
@@ -89,12 +98,11 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Long.valueOf((String)value);
+ return Long.valueOf((String) value);
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to int.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int.");
}
}
@@ -111,22 +119,21 @@ public enum AMQType
DECIMAL('D')
{
-
public int getEncodingSize(Object value)
{
- return EncodingUtils.encodedByteLength()+ EncodingUtils.encodedIntegerLength();
+ return EncodingUtils.encodedByteLength() + EncodingUtils.encodedIntegerLength();
}
public Object toNativeValue(Object value)
{
- if(value instanceof BigDecimal)
+ if (value instanceof BigDecimal)
{
return (BigDecimal) value;
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to BigDecimal.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to BigDecimal.");
}
}
@@ -150,7 +157,8 @@ public enum AMQType
int unscaled = EncodingUtils.readInteger(buffer);
BigDecimal bd = new BigDecimal(unscaled);
- return bd.setScale(places);
+
+ return bd.setScale(places);
}
},
@@ -163,14 +171,14 @@ public enum AMQType
public Object toNativeValue(Object value)
{
- if(value instanceof Long)
+ if (value instanceof Long)
{
return (Long) value;
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to timestamp.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to timestamp.");
}
}
@@ -179,37 +187,97 @@ public enum AMQType
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)
{
- // TODO : fixme
- throw new UnsupportedOperationException();
+ // 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)
{
- // TODO : fixme
- throw new UnsupportedOperationException();
+ // 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)
{
- // TODO : fixme
- throw new UnsupportedOperationException();
+ // 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)
{
- // TODO : fixme
- throw new UnsupportedOperationException();
+ 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);
+ }
}
},
@@ -220,7 +288,6 @@ public enum AMQType
return 0;
}
-
public Object toNativeValue(Object value)
{
if (value == null)
@@ -229,14 +296,13 @@ public enum AMQType
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to null String.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to null String.");
}
}
public void writeValueImpl(Object value, ByteBuffer buffer)
- {
- }
+ { }
public Object readValueFromBuffer(ByteBuffer buffer)
{
@@ -244,8 +310,6 @@ public enum AMQType
}
},
- // Extended types
-
BINARY('x')
{
public int getEncodingSize(Object value)
@@ -253,21 +317,19 @@ public enum AMQType
return EncodingUtils.encodedLongstrLength((byte[]) value);
}
-
public Object toNativeValue(Object value)
{
- if((value instanceof byte[]) || (value == null))
+ if ((value instanceof byte[]) || (value == null))
{
return value;
}
else
{
- throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName() +
- ") cannot be converted to byte[]");
+ 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);
@@ -277,7 +339,6 @@ public enum AMQType
{
return EncodingUtils.readLongstr(buffer);
}
-
},
ASCII_STRING('c')
@@ -287,7 +348,6 @@ public enum AMQType
return EncodingUtils.encodedLongStringLength((String) value);
}
-
public String toNativeValue(Object value)
{
if (value != null)
@@ -309,7 +369,6 @@ public enum AMQType
{
return EncodingUtils.readLongString(buffer);
}
-
},
WIDE_STRING('C')
@@ -320,7 +379,6 @@ public enum AMQType
return EncodingUtils.encodedLongStringLength((String) value);
}
-
public String toNativeValue(Object value)
{
if (value != null)
@@ -351,7 +409,6 @@ public enum AMQType
return EncodingUtils.encodedBooleanLength();
}
-
public Object toNativeValue(Object value)
{
if (value instanceof Boolean)
@@ -360,12 +417,12 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Boolean.valueOf((String)value);
+ return Boolean.valueOf((String) value);
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to boolean.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to boolean.");
}
}
@@ -374,7 +431,6 @@ public enum AMQType
EncodingUtils.writeBoolean(buffer, (Boolean) value);
}
-
public Object readValueFromBuffer(ByteBuffer buffer)
{
return EncodingUtils.readBoolean(buffer);
@@ -388,7 +444,6 @@ public enum AMQType
return EncodingUtils.encodedCharLength();
}
-
public Character toNativeValue(Object value)
{
if (value instanceof Character)
@@ -401,8 +456,8 @@ public enum AMQType
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to char.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to char.");
}
}
@@ -415,7 +470,6 @@ public enum AMQType
{
return EncodingUtils.readChar(buffer);
}
-
},
BYTE('b')
@@ -425,7 +479,6 @@ public enum AMQType
return EncodingUtils.encodedByteLength();
}
-
public Byte toNativeValue(Object value)
{
if (value instanceof Byte)
@@ -434,12 +487,12 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Byte.valueOf((String)value);
+ return Byte.valueOf((String) value);
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to byte.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to byte.");
}
}
@@ -456,13 +509,11 @@ public enum AMQType
SHORT('s')
{
-
public int getEncodingSize(Object value)
{
return EncodingUtils.encodedShortLength();
}
-
public Short toNativeValue(Object value)
{
if (value instanceof Short)
@@ -475,16 +526,13 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Short.valueOf((String)value);
+ return Short.valueOf((String) value);
}
-
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to short.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to short.");
}
-
-
}
public void writeValueImpl(Object value, ByteBuffer buffer)
@@ -521,12 +569,11 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Integer.valueOf((String)value);
+ return Integer.valueOf((String) value);
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to int.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int.");
}
}
@@ -543,7 +590,6 @@ public enum AMQType
LONG('l')
{
-
public int getEncodingSize(Object value)
{
return EncodingUtils.encodedLongLength();
@@ -551,7 +597,7 @@ public enum AMQType
public Object toNativeValue(Object value)
{
- if(value instanceof Long)
+ if (value instanceof Long)
{
return (Long) value;
}
@@ -569,12 +615,12 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Long.valueOf((String)value);
+ return Long.valueOf((String) value);
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to long.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to long.");
}
}
@@ -583,7 +629,6 @@ public enum AMQType
EncodingUtils.writeLong(buffer, (Long) value);
}
-
public Object readValueFromBuffer(ByteBuffer buffer)
{
return EncodingUtils.readLong(buffer);
@@ -597,7 +642,6 @@ public enum AMQType
return EncodingUtils.encodedFloatLength();
}
-
public Float toNativeValue(Object value)
{
if (value instanceof Float)
@@ -606,12 +650,12 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Float.valueOf((String)value);
+ return Float.valueOf((String) value);
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to float.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to float.");
}
}
@@ -628,13 +672,11 @@ public enum AMQType
DOUBLE('d')
{
-
public int getEncodingSize(Object value)
{
return EncodingUtils.encodedDoubleLength();
}
-
public Double toNativeValue(Object value)
{
if (value instanceof Double)
@@ -647,12 +689,12 @@ public enum AMQType
}
else if ((value instanceof String) || (value == null))
{
- return Double.valueOf((String)value);
+ return Double.valueOf((String) value);
}
else
{
- throw new NumberFormatException("Cannot convert: " + value + "(" +
- value.getClass().getName() + ") to double.");
+ throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName()
+ + ") to double.");
}
}
@@ -667,35 +709,87 @@ public enum AMQType
}
};
+ /** 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((byte)identifier());
+ 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/AMQTypedValue.java b/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java
index 7193580884..e5b1fad9a8 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
@@ -18,23 +18,40 @@
* 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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create a fully typed AMQP value from a native type and a type tag. <td> {@link AMQType}
+ * <tr><td> Create a fully typed AMQP value from a binary representation in a byte buffer. <td> {@link AMQType}
+ * <tr><td> Write a fully typed AMQP value to a binary representation in a byte buffer. <td> {@link AMQType}
+ * <tr><td> Extract the type from a fully typed AMQP value.
+ * <tr><td> Extract the value from a fully typed AMQP value.
+ * </table>
+ */
public class AMQTypedValue
{
+ /** The type of the value. */
private final AMQType _type;
- private final Object _value;
+ /** The Java native representation of the AMQP typed value. */
+ private final Object _value;
public AMQTypedValue(AMQType type, Object value)
{
- if(type == null)
+ if (type == null)
{
throw new NullPointerException("Cannot create a typed value with null type");
}
+
_type = type;
_value = type.toNativeValue(value);
}
@@ -42,10 +59,9 @@ public class AMQTypedValue
private AMQTypedValue(AMQType type, ByteBuffer buffer)
{
_type = type;
- _value = type.readValueFromBuffer( buffer );
+ _value = type.readValueFromBuffer(buffer);
}
-
public AMQType getType()
{
return _type;
@@ -56,10 +72,9 @@ public class AMQTypedValue
return _value;
}
-
public void writeToBuffer(ByteBuffer buffer)
{
- _type.writeToBuffer(_value,buffer);
+ _type.writeToBuffer(_value, buffer);
}
public int getEncodingSize()
@@ -70,11 +85,12 @@ public class AMQTypedValue
public static AMQTypedValue readFromBuffer(ByteBuffer buffer)
{
AMQType type = AMQTypeMap.getType(buffer.get());
+
return new AMQTypedValue(type, buffer);
}
public String toString()
{
- return "["+getType()+": "+getValue()+"]";
+ return "[" + getType() + ": " + getValue() + "]";
}
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java b/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java
index 5ec62ede93..94030f383e 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java
@@ -24,7 +24,6 @@ import org.apache.mina.common.ByteBuffer;
public class CompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock
{
- private ByteBuffer _encodedBlock;
private AMQDataBlock[] _blocks;
@@ -33,27 +32,12 @@ public class CompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQD
_blocks = blocks;
}
- /**
- * The encoded block will be logically first before the AMQDataBlocks which are encoded
- * into the buffer afterwards.
- * @param encodedBlock already-encoded data
- * @param blocks some blocks to be encoded.
- */
- public CompositeAMQDataBlock(ByteBuffer encodedBlock, AMQDataBlock[] blocks)
- {
- this(blocks);
- _encodedBlock = encodedBlock;
- }
public AMQDataBlock[] getBlocks()
{
return _blocks;
}
- public ByteBuffer getEncodedBlock()
- {
- return _encodedBlock;
- }
public long getSize()
{
@@ -62,20 +46,11 @@ public class CompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQD
{
frameSize += _blocks[i].getSize();
}
- if (_encodedBlock != null)
- {
- _encodedBlock.rewind();
- frameSize += _encodedBlock.remaining();
- }
return frameSize;
}
public void writePayload(ByteBuffer buffer)
{
- if (_encodedBlock != null)
- {
- buffer.put(_encodedBlock);
- }
for (int i = 0; i < _blocks.length; i++)
{
_blocks[i].writePayload(buffer);
@@ -91,7 +66,7 @@ public class CompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQD
else
{
StringBuilder buf = new StringBuilder(this.getClass().getName());
- buf.append("{encodedBlock=").append(_encodedBlock);
+ buf.append("{");
for (int i = 0 ; i < _blocks.length; i++)
{
buf.append(" ").append(i).append("=[").append(_blocks[i].toString()).append("]");
diff --git a/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java b/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java
index be38695384..9d39f8aa86 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java
@@ -21,8 +21,10 @@
package org.apache.qpid.framing;
import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import org.apache.qpid.AMQException;
-public class ContentBody extends AMQBody
+public class ContentBody implements AMQBody
{
public static final byte TYPE = 3;
@@ -63,11 +65,24 @@ public class ContentBody extends AMQBody
{
if (payload != null)
{
- ByteBuffer copy = payload.duplicate();
- buffer.put(copy.rewind());
+ if(payload.isDirect() || payload.isReadOnly())
+ {
+ ByteBuffer copy = payload.duplicate();
+ buffer.put(copy.rewind());
+ }
+ else
+ {
+ buffer.put(payload.array(),payload.arrayOffset(),payload.limit());
+ }
}
}
+ public void handle(final int channelId, final AMQVersionAwareProtocolSession session)
+ throws AMQException
+ {
+ session.contentBodyReceived(channelId, this);
+ }
+
protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException
{
if (size > 0)
diff --git a/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java b/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java
index 02631a5f88..83e5a7e341 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java
@@ -21,8 +21,10 @@
package org.apache.qpid.framing;
import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import org.apache.qpid.AMQException;
-public class ContentHeaderBody extends AMQBody
+public class ContentHeaderBody implements AMQBody
{
public static final byte TYPE = 2;
@@ -110,6 +112,12 @@ public class ContentHeaderBody extends AMQBody
properties.writePropertyListPayload(buffer);
}
+ public void handle(final int channelId, final AMQVersionAwareProtocolSession session)
+ throws AMQException
+ {
+ session.contentHeaderReceived(channelId, this);
+ }
+
public static AMQFrame createAMQFrame(int channelId, int classId, int weight, BasicContentHeaderProperties properties,
long bodySize)
{
diff --git a/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java b/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java
index 712eb437db..46189b63d7 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java
@@ -22,6 +22,8 @@ package org.apache.qpid.framing;
import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.amqp_8_0.BasicConsumeBodyImpl;
+
public class ContentHeaderPropertiesFactory
{
private static final ContentHeaderPropertiesFactory _instance = new ContentHeaderPropertiesFactory();
@@ -43,7 +45,7 @@ public class ContentHeaderPropertiesFactory
// AMQP version change: "Hardwired" version to major=8, minor=0
// TODO: Change so that the actual version is obtained from
// the ProtocolInitiation object for this session.
- if (classId == BasicConsumeBody.getClazz((byte)8, (byte)0))
+ if (classId == BasicConsumeBodyImpl.CLASS_ID)
{
properties = new BasicContentHeaderProperties();
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java b/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java
new file mode 100644
index 0000000000..f6795ff200
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java
@@ -0,0 +1,50 @@
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+public abstract class DeferredDataBlock extends AMQDataBlock
+{
+ private AMQDataBlock _underlyingDataBlock;
+
+
+ public long getSize()
+ {
+ if(_underlyingDataBlock == null)
+ {
+ _underlyingDataBlock = createAMQDataBlock();
+ }
+ return _underlyingDataBlock.getSize();
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ if(_underlyingDataBlock == null)
+ {
+ _underlyingDataBlock = createAMQDataBlock();
+ }
+ _underlyingDataBlock.writePayload(buffer);
+ }
+
+ abstract protected AMQDataBlock createAMQDataBlock();
+
+}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java b/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java
index ccba8bd41e..6425f8c591 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java
@@ -229,7 +229,11 @@ public class EncodingUtils
encodedString[i] = (byte) cha[i];
}
- writeBytes(buffer, encodedString);
+ // TODO: check length fits in an unsigned byte
+ writeUnsignedByte(buffer, (short)encodedString.length);
+ buffer.put(encodedString);
+
+
}
else
{
@@ -928,15 +932,15 @@ public class EncodingUtils
public static byte[] readBytes(ByteBuffer buffer)
{
- short length = buffer.getUnsigned();
+ long length = buffer.getUnsignedInt();
if (length == 0)
{
return null;
}
else
{
- byte[] dataBytes = new byte[length];
- buffer.get(dataBytes, 0, length);
+ byte[] dataBytes = new byte[(int)length];
+ buffer.get(dataBytes, 0, (int)length);
return dataBytes;
}
@@ -947,13 +951,14 @@ public class EncodingUtils
if (data != null)
{
// TODO: check length fits in an unsigned byte
- writeUnsignedByte(buffer, (short) data.length);
+ writeUnsignedInteger(buffer, (long)data.length);
buffer.put(data);
}
else
- {
+ {
// really writing out unsigned byte
- buffer.put((byte) 0);
+ //buffer.put((byte) 0);
+ writeUnsignedInteger(buffer, 0L);
}
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
index 3438770450..9ba9b53b13 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
@@ -18,7 +18,6 @@
* under the License.
*
*/
-
package org.apache.qpid.framing;
import org.apache.mina.common.ByteBuffer;
@@ -360,6 +359,41 @@ public class FieldTable
}
}
+ /**
+ * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name.
+ *
+ * @param string The name of the parameter to get the associated FieldTable value for.
+ *
+ * @return The associated FieldTable value, or <tt>null</tt> if the associated value is not of FieldTable type or
+ * not present in the field table at all.
+ */
+ public FieldTable getFieldTable(String string)
+ {
+ return getFieldTable(new AMQShortString(string));
+ }
+
+ /**
+ * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name.
+ *
+ * @param string The name of the parameter to get the associated FieldTable value for.
+ *
+ * @return The associated FieldTable value, or <tt>null</tt> if the associated value is not of FieldTable type or
+ * not present in the field table at all.
+ */
+ public FieldTable getFieldTable(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+
+ if ((value != null) && (value.getType() == AMQType.FIELD_TABLE))
+ {
+ return (FieldTable) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
public Object getObject(String string)
{
return getObject(new AMQShortString(string));
@@ -568,6 +602,32 @@ public class FieldTable
return setProperty(string, AMQType.VOID.asTypedValue(null));
}
+ /**
+ * Associates a nested field table with the specified parameter name.
+ *
+ * @param string The name of the parameter to store in the table.
+ * @param ftValue The field table value to associate with the parameter name.
+ *
+ * @return The stored value.
+ */
+ public Object setFieldTable(String string, FieldTable ftValue)
+ {
+ return setFieldTable(new AMQShortString(string), ftValue);
+ }
+
+ /**
+ * Associates a nested field table with the specified parameter name.
+ *
+ * @param string The name of the parameter to store in the table.
+ * @param ftValue The field table value to associate with the parameter name.
+ *
+ * @return The stored value.
+ */
+ public Object setFieldTable(AMQShortString string, FieldTable ftValue)
+ {
+ return setProperty(string, AMQType.FIELD_TABLE.asTypedValue(ftValue));
+ }
+
public Object setObject(AMQShortString string, Object object)
{
if (object instanceof Boolean)
@@ -706,12 +766,15 @@ public class FieldTable
public void writeToBuffer(ByteBuffer buffer)
{
- final boolean trace = _logger.isTraceEnabled();
+ final boolean trace = _logger.isDebugEnabled();
if (trace)
{
- _logger.trace("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() + "...");
- _logger.trace(_properties.toString());
+ _logger.debug("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() + "...");
+ if (_properties != null)
+ {
+ _logger.debug(_properties.toString());
+ }
}
EncodingUtils.writeUnsignedInteger(buffer, getEncodedSize());
@@ -894,14 +957,21 @@ public class FieldTable
if (_encodedForm != null)
{
+ if(buffer.isDirect() || buffer.isReadOnly())
+ {
+ ByteBuffer encodedForm = _encodedForm.duplicate();
- if (_encodedForm.position() != 0)
+ if (encodedForm.position() != 0)
+ {
+ encodedForm.flip();
+ }
+
+ buffer.put(encodedForm);
+ }
+ else
{
- _encodedForm.flip();
+ buffer.put(_encodedForm.array(),_encodedForm.arrayOffset(),(int)_encodedSize);
}
- // _encodedForm.limit((int)getEncodedSize());
-
- buffer.put(_encodedForm);
}
else if (_properties != null)
{
@@ -915,11 +985,11 @@ public class FieldTable
final Map.Entry<AMQShortString, AMQTypedValue> me = it.next();
try
{
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:"
+ _logger.debug("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:"
+ me.getValue().getValue());
- _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining());
+ _logger.debug("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining());
}
// Write the actual parameter name
@@ -928,12 +998,12 @@ public class FieldTable
}
catch (Exception e)
{
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Exception thrown:" + e);
- _logger.trace("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:"
+ _logger.debug("Exception thrown:" + e);
+ _logger.debug("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:"
+ me.getValue().getValue());
- _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining());
+ _logger.debug("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining());
}
throw new RuntimeException(e);
@@ -945,7 +1015,7 @@ public class FieldTable
private void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException
{
- final boolean trace = _logger.isTraceEnabled();
+ final boolean trace = _logger.isDebugEnabled();
if (length > 0)
{
@@ -961,7 +1031,7 @@ public class FieldTable
if (trace)
{
- _logger.trace("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + value.getType()
+ _logger.debug("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + value.getType()
+ "', key '" + key + "', value '" + value.getValue() + "'");
}
@@ -976,7 +1046,7 @@ public class FieldTable
if (trace)
{
- _logger.trace("FieldTable::FieldTable(buffer," + length + "): Done.");
+ _logger.debug("FieldTable::FieldTable(buffer," + length + "): Done.");
}
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java b/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java
index 7246c4a1cf..15a43345b5 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java
@@ -21,8 +21,10 @@
package org.apache.qpid.framing;
import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import org.apache.qpid.AMQException;
-public class HeartbeatBody extends AMQBody
+public class HeartbeatBody implements AMQBody
{
public static final byte TYPE = 8;
public static AMQFrame FRAME = new HeartbeatBody().toFrame();
@@ -46,15 +48,21 @@ public class HeartbeatBody extends AMQBody
return TYPE;
}
- protected int getSize()
+ public int getSize()
{
return 0;//heartbeats we generate have no payload
}
- protected void writePayload(ByteBuffer buffer)
+ public void writePayload(ByteBuffer buffer)
{
}
+ public void handle(final int channelId, final AMQVersionAwareProtocolSession session)
+ throws AMQException
+ {
+ session.heartbeatBodyReceived(channelId, this);
+ }
+
protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException
{
if(size > 0)
diff --git a/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java b/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java
index 4c253b9973..3ac17e9204 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java
@@ -139,7 +139,7 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData
}
}
- public void checkVersion() throws AMQException
+ public ProtocolVersion checkVersion() throws AMQException
{
if(_protocolHeader.length != 4)
@@ -180,6 +180,7 @@ public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQData
throw new AMQProtocolVersionException("Protocol version " +
_protocolMajor + "." + _protocolMinor + " not suppoerted by this version of the Qpid broker.", null);
}
+ return pv;
}
public String toString()
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 26c048e34a..f8cf3f3011 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
@@ -25,7 +25,7 @@ import org.apache.mina.common.ByteBuffer;
public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock
{
- private ByteBuffer _encodedBlock;
+ private AMQDataBlock _firstFrame;
private AMQDataBlock _block;
@@ -40,10 +40,10 @@ public class SmallCompositeAMQDataBlock extends AMQDataBlock implements Encodabl
* @param encodedBlock already-encoded data
* @param block a block to be encoded.
*/
- public SmallCompositeAMQDataBlock(ByteBuffer encodedBlock, AMQDataBlock block)
+ public SmallCompositeAMQDataBlock(AMQDataBlock encodedBlock, AMQDataBlock block)
{
this(block);
- _encodedBlock = encodedBlock;
+ _firstFrame = encodedBlock;
}
public AMQDataBlock getBlock()
@@ -51,28 +51,28 @@ public class SmallCompositeAMQDataBlock extends AMQDataBlock implements Encodabl
return _block;
}
- public ByteBuffer getEncodedBlock()
+ public AMQDataBlock getFirstFrame()
{
- return _encodedBlock;
+ return _firstFrame;
}
public long getSize()
{
long frameSize = _block.getSize();
- if (_encodedBlock != null)
+ if (_firstFrame != null)
{
- _encodedBlock.rewind();
- frameSize += _encodedBlock.remaining();
+
+ frameSize += _firstFrame.getSize();
}
return frameSize;
}
public void writePayload(ByteBuffer buffer)
{
- if (_encodedBlock != null)
+ if (_firstFrame != null)
{
- buffer.put(_encodedBlock);
+ _firstFrame.writePayload(buffer);
}
_block.writePayload(buffer);
@@ -87,7 +87,7 @@ public class SmallCompositeAMQDataBlock extends AMQDataBlock implements Encodabl
else
{
StringBuilder buf = new StringBuilder(this.getClass().getName());
- buf.append("{encodedBlock=").append(_encodedBlock);
+ buf.append("{encodedBlock=").append(_firstFrame);
buf.append(" _block=[").append(_block.toString()).append("]");
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 6006e9793c..516d0c569c 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
@@ -182,7 +182,7 @@ public class VersionSpecificRegistry
+ " method " + methodID + ".", null);
}
- return bodyFactory.newInstance(_protocolMajorVersion, _protocolMinorVersion, classID, methodID, in, size);
+ return bodyFactory.newInstance( in, size);
}
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 706499c1b0..49c28bb06b 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
@@ -27,6 +27,8 @@ public interface MessagePublishInfo
public AMQShortString getExchange();
+ public void setExchange(AMQShortString exchange);
+
public boolean isImmediate();
public boolean isMandatory();
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
new file mode 100644
index 0000000000..3c5cb74773
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.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
new file mode 100644
index 0000000000..2fd4f70138
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java
@@ -0,0 +1,172 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.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
new file mode 100644
index 0000000000..2b7c9534a9
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.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
new file mode 100644
index 0000000000..b1be49a350
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java
@@ -0,0 +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;
+ }
+ }
+}
diff --git a/java/common/src/main/java/org/apache/qpid/pool/Job.java b/java/common/src/main/java/org/apache/qpid/pool/Job.java
index ba3c5d03fa..b2a09ac592 100644
--- a/java/common/src/main/java/org/apache/qpid/pool/Job.java
+++ b/java/common/src/main/java/org/apache/qpid/pool/Job.java
@@ -94,21 +94,23 @@ public class Job implements Runnable
/**
* Sequentially processes, up to the maximum number per job, the aggregated continuations in enqueued in this job.
*/
- void processAll()
+ boolean processAll()
{
// limit the number of events processed in one run
- for (int i = 0; i < _maxEvents; i++)
+ int i = _maxEvents;
+ while( --i != 0 )
{
Event e = _eventQueue.poll();
if (e == null)
{
- break;
+ return true;
}
else
{
e.process(_session);
}
}
+ return false;
}
/**
@@ -144,9 +146,15 @@ public class Job implements Runnable
*/
public void run()
{
- processAll();
- deactivate();
- _completionHandler.completed(_session, this);
+ if(processAll())
+ {
+ deactivate();
+ _completionHandler.completed(_session, this);
+ }
+ else
+ {
+ _completionHandler.notCompleted(_session, this);
+ }
}
/**
@@ -158,5 +166,7 @@ public class Job implements Runnable
static interface JobCompletionHandler
{
public void completed(IoSession session, Job job);
+
+ public void notCompleted(final IoSession session, final Job job);
}
}
diff --git a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
index d0dfb1adcf..2912e54662 100644
--- a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
+++ b/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
@@ -30,6 +30,8 @@ import org.apache.qpid.pool.Event.CloseEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ExecutorService;
/**
* PoolingFilter, is a no-op pass through filter that hands all events down the Mina filter chain by default. As it
@@ -83,9 +85,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
/** Used for debugging purposes. */
private static final Logger _logger = LoggerFactory.getLogger(PoolingFilter.class);
- /** Holds a mapping from Mina sessions to batched jobs for execution. */
- private final ConcurrentMap<IoSession, Job> _jobs = new ConcurrentHashMap<IoSession, Job>();
-
/** Holds the managed reference to obtain the executor for the batched jobs. */
private final ReferenceCountingExecutorService _poolReference;
@@ -93,7 +92,9 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
private final String _name;
/** Defines the maximum number of events that will be batched into a single job. */
- private final int _maxEvents = Integer.getInteger("amqj.server.read_write_pool.max_events", 10);
+ static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10);
+
+ private final int _maxEvents;
/**
* Creates a named pooling filter, on the specified shared thread pool.
@@ -101,10 +102,11 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
* @param refCountingPool The thread pool reference.
* @param name The identifying name of the filter type.
*/
- public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
+ public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name, int maxEvents)
{
_poolReference = refCountingPool;
_name = name;
+ _maxEvents = maxEvents;
}
/**
@@ -159,20 +161,34 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
/**
* Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running.
*
- * @param session The Mina session to work in.
+ * @param job The job.
* @param event The event to hand off asynchronously.
*/
- void fireAsynchEvent(IoSession session, Event event)
+ void fireAsynchEvent(Job job, Event event)
{
- Job job = getJobForSession(session);
+
// job.acquire(); //prevents this job being removed from _jobs
job.add(event);
- // Additional checks on pool to check that it hasn't shutdown.
- // The alternative is to catch the RejectedExecutionException that will result from executing on a shutdown pool
- if (job.activate() && (_poolReference.getPool() != null) && !_poolReference.getPool().isShutdown())
+ final ExecutorService pool = _poolReference.getPool();
+
+ if(pool == null)
{
- _poolReference.getPool().execute(job);
+ return;
+ }
+
+ // rather than perform additional checks on pool to check that it hasn't shutdown.
+ // catch the RejectedExecutionException that will result from executing on a shutdown pool
+ if (job.activate())
+ {
+ try
+ {
+ pool.execute(job);
+ }
+ catch(RejectedExecutionException e)
+ {
+ _logger.warn("Thread pool shutdown while tasks still outstanding");
+ }
}
}
@@ -185,7 +201,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void createNewJobForSession(IoSession session)
{
- Job job = new Job(session, this, _maxEvents);
+ Job job = new Job(session, this, MAX_JOB_EVENTS);
session.setAttribute(_name, job);
}
@@ -196,7 +212,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*
* @return The Job for this filter to place asynchronous events into.
*/
- private Job getJobForSession(IoSession session)
+ public Job getJobForSession(IoSession session)
{
return (Job) session.getAttribute(_name);
}
@@ -210,17 +226,57 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void completed(IoSession session, Job job)
{
+
+
if (!job.isComplete())
{
+ final ExecutorService pool = _poolReference.getPool();
+
+ if(pool == null)
+ {
+ return;
+ }
+
+
// ritchiem : 2006-12-13 Do we need to perform the additional checks here?
// Can the pool be shutdown at this point?
- if (job.activate() && (_poolReference.getPool() != null) && !_poolReference.getPool().isShutdown())
+ if (job.activate())
{
- _poolReference.getPool().execute(job);
+ try
+ {
+ pool.execute(job);
+ }
+ catch(RejectedExecutionException e)
+ {
+ _logger.warn("Thread pool shutdown while tasks still outstanding");
+ }
+
}
}
}
+ public void notCompleted(IoSession session, Job job)
+ {
+ final ExecutorService pool = _poolReference.getPool();
+
+ if(pool == null)
+ {
+ return;
+ }
+
+ try
+ {
+ pool.execute(job);
+ }
+ catch(RejectedExecutionException e)
+ {
+ _logger.warn("Thread pool shutdown while tasks still outstanding");
+ }
+
+ }
+
+
+
/**
* No-op pass through filter to the next filter in the chain.
*
@@ -377,7 +433,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public AsynchReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
{
- super(refCountingPool, name);
+ super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS));
}
/**
@@ -389,8 +445,8 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void messageReceived(NextFilter nextFilter, final IoSession session, Object message)
{
-
- fireAsynchEvent(session, new Event.ReceivedEvent(nextFilter, message));
+ Job job = getJobForSession(session);
+ fireAsynchEvent(job, new Event.ReceivedEvent(nextFilter, message));
}
/**
@@ -401,7 +457,8 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void sessionClosed(final NextFilter nextFilter, final IoSession session)
{
- fireAsynchEvent(session, new CloseEvent(nextFilter));
+ Job job = getJobForSession(session);
+ fireAsynchEvent(job, new CloseEvent(nextFilter));
}
}
@@ -419,7 +476,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public AsynchWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
{
- super(refCountingPool, name);
+ super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS));
}
/**
@@ -431,7 +488,8 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest)
{
- fireAsynchEvent(session, new Event.WriteEvent(nextFilter, writeRequest));
+ Job job = getJobForSession(session);
+ fireAsynchEvent(job, new Event.WriteEvent(nextFilter, writeRequest));
}
/**
@@ -442,7 +500,8 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void sessionClosed(final NextFilter nextFilter, final IoSession session)
{
- fireAsynchEvent(session, new CloseEvent(nextFilter));
+ Job job = getJobForSession(session);
+ fireAsynchEvent(job, new CloseEvent(nextFilter));
}
}
}
diff --git a/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java b/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java
index 2fbeeda1d4..5a7679a972 100644
--- a/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java
+++ b/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java
@@ -21,6 +21,7 @@
package org.apache.qpid.protocol;
import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.AMQException;
/**
* AMQMethodListener is a listener that receives notifications of AMQP methods. The methods are packaged as events in
@@ -57,7 +58,7 @@ public interface AMQMethodListener
*
* @todo Consider narrowing the exception.
*/
- <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws Exception;
+ <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws AMQException;
/**
* Notifies the listener of an error on the event context to which it is listening. The listener should perform
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 7c1d6fdaa0..b56a05f725 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
@@ -20,7 +20,8 @@
*/
package org.apache.qpid.protocol;
-import org.apache.qpid.framing.VersionSpecificRegistry;
+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
@@ -42,5 +43,15 @@ public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, Proto
*
* @return The method registry for a specific version of the AMQP.
*/
- public VersionSpecificRegistry getRegistry();
+// 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 60a7f30185..dea80cdcf4 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
@@ -20,6 +20,8 @@
*/
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.
@@ -32,6 +34,7 @@ package org.apache.qpid.protocol;
public interface ProtocolVersionAware
{
/**
+ * @deprecated
* Reports the AMQP minor version, that the implementer can handle.
*
* @return The AMQP minor version.
@@ -39,9 +42,12 @@ public interface ProtocolVersionAware
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/ConcurrentLinkedMessageQueueAtomicSize.java b/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java
index 461cf9591d..633cf4fe3a 100644
--- a/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java
+++ b/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java
@@ -215,6 +215,14 @@ public class ConcurrentLinkedMessageQueueAtomicSize<E> extends ConcurrentLinkedQ
public void remove()
{
last.remove();
+ if(last == _mainIterator)
+ {
+ _size.decrementAndGet();
+ }
+ else
+ {
+ _messageHeadSize.decrementAndGet();
+ }
}
};
}
diff --git a/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java b/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java
new file mode 100644
index 0000000000..b93dc46741
--- /dev/null
+++ b/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java
@@ -0,0 +1,396 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.mina.SocketIOTest;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.CloseFuture;
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.IoConnector;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.SimpleByteBufferAllocator;
+import org.apache.mina.filter.ReadThrottleFilterBuilder;
+import org.apache.mina.filter.WriteBufferLimitFilterBuilder;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+
+public class IOWriterClient implements Runnable
+{
+ private static final Logger _logger = LoggerFactory.getLogger(IOWriterClient.class);
+
+ public static int DEFAULT_TEST_SIZE = 2;
+
+ private IoSession _session;
+
+ private long _startTime;
+
+ private long[] _chunkTimes;
+
+ public int _chunkCount = 200000;
+
+ private int _chunkSize = 1024;
+
+ private CountDownLatch _notifier;
+
+ private int _maximumWriteQueueLength;
+
+ static public int _PORT = IOWriterServer._PORT;
+
+ public void run()
+ {
+ _logger.info("Starting to send " + _chunkCount + " buffers of " + _chunkSize + "B");
+ _startTime = System.currentTimeMillis();
+ _notifier = new CountDownLatch(1);
+
+ for (int i = 0; i < _chunkCount; i++)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(_chunkSize, false);
+ byte check = (byte) (i % 128);
+ buf.put(check);
+ buf.fill((byte) 88, buf.remaining());
+ buf.flip();
+
+ _session.write(buf);
+ }
+
+ long _sentall = System.currentTimeMillis();
+ long _receivedall = _sentall;
+ try
+ {
+ _logger.info("All buffers sent; waiting for receipt from server");
+ _notifier.await();
+ _receivedall = System.currentTimeMillis();
+ }
+ catch (InterruptedException e)
+ {
+ //Ignore
+ }
+ _logger.info("Completed");
+ _logger.info("Total time waiting for server after last write: " + (_receivedall - _sentall));
+
+ long totalTime = System.currentTimeMillis() - _startTime;
+
+ _logger.info("Total time: " + totalTime);
+ _logger.info("MB per second: " + (int) ((1.0 * _chunkSize * _chunkCount) / totalTime));
+ long lastChunkTime = _startTime;
+ double average = 0;
+ for (int i = 0; i < _chunkTimes.length; i++)
+ {
+ if (i == 0)
+ {
+ average = _chunkTimes[i] - _startTime;
+ }
+ else
+ {
+ long delta = _chunkTimes[i] - lastChunkTime;
+ if (delta != 0)
+ {
+ average = (average + delta) / 2;
+ }
+ }
+ lastChunkTime = _chunkTimes[i];
+ }
+ _logger.info("Average chunk time: " + average + "ms");
+ _logger.info("Maximum WriteRequestQueue size: " + _maximumWriteQueueLength);
+
+ CloseFuture cf = _session.close();
+ _logger.info("Closing session");
+ cf.join();
+ }
+
+ private class WriterHandler extends IoHandlerAdapter
+ {
+ private int _chunksReceived = 0;
+
+ private int _partialBytesRead = 0;
+
+ private byte _partialCheckNumber;
+
+ private int _totalBytesReceived = 0;
+
+ private int _receivedCount = 0;
+ private int _sentCount = 0;
+ private static final String DEFAULT_READ_BUFFER = "262144";
+ private static final String DEFAULT_WRITE_BUFFER = "262144";
+
+ public void sessionCreated(IoSession session) throws Exception
+ {
+ IoFilterChain chain = session.getFilterChain();
+
+ ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER)));
+ readfilter.attach(chain);
+
+ WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
+
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER)));
+
+ writefilter.attach(chain);
+ }
+
+ public void messageSent(IoSession session, Object message) throws Exception
+ {
+ _maximumWriteQueueLength = Math.max(session.getScheduledWriteRequests(), _maximumWriteQueueLength);
+
+ if (_logger.isDebugEnabled())
+ {
+ ++_sentCount;
+ if (_sentCount % 1000 == 0)
+ {
+ _logger.debug("Sent count " + _sentCount + ":WQueue" + session.getScheduledWriteRequests());
+
+ }
+ }
+ }
+
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ if (_logger.isDebugEnabled())
+ {
+ ++_receivedCount;
+
+ if (_receivedCount % 1000 == 0)
+ {
+ _logger.debug("Receieved count " + _receivedCount);
+ }
+ }
+
+ ByteBuffer result = (ByteBuffer) message;
+ _totalBytesReceived += result.remaining();
+ int size = result.remaining();
+ long now = System.currentTimeMillis();
+ if (_partialBytesRead > 0)
+ {
+ int offset = _chunkSize - _partialBytesRead;
+ if (size >= offset)
+ {
+ _chunkTimes[_chunksReceived++] = now;
+ result.position(offset);
+ }
+ else
+ {
+ // have not read even one chunk, including the previous partial bytes
+ _partialBytesRead += size;
+ return;
+ }
+ }
+
+
+ int chunkCount = result.remaining() / _chunkSize;
+
+ for (int i = 0; i < chunkCount; i++)
+ {
+ _chunkTimes[_chunksReceived++] = now;
+ byte check = result.get();
+ _logger.debug("Check number " + check + " read");
+ if (check != (byte) ((_chunksReceived - 1) % 128))
+ {
+ _logger.error("Check number " + check + " read when expected " + (_chunksReceived % 128));
+ }
+ _logger.debug("Chunk times recorded");
+
+ try
+ {
+ result.skip(_chunkSize - 1);
+ }
+ catch (IllegalArgumentException e)
+ {
+ _logger.error("Position was: " + result.position());
+ _logger.error("Tried to skip to: " + (_chunkSize * i));
+ _logger.error("limit was; " + result.limit());
+ }
+ }
+ _logger.debug("Chunks received now " + _chunksReceived);
+ _logger.debug("Bytes received: " + _totalBytesReceived);
+ _partialBytesRead = result.remaining();
+
+ if (_partialBytesRead > 0)
+ {
+ _partialCheckNumber = result.get();
+ }
+
+
+ if (_chunksReceived >= _chunkCount)
+ {
+ _notifier.countDown();
+ }
+
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause) throws Exception
+ {
+ _logger.error("Error: " + cause, cause);
+ }
+ }
+
+ public void startWriter() throws IOException, InterruptedException
+ {
+
+ _maximumWriteQueueLength = 0;
+
+ IoConnector ioConnector = null;
+
+ if (Boolean.getBoolean("multinio"))
+ {
+ _logger.warn("Using MultiThread NIO");
+ ioConnector = new org.apache.mina.transport.socket.nio.MultiThreadSocketConnector();
+ }
+ else
+ {
+ _logger.warn("Using MINA NIO");
+ ioConnector = new org.apache.mina.transport.socket.nio.SocketConnector();
+ }
+
+ SocketSessionConfig scfg = (SocketSessionConfig) ioConnector.getDefaultConfig().getSessionConfig();
+ scfg.setTcpNoDelay(true);
+ scfg.setSendBufferSize(32768);
+ scfg.setReceiveBufferSize(32768);
+
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+
+
+ final InetSocketAddress address = new InetSocketAddress("localhost", _PORT);
+ _logger.info("Attempting connection to " + address);
+
+ //Old mina style
+// ioConnector.setHandler(new WriterHandler());
+// ConnectFuture future = ioConnector.connect(address);
+ ConnectFuture future = ioConnector.connect(address, new WriterHandler());
+ // wait for connection to complete
+ future.join();
+ _logger.info("Connection completed");
+ // we call getSession which throws an IOException if there has been an error connecting
+ _session = future.getSession();
+
+ _chunkTimes = new long[_chunkCount];
+ Thread t = new Thread(this);
+ t.start();
+ t.join();
+ _logger.info("Test Complete");
+ }
+
+
+ public void test1k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 1k test");
+ _chunkSize = 1024;
+ startWriter();
+ }
+
+
+ public void test2k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 2k test");
+ _chunkSize = 2048;
+ startWriter();
+ }
+
+
+ public void test4k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 4k test");
+ _chunkSize = 4096;
+ startWriter();
+ }
+
+
+ public void test8k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 8k test");
+ _chunkSize = 8192;
+ startWriter();
+ }
+
+
+ public void test16k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 16k test");
+ _chunkSize = 16384;
+ startWriter();
+ }
+
+
+ public void test32k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 32k test");
+ _chunkSize = 32768;
+ startWriter();
+ }
+
+
+ public static int getIntArg(String[] args, int index, int defaultValue)
+ {
+ if (args.length > index)
+ {
+ try
+ {
+ return Integer.parseInt(args[index]);
+ }
+ catch (NumberFormatException e)
+ {
+ //Do nothing
+ }
+ }
+ return defaultValue;
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException
+ {
+ _PORT = getIntArg(args, 0, _PORT);
+
+ int test = getIntArg(args, 1, DEFAULT_TEST_SIZE);
+
+ IOWriterClient w = new IOWriterClient();
+ w._chunkCount = getIntArg(args, 2, w._chunkCount);
+ switch (test)
+ {
+ case 0:
+ w.test1k();
+ w.test2k();
+ w.test4k();
+ w.test8k();
+ w.test16k();
+ w.test32k();
+ break;
+ case 1:
+ w.test1k();
+ break;
+ case 2:
+ w.test2k();
+ break;
+ case 4:
+ w.test4k();
+ break;
+ case 8:
+ w.test8k();
+ break;
+ case 16:
+ w.test16k();
+ break;
+ case 32:
+ w.test32k();
+ break;
+ }
+ }
+}
diff --git a/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java b/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java
new file mode 100644
index 0000000000..423e98c67b
--- /dev/null
+++ b/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.mina.SocketIOTest;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.SimpleByteBufferAllocator;
+import org.apache.mina.filter.ReadThrottleFilterBuilder;
+import org.apache.mina.filter.WriteBufferLimitFilterBuilder;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+/** Tests MINA socket performance. This acceptor simply reads data from the network and writes it back again. */
+public class IOWriterServer
+{
+ private static final Logger _logger = LoggerFactory.getLogger(IOWriterServer.class);
+
+ static public int _PORT = 9999;
+
+ private static final String DEFAULT_READ_BUFFER = "262144";
+ private static final String DEFAULT_WRITE_BUFFER = "262144";
+
+
+ private static class TestHandler extends IoHandlerAdapter
+ {
+ private int _sentCount = 0;
+
+ private int _bytesSent = 0;
+
+ private int _receivedCount = 0;
+
+ public void sessionCreated(IoSession ioSession) throws java.lang.Exception
+ {
+ IoFilterChain chain = ioSession.getFilterChain();
+
+ ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER)));
+ readfilter.attach(chain);
+
+ WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
+
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER)));
+
+ writefilter.attach(chain);
+
+ }
+
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ ((ByteBuffer) message).acquire();
+ session.write(message);
+
+ if (_logger.isDebugEnabled())
+ {
+ _bytesSent += ((ByteBuffer) message).remaining();
+
+ _sentCount++;
+
+ if (_sentCount % 1000 == 0)
+ {
+ _logger.debug("Bytes sent: " + _bytesSent);
+ }
+ }
+ }
+
+ public void messageSent(IoSession session, Object message) throws Exception
+ {
+ if (_logger.isDebugEnabled())
+ {
+ ++_receivedCount;
+
+ if (_receivedCount % 1000 == 0)
+ {
+ _logger.debug("Receieved count " + _receivedCount);
+ }
+ }
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause) throws Exception
+ {
+ _logger.error("Error: " + cause, cause);
+ }
+ }
+
+ public void startAcceptor() throws IOException
+ {
+ IoAcceptor acceptor;
+ if (Boolean.getBoolean("multinio"))
+ {
+ _logger.warn("Using MultiThread NIO");
+ acceptor = new org.apache.mina.transport.socket.nio.MultiThreadSocketAcceptor();
+ }
+ else
+ {
+ _logger.warn("Using MINA NIO");
+ acceptor = new org.apache.mina.transport.socket.nio.SocketAcceptor();
+ }
+
+
+ SocketSessionConfig sc = (SocketSessionConfig) acceptor.getDefaultConfig().getSessionConfig();
+ sc.setTcpNoDelay(true);
+ sc.setSendBufferSize(32768);
+ sc.setReceiveBufferSize(32768);
+
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+
+ //The old mina style
+// acceptor.setLocalAddress(new InetSocketAddress(_PORT));
+// acceptor.setHandler(new TestHandler());
+// acceptor.bind();
+ acceptor.bind(new InetSocketAddress(_PORT), new TestHandler());
+
+ _logger.info("Bound on port " + _PORT + ":" + _logger.isDebugEnabled());
+ _logger.debug("debug on");
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+
+ if (args.length > 0)
+ {
+ try
+ {
+ _PORT = Integer.parseInt(args[0]);
+ }
+ catch (NumberFormatException e)
+ {
+ //IGNORE so use default port 9999;
+ }
+ }
+
+ IOWriterServer a = new IOWriterServer();
+ a.startAcceptor();
+ }
+}
diff --git a/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java b/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java
new file mode 100644
index 0000000000..92e7ce0a80
--- /dev/null
+++ b/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+public class AMQShortStringTest extends TestCase
+{
+
+ public static final AMQShortString HELLO = new AMQShortString("Hello");
+ public static final AMQShortString HELL = new AMQShortString("Hell");
+ public static final AMQShortString GOODBYE = new AMQShortString("Goodbye");
+ public static final AMQShortString GOOD = new AMQShortString("Good");
+ public static final AMQShortString BYE = new AMQShortString("BYE");
+
+ public void testStartsWith()
+ {
+ assertTrue(HELLO.startsWith(HELL));
+
+ assertFalse(HELL.startsWith(HELLO));
+
+ assertTrue(GOODBYE.startsWith(GOOD));
+
+ assertFalse(GOOD.startsWith(GOODBYE));
+ }
+
+ public void testEndWith()
+ {
+ assertFalse(HELL.endsWith(HELLO));
+
+ assertTrue(GOODBYE.endsWith(new AMQShortString("bye")));
+
+ assertFalse(GOODBYE.endsWith(BYE));
+ }
+
+
+ public void testTokenize()
+ {
+ AMQShortString dotSeparatedWords = new AMQShortString("this.is.a.test.with.1.2.3.-numbers-and-then--dashes-");
+ AMQShortStringTokenizer dotTokenizer = dotSeparatedWords.tokenize((byte) '.');
+
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("this"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("is"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("a"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("test"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(new AMQShortString("with"),(dotTokenizer.nextToken()));
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(dotTokenizer.nextToken().toIntValue() , 1);
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(dotTokenizer.nextToken().toIntValue() , 2);
+ assertTrue(dotTokenizer.hasMoreTokens());
+ assertEquals(dotTokenizer.nextToken().toIntValue() , 3);
+ assertTrue(dotTokenizer.hasMoreTokens());
+ AMQShortString dashString = dotTokenizer.nextToken();
+ assertEquals(new AMQShortString("-numbers-and-then--dashes-"),(dashString));
+
+ AMQShortStringTokenizer dashTokenizer = dashString.tokenize((byte)'-');
+ assertEquals(dashTokenizer.countTokens(), 7);
+
+ AMQShortString[] expectedResults = new AMQShortString[]
+ { AMQShortString.EMPTY_STRING,
+ new AMQShortString("numbers"),
+ new AMQShortString("and"),
+ new AMQShortString("then"),
+ AMQShortString.EMPTY_STRING,
+ new AMQShortString("dashes"),
+ AMQShortString.EMPTY_STRING };
+
+ for(int i = 0; i < 7; i++)
+ {
+ assertTrue(dashTokenizer.hasMoreTokens());
+ assertEquals(dashTokenizer.nextToken(), expectedResults[i]);
+ }
+
+ assertFalse(dotTokenizer.hasMoreTokens());
+ }
+
+
+ public void testEquals()
+ {
+ assertEquals(GOODBYE, new AMQShortString("Goodbye"));
+ assertEquals(new AMQShortString("A"), new AMQShortString("A"));
+ assertFalse(new AMQShortString("A").equals(new AMQShortString("a")));
+ }
+
+
+}
diff --git a/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java b/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java
index e63b0df770..007da7423e 100644
--- a/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java
+++ b/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java
@@ -1,21 +1,21 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.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
*
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
@@ -439,6 +439,60 @@ public class PropertyFieldTableTest extends TestCase
Assert.assertEquals("Hello", table1.getString("value"));
}
+ /** Check that a nested field table parameter correctly encodes and decodes to a byte buffer. */
+ public void testNestedFieldTable()
+ {
+ byte[] testBytes = new byte[] { 0, 1, 2, 3, 4, 5 };
+
+ FieldTable outerTable = new FieldTable();
+ FieldTable innerTable = new FieldTable();
+
+ // Put some stuff in the inner table.
+ innerTable.setBoolean("bool", true);
+ innerTable.setByte("byte", Byte.MAX_VALUE);
+ innerTable.setBytes("bytes", testBytes);
+ innerTable.setChar("char", 'c');
+ innerTable.setDouble("double", Double.MAX_VALUE);
+ innerTable.setFloat("float", Float.MAX_VALUE);
+ innerTable.setInteger("int", Integer.MAX_VALUE);
+ innerTable.setLong("long", Long.MAX_VALUE);
+ innerTable.setShort("short", Short.MAX_VALUE);
+ innerTable.setString("string", "hello");
+ innerTable.setString("null-string", null);
+
+ // Put the inner table in the outer one.
+ outerTable.setFieldTable("innerTable", innerTable);
+
+ // Write the outer table into the buffer.
+ final ByteBuffer buffer = ByteBuffer.allocate((int) outerTable.getEncodedSize() + 4);
+ outerTable.writeToBuffer(buffer);
+ buffer.flip();
+
+ // Extract the table back from the buffer again.
+ try
+ {
+ FieldTable extractedOuterTable = EncodingUtils.readFieldTable(buffer);
+
+ FieldTable extractedTable = extractedOuterTable.getFieldTable("innerTable");
+
+ Assert.assertEquals((Boolean) true, extractedTable.getBoolean("bool"));
+ Assert.assertEquals((Byte) Byte.MAX_VALUE, extractedTable.getByte("byte"));
+ assertBytesEqual(testBytes, extractedTable.getBytes("bytes"));
+ Assert.assertEquals((Character) 'c', extractedTable.getCharacter("char"));
+ Assert.assertEquals(Double.MAX_VALUE, extractedTable.getDouble("double"));
+ Assert.assertEquals(Float.MAX_VALUE, extractedTable.getFloat("float"));
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, extractedTable.getInteger("int"));
+ Assert.assertEquals((Long) Long.MAX_VALUE, extractedTable.getLong("long"));
+ Assert.assertEquals((Short) Short.MAX_VALUE, extractedTable.getShort("short"));
+ Assert.assertEquals("hello", extractedTable.getString("string"));
+ Assert.assertEquals(null, extractedTable.getString("null-string"));
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ fail("Failed to decode field table with nested inner table.");
+ }
+ }
+
public void testValues()
{
FieldTable table = new FieldTable();
diff --git a/java/common/templates/method/MethodBodyClass.tmpl b/java/common/templates/method/MethodBodyClass.tmpl
deleted file mode 100644
index eb730fd891..0000000000
--- a/java/common/templates/method/MethodBodyClass.tmpl
+++ /dev/null
@@ -1,183 +0,0 @@
-&{${CLASS}${METHOD}Body.java}
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 ${GENERATOR} - do not modify.
- * Supported AMQP versions:
-%{VLIST} * ${major}-${minor}
- */
-
-package org.apache.qpid.framing;
-
-import java.util.HashMap;
-
-import org.apache.mina.common.ByteBuffer;
-
-public class ${CLASS}${METHOD}Body extends AMQMethodBody implements EncodableAMQDataBlock
-{
- private static final AMQMethodBodyInstanceFactory factory = new AMQMethodBodyInstanceFactory()
- {
- public AMQMethodBody newInstance(byte major, byte minor, ByteBuffer in, long size) throws AMQFrameDecodingException
- {
- return new ${CLASS}${METHOD}Body(major, minor, in);
- }
-
- public AMQMethodBody newInstance(byte major, byte minor, int clazzID, int methodID, ByteBuffer in, long size) throws AMQFrameDecodingException
- {
- return new ${CLASS}${METHOD}Body(major, minor, clazzID, methodID, in);
- }
-
- };
-
- public static AMQMethodBodyInstanceFactory getFactory()
- {
- return factory;
- }
-
- public static HashMap<Integer, Integer> classIdMap = new HashMap<Integer, Integer>();
- public static HashMap<Integer, Integer> methodIdMap = new HashMap<Integer, Integer>();
-
- private static void registerMethodId(byte major, byte minor, int methodId)
- {
- methodIdMap.put((0xff & (int) major) | ((0xff & (int) minor)<<8), methodId);
- }
-
- private static void registerClassId(byte major, byte minor, int classId)
- {
- classIdMap.put((0xff & (int) major) | ((0xff & (int) minor)<<8), classId);
- }
-
-
- static
- {
-
- ${CLASS_ID_INIT}
- ${METHOD_ID_INIT}
-
- }
-
- // Fields declared in specification
-%{FLIST} ${field_declaration}
-
- private final int _clazz;
- private final int _method;
-
-
- // Constructor
-
- public ${CLASS}${METHOD}Body(byte major, byte minor, ByteBuffer buffer) throws AMQFrameDecodingException
- {
- this(major, minor, getClazz(major,minor), getMethod(major,minor), buffer);
- }
-
- public ${CLASS}${METHOD}Body(byte major, byte minor, int clazzID, int methodID, ByteBuffer buffer) throws AMQFrameDecodingException
- {
-
- super(major, minor);
- _clazz = clazzID;
- _method = methodID;
- %{FLIST} ${mb_field_decode}
- }
- public ${CLASS}${METHOD}Body(byte major, byte minor, int clazzID, int methodID
- %{FLIST} ${mb_field_parameter_list}
- )
- {
- super(major, minor);
- _clazz = getClazz(major,minor);
- _method = getMethod(major,minor);
- %{FLIST} ${mb_field_body_initialize}
- }
-
- public int getClazz()
- {
- return _clazz;
- }
-
- public int getMethod()
- {
- return _method;
- }
-
- public static int getClazz(byte major, byte minor)
- {
- return classIdMap.get((0xff & (int) major) | ((0xff & (int) minor)<<8));
- }
-
- public static int getMethod(byte major, byte minor)
- {
- return methodIdMap.get((0xff & (int) major) | ((0xff & (int) minor)<<8));
- }
-
-
- // Field methods
-%{FLIST} ${mb_field_get_method}
-
- protected int getBodySize()
- {
- int size = 0;
-%{FLIST} ${mb_field_size}
- return size;
- }
-
- protected void writeMethodPayload(ByteBuffer buffer)
- {
-%{FLIST} ${mb_field_encode}
- }
-
- public void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException
- {
-%{FLIST} ${mb_field_decode}
- }
-
- public String toString()
- {
- StringBuffer buf = new StringBuffer(super.toString());
-%{FLIST} ${mb_field_to_string}
- return buf.toString();
- }
-
- public static AMQFrame createAMQFrame(int channelId, byte major, byte minor
-%{FLIST} ${mb_field_parameter_list}
- )
- {
- return createAMQFrame(channelId, major, minor, getClazz(major,minor), getMethod(major,minor)
-%{FLIST} ${mb_field_passed_parameter_list}
- );
-
-
-
- }
-
- public static AMQFrame createAMQFrame(int channelId, byte major, byte minor, int clazzID, int methodID
-%{FLIST} ${mb_field_parameter_list}
- )
- {
- ${CLASS}${METHOD}Body body = new ${CLASS}${METHOD}Body(major, minor, clazzID, methodID
-%{FLIST} ${mb_field_passed_parameter_list}
- );
-
-
- AMQFrame frame = new AMQFrame(channelId, body);
- return frame;
- }
-
-}
diff --git a/java/common/templates/method/MethodBodyInterface.vm b/java/common/templates/method/MethodBodyInterface.vm
new file mode 100644
index 0000000000..d5feba12de
--- /dev/null
+++ b/java/common/templates/method/MethodBodyInterface.vm
@@ -0,0 +1,62 @@
+#macro( UpperCamel $name )
+#set( $name = "${name.substring(0,1).toUpperCase()}${name.substring(1)}" )
+#end
+#macro( toUpperCamel $name )${name.substring(0,1).toUpperCase()}${name.substring(1)}#end
+
+
+
+#set( $amqp_ClassName = $amqpClass.Name)
+#UpperCamel( $amqp_ClassName )
+#set( $amqp_MethodName = $amqpMethod.Name )
+#UpperCamel( $amqp_MethodName )
+#set( $javaClassName = "${amqp_ClassName}${amqp_MethodName}Body" )
+
+
+#set( $filename = "${javaClassName}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 ${generator} - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+
+package org.apache.qpid.framing;
+
+
+public interface ${javaClassName} extends EncodableAMQDataBlock, AMQMethodBody
+{
+
+
+#foreach( $field in $amqpMethod.Fields )
+
+#if( $amqpMethod.isCommon( $field ) )
+ public $field.ConsistentNativeType get#toUpperCamel( ${field.Name} )();
+#end
+#end
+
+
+
+}
diff --git a/java/common/templates/method/version/MethodBodyClass.vm b/java/common/templates/method/version/MethodBodyClass.vm
new file mode 100644
index 0000000000..9b2ba0fa39
--- /dev/null
+++ b/java/common/templates/method/version/MethodBodyClass.vm
@@ -0,0 +1,209 @@
+#macro( UpperCamel $name )
+#set( $name = "${name.substring(0,1).toUpperCase()}${name.substring(1)}" )
+#end
+#macro( toUpperCamel $name )${name.substring(0,1).toUpperCase()}${name.substring(1)}#end
+
+
+
+#set( $amqp_ClassName = $amqpClass.Name)
+#UpperCamel( $amqp_ClassName )
+#set( $amqp_MethodName = $amqpMethod.Name )
+#UpperCamel( $amqp_MethodName )
+#set( $javaClassName = "${amqp_ClassName}${amqp_MethodName}BodyImpl" )
+#set( $interfaceName = "${amqp_ClassName}${amqp_MethodName}Body" )
+#set( $amqpPackageName = "amqp_$version.getMajor()_$version.getMinor()" )
+
+#set( $filename = "${amqpPackageName}/${javaClassName}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 ${generator} - do not modify.
+ * Supported AMQP version:
+ * $version.getMajor()-$version.getMinor()
+ */
+
+#set( $clazz = $amqpClass.asSingleVersionClass( $version ) )
+#set( $method = $amqpMethod.asSingleVersionMethod( $version ) )
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import java.util.HashMap;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.AMQException;
+
+public class ${javaClassName} extends AMQMethodBody_$version.getMajor()_$version.getMinor() implements $interfaceName
+{
+ private static final AMQMethodBodyInstanceFactory FACTORY_INSTANCE = new AMQMethodBodyInstanceFactory()
+ {
+ public AMQMethodBody newInstance(ByteBuffer in, long size) throws AMQFrameDecodingException
+ {
+ return new ${javaClassName}(in);
+ }
+
+
+ };
+
+
+ public static AMQMethodBodyInstanceFactory getFactory()
+ {
+ return FACTORY_INSTANCE;
+ }
+
+ public static int CLASS_ID = $clazz.ClassId;
+
+ public static int METHOD_ID = $method.MethodId;
+
+
+
+ // Fields declared in specification
+#foreach( $field in $method.ConsolidatedFields )
+ private final $field.NativeType _$field.getName(); // $field.UnderlyingFields
+#end
+
+
+ // Constructor
+
+ public ${javaClassName}(ByteBuffer buffer) throws AMQFrameDecodingException
+ {
+#foreach( $field in $method.ConsolidatedFields )
+ _$field.Name = read$field.getEncodingType()( buffer );
+#end
+ }
+
+ public ${javaClassName}(
+#foreach( $field in $method.FieldList )
+#if( $velocityCount == $method.getFieldList().size() )
+ $field.NativeType $field.Name
+#else
+ $field.NativeType $field.Name,
+#end
+#end
+ )
+ {
+#set( $consolidatedFieldName = "" )
+#foreach( $field in $method.FieldList )
+#if( $method.isConsolidated( $field.Name ) )
+#if( !$method.getConsolidatedFieldName( $field.Name ).equals( $consolidatedFieldName ) )
+#if( !$consolidatedFieldName.equals("") )
+ _$consolidatedFieldName = $consolidatedFieldName; // 1
+#end
+#set( $consolidatedFieldName = $method.getConsolidatedFieldName( $field.Name ) )
+ byte $consolidatedFieldName = (byte)0;
+#end
+ if( $field.Name )
+ {
+ $consolidatedFieldName = (byte) (((int) $consolidatedFieldName) | (1 << $method.getPositionInBitField( $field.Name )));
+ }
+#if( $velocityCount == $method.getFieldList().size())
+ _$consolidatedFieldName = $consolidatedFieldName;
+#else
+
+#end
+#else
+#if( !$consolidatedFieldName.equals("") )
+ _$consolidatedFieldName = $consolidatedFieldName;
+#end
+#set( $consolidatedFieldName = "" )
+ _$field.Name = $field.Name;
+#end
+#end
+ }
+
+ public int getClazz()
+ {
+ return CLASS_ID;
+ }
+
+ public int getMethod()
+ {
+ return METHOD_ID;
+ }
+
+
+#foreach( $field in $method.FieldList )
+ public final $field.NativeType get#toUpperCamel( ${field.Name} )()
+ {
+#if( $method.isConsolidated( $field.Name ) )
+ return (((int)(_$method.getConsolidatedFieldName( $field.Name ))) & ( 1 << $method.getPositionInBitField( $field.Name ))) != 0;
+#else
+ return _$field.Name;
+#end
+ }
+#end
+
+ protected int getBodySize()
+ {
+#set( $fixedSize = 0 )
+#foreach( $field in $method.ConsolidatedFields )
+#if( $field.isFixedSize() )
+#set( $fixedSize = $fixedSize + $field.Size )
+#end
+#end
+ int size = $fixedSize;
+#foreach( $field in $method.ConsolidatedFields )
+#if( ! $field.isFixedSize() )
+ size += getSizeOf( _$field.Name );
+#end
+#end
+ return size;
+ }
+
+ public void writeMethodPayload(ByteBuffer buffer)
+ {
+#foreach( $field in $method.ConsolidatedFields )
+ write$field.getEncodingType()( buffer, _$field.Name );
+#end
+ }
+
+ public boolean execute(MethodDispatcher dispatcher, int channelId) throws AMQException
+ {
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+#if( $amqpMethod.inAllVersions() )
+ return dispatcher.dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(this, channelId);
+#else
+ return ((MethodDispatcher_$version.getMajor()_$version.getMinor())dispatcher).dispatch${amqp_ClassName}${amqpMethodNameUpperCamel}(this, channelId);
+
+#end
+
+ }
+
+
+ public String toString()
+ {
+ StringBuilder buf = new StringBuilder("[$javaClassName: ");
+#foreach( $field in $method.FieldList )
+ buf.append( "$field.Name=" );
+ buf.append( get#toUpperCamel( $field.Name )() );
+#if( $velocityCount != $method.FieldList.size() )
+ buf.append( ", " );
+#end
+#end
+ buf.append("]");
+ return buf.toString();
+ }
+
+
+}
diff --git a/java/common/templates/model/ClientMethodDispatcherInterface.vm b/java/common/templates/model/ClientMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..9e4aee7dee
--- /dev/null
+++ b/java/common/templates/model/ClientMethodDispatcherInterface.vm
@@ -0,0 +1,56 @@
+#set( $filename = "ClientMethodDispatcher.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.qpid.AMQException;
+
+
+public interface ClientMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#if( $amqpMethodVersions.inAllVersions() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isClientMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/java/common/templates/model/MethodDispatcherInterface.vm b/java/common/templates/model/MethodDispatcherInterface.vm
new file mode 100644
index 0000000000..ff14715fef
--- /dev/null
+++ b/java/common/templates/model/MethodDispatcherInterface.vm
@@ -0,0 +1,39 @@
+#set( $filename = "MethodDispatcher.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.qpid.AMQException;
+
+
+public interface MethodDispatcher extends
+ ClientMethodDispatcher, ServerMethodDispatcher
+{
+} \ No newline at end of file
diff --git a/java/common/templates/model/MethodRegistryClass.tmpl b/java/common/templates/model/MethodRegistryClass.tmpl
deleted file mode 100644
index 474d9e31d1..0000000000
--- a/java/common/templates/model/MethodRegistryClass.tmpl
+++ /dev/null
@@ -1,159 +0,0 @@
-&{MainRegistry.java}
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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 ${GENERATOR} - do not modify.
- * Supported AMQP versions:
-%{VLIST} * ${major}-${minor}
- */
-
-package org.apache.qpid.framing;
-
-import java.util.HashMap;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.mina.common.ByteBuffer;
-
-public class MainRegistry
-{
- private static final HashMap<Long, AMQMethodBodyInstanceFactory> classIDMethodIDVersionBodyMap = new HashMap<Long, AMQMethodBodyInstanceFactory>();
-
-
- private static final Logger _log = LoggerFactory.getLogger(MainRegistry.class);
-
-
- private static final int DEFAULT_MINOR_VERSION_COUNT = 10;
- private static final int DEFAULT_MAJOR_VERSION_COUNT = 10;
-
- private static VersionSpecificRegistry[][] _specificRegistries = new VersionSpecificRegistry[DEFAULT_MAJOR_VERSION_COUNT][];
-
- static
- {
-%{CLIST} ${reg_map_put_method}
-
- configure();
- }
-
- public static AMQMethodBody get(short classID, short methodID, byte major, byte minor, ByteBuffer in, long size)
- throws AMQFrameDecodingException
- {
- VersionSpecificRegistry registry = getVersionSpecificRegistry(major, minor);
- AMQMethodBodyInstanceFactory bodyFactory = registry.getMethodBody(classID,methodID);
-
- if (bodyFactory == null)
- {
- throw new AMQFrameDecodingException(null,
- "Unable to find a suitable decoder for class " + classID + " and method " +
- methodID + " in AMQP version " + major + "-" + minor + ".", null);
- }
- return bodyFactory.newInstance(major, minor, in, size);
-
-
- }
-
-
- public static VersionSpecificRegistry getVersionSpecificRegistry(ProtocolVersion pv)
- {
- return getVersionSpecificRegistry(pv.getMajorVersion(), pv.getMinorVersion());
- }
- public static VersionSpecificRegistry getVersionSpecificRegistry(byte major, byte minor)
- {
- try
- {
- return _specificRegistries[(int)major][(int)minor];
- }
- catch (IndexOutOfBoundsException e)
- {
- return null;
- }
- catch (NullPointerException e)
- {
- return null;
- }
-
-
- }
-
- private static VersionSpecificRegistry addVersionSpecificRegistry(byte major, byte minor)
- {
- VersionSpecificRegistry[][] registries = _specificRegistries;
- if(major >= registries.length)
- {
- _specificRegistries = new VersionSpecificRegistry[(int)major + 1][];
- System.arraycopy(registries, 0, _specificRegistries, 0, registries.length);
- registries = _specificRegistries;
- }
- if(registries[major] == null)
- {
- registries[major] = new VersionSpecificRegistry[ minor >= DEFAULT_MINOR_VERSION_COUNT ? minor + 1 : DEFAULT_MINOR_VERSION_COUNT ];
- }
- else if(registries[major].length <= minor)
- {
- VersionSpecificRegistry[] minorArray = registries[major];
- registries[major] = new VersionSpecificRegistry[ minor + 1 ];
- System.arraycopy(minorArray, 0, registries[major], 0, minorArray.length);
-
- }
-
- VersionSpecificRegistry newRegistry = new VersionSpecificRegistry(major,minor);
-
- registries[major][minor] = newRegistry;
-
- return newRegistry;
- }
-
- private static void registerMethod(short classID, short methodID, byte major, byte minor, AMQMethodBodyInstanceFactory instanceFactory )
- {
- VersionSpecificRegistry registry = getVersionSpecificRegistry(major,minor);
- if(registry == null)
- {
- registry = addVersionSpecificRegistry(major,minor);
-
- }
-
- registry.registerMethod(classID, methodID, instanceFactory);
-
- }
-
-
- private static void configure()
- {
- for(int i = 0 ; i < _specificRegistries.length; i++)
- {
- VersionSpecificRegistry[] registries = _specificRegistries[i];
- if(registries != null)
- {
- for(int j = 0 ; j < registries.length; j++)
- {
- VersionSpecificRegistry registry = registries[j];
-
- if(registry != null)
- {
- registry.configure();
- }
- }
- }
- }
-
- }
-
-}
diff --git a/java/common/templates/model/MethodRegistryClass.vm b/java/common/templates/model/MethodRegistryClass.vm
new file mode 100644
index 0000000000..759e5e4a42
--- /dev/null
+++ b/java/common/templates/model/MethodRegistryClass.vm
@@ -0,0 +1,104 @@
+#set( $filename = "MethodRegistry.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+
+import java.util.Map;
+import java.util.HashMap;
+
+
+public abstract class MethodRegistry
+{
+ private static final Map<ProtocolVersion, MethodRegistry> _registries =
+ new HashMap<ProtocolVersion, MethodRegistry>();
+
+#foreach( $supportedVersion in $model.VersionSet )
+
+#set( $verName = "_$supportedVersion.getMajor()_$supportedVersion.getMinor()" )
+#set( $regPackage = "org.apache.qpid.framing.amqp$verName" )
+ public static final MethodRegistry registry_$supportedVersion.getMajor()_$supportedVersion.getMinor() =
+ new ${regPackage}.MethodRegistry${verName}();
+
+#end
+
+
+ public abstract AMQMethodBody convertToBody(ByteBuffer in, long size)
+ throws AMQFrameDecodingException;
+
+ public abstract int getMaxClassId();
+
+ public abstract int getMaxMethodId(int classId);
+
+
+ protected MethodRegistry(ProtocolVersion pv)
+ {
+ _registries.put(pv, this);
+ }
+
+ public static MethodRegistry getMethodRegistry(ProtocolVersion pv)
+ {
+ return _registries.get(pv);
+ }
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#if( $amqpMethodVersions.isVersionInterfaceConsistent() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public abstract ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body create${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body(
+#foreach( $field in $amqpMethod.FieldList )
+#if( $velocityCount == $amqpMethod.getFieldList().size() )
+ final $field.NativeType $field.Name
+#else
+ final $field.NativeType $field.Name,
+#end
+#end
+ );
+
+
+#end
+#end
+#end
+
+
+ public abstract ProtocolVersionMethodConverter getProtocolVersionMethodConverter();
+
+} \ No newline at end of file
diff --git a/java/common/templates/model/ProtocolVersionListClass.tmpl b/java/common/templates/model/ProtocolVersionListClass.vm
index 4a2592d11b..9ac6adfdf5 100644
--- a/java/common/templates/model/ProtocolVersionListClass.tmpl
+++ b/java/common/templates/model/ProtocolVersionListClass.vm
@@ -1,4 +1,4 @@
-&{ProtocolVersion.java}
+#set( $filename = "ProtocolVersion.java" )
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -21,9 +21,11 @@
*/
/*
-* This file is auto-generated by ${GENERATOR} - do not modify.
+* This file is auto-generated by $generator - do not modify.
* Supported AMQP versions:
-%{VLIST} * ${major}-${minor}
+#foreach( $version in $model.getVersionSet() )
+* $version.getMajor()-$version.getMinor()
+#end
*/
package org.apache.qpid.framing;
@@ -31,6 +33,8 @@ package org.apache.qpid.framing;
import java.util.SortedSet;
import java.util.Collections;
import java.util.TreeSet;
+import java.util.Map;
+import java.util.HashMap;
public class ProtocolVersion implements Comparable
@@ -59,15 +63,33 @@ public class ProtocolVersion implements Comparable
public int compareTo(Object o)
{
ProtocolVersion pv = (ProtocolVersion) o;
- if(getMajorVersion() > pv.getMajorVersion())
+
+ /*
+ * 0-8 has it's major and minor numbers the wrong way round (it's actually 8-0)...
+ * so we need to deal with that case specially
+ */
+
+ if((_majorVersion == (byte) 8) && (_minorVersion == (byte) 0))
+ {
+ ProtocolVersion fixedThis = new ProtocolVersion(_minorVersion, _majorVersion);
+ return fixedThis.compareTo(pv);
+ }
+
+ if((pv.getMajorVersion() == (byte) 8) && (pv.getMinorVersion() == (byte) 0))
+ {
+ ProtocolVersion fixedOther = new ProtocolVersion(pv.getMinorVersion(), pv.getMajorVersion());
+ return this.compareTo(fixedOther);
+ }
+
+ if(_majorVersion > pv.getMajorVersion())
{
return 1;
}
- else if(getMajorVersion() < pv.getMajorVersion())
+ else if(_majorVersion < pv.getMajorVersion())
{
return -1;
}
- else if(getMajorVersion() > pv.getMajorVersion())
+ else if(_minorVersion > pv.getMinorVersion())
{
return 1;
}
@@ -104,14 +126,34 @@ public class ProtocolVersion implements Comparable
}
private static final SortedSet<ProtocolVersion> _supportedVersions;
-
+ private static final Map<String, ProtocolVersion> _nameToVersionMap =
+ new HashMap<String, ProtocolVersion>();
+ private static final ProtocolVersion _defaultVersion;
+
+
+#foreach( $version in $model.getVersionSet() )
+#set( $versionId = "v$version.getMajor()_$version.getMinor()" )
+ public static final ProtocolVersion $versionId = new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor());
+#end
+
static
{
SortedSet<ProtocolVersion> versions = new TreeSet<ProtocolVersion>();
-%{VLIST} versions.add(new ProtocolVersion((byte)${major},(byte)${minor}));
-
+#foreach( $version in $model.getVersionSet() )
+#set( $versionId = "v$version.getMajor()_$version.getMinor()" )
+ versions.add($versionId);
+ _nameToVersionMap.put("${version.getMajor()}-${version.getMinor()}", $versionId);
+#end
_supportedVersions = Collections.unmodifiableSortedSet(versions);
+
+
+ ProtocolVersion systemDefinedVersion =
+ _nameToVersionMap.get(System.getProperty("org.apache.qpid.amqp_version"));
+
+ _defaultVersion = (systemDefinedVersion == null)
+ ? getLatestSupportedVersion()
+ : systemDefinedVersion;
}
@@ -121,7 +163,16 @@ public class ProtocolVersion implements Comparable
}
+
+ public static ProtocolVersion parse(String name)
+ {
+ return _nameToVersionMap.get(name);
+ }
+ public static ProtocolVersion defaultProtocolVersion()
+ {
+ return _defaultVersion;
+ }
}
diff --git a/java/common/templates/model/ServerMethodDispatcherInterface.vm b/java/common/templates/model/ServerMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..b80d6027b7
--- /dev/null
+++ b/java/common/templates/model/ServerMethodDispatcherInterface.vm
@@ -0,0 +1,56 @@
+#set( $filename = "ServerMethodDispatcher.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.qpid.AMQException;
+
+
+public interface ServerMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#if( $amqpMethodVersions.inAllVersions() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isServerMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/java/common/templates/model/AmqpConstantsClass.tmpl b/java/common/templates/model/version/AmqpConstantsClass.vm
index 8d459f2977..8d459f2977 100644
--- a/java/common/templates/model/AmqpConstantsClass.tmpl
+++ b/java/common/templates/model/version/AmqpConstantsClass.vm
diff --git a/java/common/templates/model/version/ClientMethodDispatcherInterface.vm b/java/common/templates/model/version/ClientMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..80705c1a39
--- /dev/null
+++ b/java/common/templates/model/version/ClientMethodDispatcherInterface.vm
@@ -0,0 +1,55 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/ClientMethodDispatcher_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+
+
+public interface ClientMethodDispatcher_${version.getMajor()}_${version.getMinor()} extends ClientMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isClientMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/java/common/templates/model/version/MethodDispatcherInterface.vm b/java/common/templates/model/version/MethodDispatcherInterface.vm
new file mode 100644
index 0000000000..8a7b667a91
--- /dev/null
+++ b/java/common/templates/model/version/MethodDispatcherInterface.vm
@@ -0,0 +1,43 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/MethodDispatcher_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+
+
+public interface MethodDispatcher_${version.getMajor()}_${version.getMinor()}
+ extends MethodDispatcher,
+ ServerMethodDispatcher_${version.getMajor()}_${version.getMinor()},
+ ClientMethodDispatcher_${version.getMajor()}_${version.getMinor()}
+{
+
+} \ No newline at end of file
diff --git a/java/common/templates/model/version/MethodRegistryClass.vm b/java/common/templates/model/version/MethodRegistryClass.vm
new file mode 100644
index 0000000000..277605e34b
--- /dev/null
+++ b/java/common/templates/model/version/MethodRegistryClass.vm
@@ -0,0 +1,193 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/MethodRegistry_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ * $version.getMajor()-$version.getMinor()
+ */
+
+package org.apache.qpid.framing.amqp_${version.getMajor()}_${version.getMinor()};
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.protocol.AMQConstant;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+
+
+public class MethodRegistry_$version.getMajor()_$version.getMinor() extends MethodRegistry
+{
+
+ private static final Logger _log = LoggerFactory.getLogger(MethodRegistry.class);
+
+ private ProtocolVersionMethodConverter _protocolVersionConverter = new MethodConverter_$version.getMajor()_$version.getMinor()();
+
+#set( $specificModel = $model.asSingleVersionModel() )
+
+
+#set( $maxClassId = $specificModel.getMaximumClassId()+1 )
+ private final AMQMethodBodyInstanceFactory[][] _factories = new AMQMethodBodyInstanceFactory[$maxClassId][];
+
+ public MethodRegistry_$version.getMajor()_$version.getMinor()()
+ {
+ this(new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor()));
+ }
+
+ public MethodRegistry_$version.getMajor()_$version.getMinor()(ProtocolVersion pv)
+ {
+ super(pv);
+#foreach( $amqpClass in $specificModel.getClassList() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+
+
+
+ // Register method body instance factories for the $amqpClassNameUpperCamel class.
+
+#set( $maxMethodId = $amqpClass.getMaximumMethodId()+1 )
+ _factories[$amqpClass.getClassId()] = new AMQMethodBodyInstanceFactory[$maxMethodId];
+
+#foreach( $amqpMethod in $amqpClass.getMethodList() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ _factories[$amqpClass.getClassId()][$amqpMethod.getMethodId()] = ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}BodyImpl.getFactory();
+#end
+
+#end
+
+
+ }
+
+
+ public AMQMethodBody convertToBody(ByteBuffer in, long size)
+ throws AMQFrameDecodingException
+ {
+ int classId = in.getUnsignedShort();
+ int methodId = in.getUnsignedShort();
+
+ AMQMethodBodyInstanceFactory bodyFactory;
+ try
+ {
+ bodyFactory = _factories[classId][methodId];
+ }
+ catch(NullPointerException e)
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Class " + classId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+ }
+ catch(IndexOutOfBoundsException e)
+ {
+ if(classId >= _factories.length)
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Class " + classId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+
+ }
+ else
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Method " + methodId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+
+ }
+ }
+
+
+ if (bodyFactory == null)
+ {
+ throw new AMQFrameDecodingException(AMQConstant.COMMAND_INVALID,
+ "Method " + methodId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+ }
+
+
+ return bodyFactory.newInstance(in, size);
+
+
+ }
+
+
+ public int getMaxClassId()
+ {
+ return $specificModel.getMaximumClassId();
+ }
+
+ public int getMaxMethodId(int classId)
+ {
+ return _factories[classId].length - 1;
+ }
+
+
+
+#foreach( $amqpClass in $specificModel.getClassList() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+
+
+#foreach( $amqpMethod in $amqpClass.getMethodList() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body create${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body(
+#foreach( $field in $amqpMethod.FieldList )
+#if( $velocityCount == $amqpMethod.getFieldList().size() )
+ final $field.NativeType $field.Name
+#else
+ final $field.NativeType $field.Name,
+#end
+#end
+ )
+ {
+ return new ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}BodyImpl(
+#foreach( $field in $amqpMethod.FieldList )
+#if( $velocityCount == $amqpMethod.getFieldList().size() )
+ $field.Name
+#else
+ $field.Name,
+#end
+#end
+ );
+ }
+
+#end
+
+#end
+
+
+ public ProtocolVersionMethodConverter getProtocolVersionMethodConverter()
+ {
+ return _protocolVersionConverter;
+ }
+
+
+}
diff --git a/java/common/templates/model/version/ServerMethodDispatcherInterface.vm b/java/common/templates/model/version/ServerMethodDispatcherInterface.vm
new file mode 100644
index 0000000000..db388fcc65
--- /dev/null
+++ b/java/common/templates/model/version/ServerMethodDispatcherInterface.vm
@@ -0,0 +1,55 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/ServerMethodDispatcher_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 $generator - do not modify.
+ * Supported AMQP version:
+ #foreach( $supportedVersion in $model.VersionSet )
+ * $supportedVersion.getMajor()-$supportedVersion.getMinor()
+ #end
+ */
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+
+
+public interface ServerMethodDispatcher_${version.getMajor()}_${version.getMinor()} extends ServerMethodDispatcher
+{
+
+#foreach( $amqpClass in $model.getClasses() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+#foreach( $amqpMethodVersions in $amqpClass.getMethods() )
+#set( $amqpMethod = $amqpMethodVersions.asSingleVersionMethod($amqpMethodVersions.getVersionSet().first()) )
+#if( $amqpMethod.isServerMethod() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ public boolean dispatch${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}(${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}Body body, int channelId) throws AMQException;
+#end
+#end
+#end
+
+} \ No newline at end of file
diff --git a/java/pom.xml b/java/pom.xml
index a4e4de5057..084358a6e8 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -48,6 +48,12 @@ under the License.
<url>scp://people.apache.org/www/people.apache.org/repo/m2-snapshot-repository</url>
</snapshotRepository>
+ <repository>
+ <id>apache.incubating</id>
+ <name>Apache Incubating Repository</name>
+ <url>scp://people.apache.org/www/people.apache.org/repo/m2-incubating-repository</url>
+ </repository>
+
<!--
<snapshotRepository>
<uniqueVersion>true</uniqueVersion>
@@ -147,19 +153,20 @@ under the License.
</properties>
<modules>
- <module>plugins</module>
<module>common</module>
<module>broker</module>
<module>client</module>
+ <module>plugins</module>
<module>systests</module>
<module>perftests</module>
<module>integrationtests</module>
<module>management/eclipse-plugin</module>
<module>client/example</module>
<module>client-java14</module>
+ <module>junit-toolkit</module>
+ <module>junit-toolkit-maven-plugin</module>
</modules>
-
<build>
<resources>
@@ -395,37 +402,14 @@ under the License.
</executions>
</plugin>
+ <!--
<plugin>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit-maven-plugin</artifactId>
- <version>0.6-SNAPSHOT</version>
+ <version>0.7.1-SNAPSHOT</version>
</plugin>
-
- <!-- Disabled as plugin crashes on the systest module.
- Also, the resulting NOTICE file doesn't include all license info due to missing data in dependant poms.
-
- <plugin>
- <artifactId>maven-remote-resources-plugin</artifactId>
- <version>1.0-alpha-5</version>
- <executions>
- <execution>
- <goals>
- <goal>process</goal>
- </goals>
- <configuration>
- <resourceBundles>
- <resourceBundle>org.apache:apache-incubator-disclaimer-resource-bundle:1.1</resourceBundle>
- <resourceBundle>org.apache:apache-jar-resource-bundle:1.2</resourceBundle>
- </resourceBundles>
- <properties>
- <addLicense>true</addLicense>
- <projectName>Apache Qpid</projectName>
- </properties>
- </configuration>
- </execution>
- </executions>
- </plugin-->
- </plugins>
+ -->
+ </plugins>
</pluginManagement>
<defaultGoal>install</defaultGoal>
@@ -502,18 +486,23 @@ under the License.
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
- <version>1.0.0</version>
+ <version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-filter-ssl</artifactId>
- <version>1.0.0</version>
+ <version>1.0.1</version>
</dependency>
<!--
<dependency>
+ <groupId>org.apache.derby</groupId>
+ <artifactId>derby</artifactId>
+ <version>10.3.2.1</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-java5</artifactId>
- <version>1.1.5</version>
+ <version>1.0.1</version>
</dependency>
-->
<dependency>
@@ -537,12 +526,14 @@ under the License.
<scope>test</scope>
</dependency>
+ <!--
<dependency>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit</artifactId>
- <version>0.6-SNAPSHOT</version>
+ <version>0.7.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
+ -->
<!-- Qpid Version Dependencies -->
<dependency>
@@ -580,6 +571,16 @@ under the License.
<artifactId>qpid-mgmt-client</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit-maven-plugin</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
@@ -676,25 +677,28 @@ under the License.
</snapshots>
</repository>
-->
- <repository>
- <id>junit-toolkit.snapshots</id>
- <name>JUnit Toolkit SNAPSHOT Repository</name>
- <url>http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- </repository>
- <repository>
- <id>Codehaus Snapshots</id>
- <url>http://snapshots.repository.codehaus.org/</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- <releases>
- <enabled>false</enabled>
- </releases>
- </repository>
-</repositories>
+ <repository>
+ <id>Codehaus Snapshots</id>
+ <url>http://snapshots.repository.codehaus.org/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ </repository>
+
+ <!--
+ <repository>
+ <id>junittoolkit.snapshots</id>
+ <url>http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ -->
+
+ </repositories>
<pluginRepositories>
<pluginRepository>
@@ -715,6 +719,16 @@ under the License.
</snapshots>
</pluginRepository>
+ <!--
+ <pluginRepository>
+ <id>junittoolkit.plugin.snapshots</id>
+ <url>http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+ -->
+
</pluginRepositories>
<profiles>
diff --git a/java/systests/build.xml b/java/systests/build.xml
index 4a45bfb28e..b84cb9fc32 100644
--- a/java/systests/build.xml
+++ b/java/systests/build.xml
@@ -19,7 +19,7 @@
-
-->
<project name="System Tests" default="build">
- <property name="module.depends" value="client broker common"/>
+ <property name="module.depends" value="client broker common junit-toolkit"/>
<property name="module.dist" value="false"/>
<import file="../module.xml"/>
diff --git a/java/systests/etc/bin/fail.py b/java/systests/etc/bin/fail.py
new file mode 100644
index 0000000000..517f31d075
--- /dev/null
+++ b/java/systests/etc/bin/fail.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 os
+import re
+import datetime
+
+from optparse import OptionParser
+
+BASE_CMD = "mvn -Dskip.python.test=true %s test"
+
+def main():
+ parser = OptionParser()
+ parser.add_option("-t", "--test", dest="test",
+ action="store", type="string",
+ help="run specific tests")
+ parser.add_option("-c", "--continuous", dest="continuous",
+ action="store_true", default=False,
+ help="run tests after failures, don't stop")
+
+
+ (options, args) = parser.parse_args()
+
+ # determine command to run
+ if (options.test != None):
+ cmd = (BASE_CMD % ("-Dtest="+options.test))
+ else:
+ cmd = (BASE_CMD % (""))
+
+ run_forever = options.continuous
+
+
+ failed_runs = []
+ iteration = 0
+ fail_match = re.compile("BUILD SUCCESSFUL")
+ done = False
+
+ while (run_forever or not (len(failed_runs) > 0)):
+ iteration = iteration + 1
+ if (run_forever):
+ extra_text = (", %d failures so far: %s:" % (len(failed_runs), failed_runs))
+ else:
+ extra_text = ""
+ print ("%s Test run %d%s" % (datetime.datetime.today().isoformat(), iteration, extra_text))
+ (child_stdin, child_stdout_and_stderr) = os.popen4(cmd)
+ output = child_stdout_and_stderr.read()
+ child_stdin.close()
+ child_stdout_and_stderr.close()
+ matches = fail_match.search(output)
+ if (matches == None):
+ failed_runs.append(iteration)
+ output_name = ("test-run-%d.out" % (iteration))
+ #write testouput
+ test_output = file(output_name, "w")
+ test_output.write(output)
+ test_output.close()
+ #tar test-output and surefire reports together
+ find_stdout = os.popen("find . -type d -name surefire-reports")
+ surefire_dirs = find_stdout.read().replace('\n', ' ')
+ find_stdout.close()
+ tarcmd = ("tar -zcf test-failures-%d.tar.gz %s %s" % (iteration, output_name, surefire_dirs))
+ tar_stdout = os.popen(tarcmd)
+ tar_output = tar_stdout.read()
+ tar_exitstatus = tar_stdout.close()
+ print ("Something failed! Check %s" % (output_name))
+ if (tar_exitstatus != None):
+ print ("tar exited abornmally, aborting\n %s" % (tar_output))
+ run_forever = False
+
+if __name__ == "__main__":
+ main() \ No newline at end of file
diff --git a/java/systests/pom.xml b/java/systests/pom.xml
index 64af96150a..49f088ec1a 100644
--- a/java/systests/pom.xml
+++ b/java/systests/pom.xml
@@ -1,20 +1,20 @@
<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
@@ -57,16 +57,16 @@
</dependency>
<dependency>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit</artifactId>
</dependency>
<!-- Test Dependencies -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.4.0</version>
- <scope>test</scope>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.0</version>
+ <scope>test</scope>
</dependency>
</dependencies>
@@ -82,7 +82,97 @@
<includes>
<include>**/*Test.class</include>
</includes>
+
+ <systemProperties>
+ <property>
+ <name>example.plugin.target</name>
+ <value>${basedir}/${topDirectoryLocation}/plugins/target</value>
+ </property>
+ <property>
+ <name>QPID_EXAMPLE_HOME</name>
+ <value>${basedir}/${topDirectoryLocation}/broker</value>
+ </property>
+ <property>
+ <name>QPID_HOME</name>
+ <value>${basedir}/${topDirectoryLocation}/broker</value>
+ </property>
+ </systemProperties>
+
+ <excludes>
+ <exclude>**/testcases/ImmediateMessageTest.class</exclude>
+ <exclude>**/testcases/MandatoryMessageTest.class</exclude>
+ <exclude>**/testcases/RollbackTest.class</exclude>
+ <exclude>**/testcases/TTLTest.class</exclude>
+ <exclude>**/testcases/FailoverTest.class</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+
+ <!-- Runs the framework based tests against an in-vm broker. -->
+ <plugin>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit-maven-plugin</artifactId>
+
+ <configuration>
+ <systemproperties>
+ <property>
+ <name>log4j.configuration</name>
+ <value>${log4j.configuration}</value>
+ </property>
+ </systemproperties>
+
+ <testrunner>org.apache.qpid.junit.extensions.TKTestRunner</testrunner>
+
+ <testrunneroptions>
+ <option>-X:decorators "org.apache.qpid.test.framework.qpid.InVMBrokerDecorator:org.apache.qpid.test.framework.qpid.AMQPFeatureDecorator"</option>
+ <!--<option>-d30S</option>-->
+ <option>-o ${basedir}/target/surefire-reports</option>
+ <option>--xml</option>
+ </testrunneroptions>
+
+ <testrunnerproperties>
+ <property>
+ <name>notApplicableAssertion</name>
+ <value>warn</value>
+ </property>
+ </testrunnerproperties>
+
+ <commands>
+ <AMQBrokerManagerMBeanTest>-n AMQBrokerManagerMBeanTest org.apache.qpid.server.AMQBrokerManagerMBeanTest </AMQBrokerManagerMBeanTest>
+ <TxAckTest>-n TxAckTest org.apache.qpid.server.ack.TxAckTest </TxAckTest>
+ <!--<HeadersExchangeTest>-n HeadersExchangeTest org.apache.qpid.server.exchange.HeadersExchangeTest </HeadersExchangeTest>-->
+ <ReturnUnroutableMandatoryMessageTest>-n ReturnUnroutableMandatoryMessageTest org.apache.qpid.server.exchange.ReturnUnroutableMandatoryMessageTest </ReturnUnroutableMandatoryMessageTest>
+ <!--<FailoverMethodTest>-n FailoverMethodTest org.apache.qpid.server.failover.FailoverMethodTest </FailoverMethodTest>-->
+ <DeadlockTest>-n DeadlockTest org.apache.qpid.server.failure.DeadlockTest </DeadlockTest>
+ <!--<PluginTest>-n PluginTest org.apache.qpid.server.plugins.PluginTest </PluginTest>-->
+ <AMQProtocolSessionMBeanTest>-n AMQProtocolSessionMBeanTest org.apache.qpid.server.protocol.AMQProtocolSessionMBeanTest </AMQProtocolSessionMBeanTest>
+ <MaxChannelsTest>-n MaxChannelsTest org.apache.qpid.server.protocol.MaxChannelsTest </MaxChannelsTest>
+ <AckTest>-n AckTest org.apache.qpid.server.queue.AckTest </AckTest>
+ <MessageReturnTest>-n MessageReturnTest org.apache.qpid.server.queue.MessageReturnTest </MessageReturnTest>
+ <QueueDepthWithSelectorTest>-n QueueDepthWithSelectorTest org.apache.qpid.server.queue.QueueDepthWithSelectorTest </QueueDepthWithSelectorTest>
+ <!--<SubscriptionManagerTest>-n SubscriptionManagerTest org.apache.qpid.server.queue.SubscriptionManagerTest </SubscriptionManagerTest>-->
+ <SubscriptionSetTest>-n SubscriptionSetTest org.apache.qpid.server.queue.SubscriptionSetTest </SubscriptionSetTest>
+ <TimeToLiveTest>-n TimeToLiveTest org.apache.qpid.server.queue.TimeToLiveTest </TimeToLiveTest>
+ <TxnBufferTest>-n TxnBufferTest org.apache.qpid.server.txn.TxnBufferTest </TxnBufferTest>
+ <!--<TxnTest>-n TxnTest org.apache.qpid.server.txn.TxnTest </TxnTest>-->
+ <!--QueueBrowserTest>-n QueueBrowserTest org.apache.qpid.test.client.QueueBrowserTest </QueueBrowserTest-->
+
+ <!--<Immediate-Message-Test>-n Immediate-Test -s[1] org.apache.qpid.test.testcases.ImmediateMessageTest</Immediate-Message-Test>-->
+ <!--<Mandatory-Message-Test>-n Mandatory-Test -s[1] org.apache.qpid.test.testcases.MandatoryMessageTest</Mandatory-Message-Test>-->
+ <!--<Rollback-Test>-n Rollback-Test -s[1] org.apache.qpid.test.testcases.RollbackTest</Rollback-Test>-->
+ </commands>
+
</configuration>
+
+ <executions>
+ <execution>
+ <id>framework_tests</id>
+ <phase>test</phase>
+ <goals>
+ <goal>tktest</goal>
+ </goals>
+ </execution>
+ </executions>
</plugin>
</plugins>
@@ -109,3 +199,4 @@
</build>
</project>
+
diff --git a/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java b/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
index fd28c2f77e..7fd53a64fd 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
@@ -28,6 +28,8 @@ import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.RequiredDeliveryException;
import org.apache.qpid.server.store.MemoryMessageStore;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.store.TestableMemoryMessageStore;
import org.apache.qpid.server.txn.NonTransactionalContext;
@@ -101,8 +103,8 @@ public class TxAckTest extends TestCase
{
TransactionalContext txnContext = new NonTransactionalContext(new MemoryMessageStore(),
_storeContext, null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
for (int i = 0; i < messageCount; i++)
{
long deliveryTag = i + 1;
@@ -115,6 +117,11 @@ public class TxAckTest extends TestCase
return null;
}
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public boolean isImmediate()
{
return false;
@@ -132,7 +139,7 @@ public class TxAckTest extends TestCase
};
TestMessage message = new TestMessage(deliveryTag, i, info, txnContext);
- _map.add(deliveryTag, new UnacknowledgedMessage(null, message, null, deliveryTag));
+ _map.add(deliveryTag, new UnacknowledgedMessage(new QueueEntry(null,message), null, deliveryTag));
}
_acked = acked;
_unacked = unacked;
@@ -149,7 +156,7 @@ public class TxAckTest extends TestCase
{
UnacknowledgedMessage u = _map.get(tag);
assertTrue("Message not found for tag " + tag, u != null);
- ((TestMessage) u.message).assertCountEquals(expected);
+ ((TestMessage) u.getMessage()).assertCountEquals(expected);
}
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
index 4419768416..0968f0c468 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
@@ -27,6 +27,7 @@ import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.queue.AMQMessage;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.MessageHandleFactory;
+import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.MemoryMessageStore;
@@ -250,9 +251,9 @@ public class AbstractHeadersExchangeTestBase extends TestCase
* @param deliverFirst
* @throws AMQException
*/
- public void process(StoreContext context, AMQMessage msg, boolean deliverFirst) throws AMQException
+ public void process(StoreContext context, QueueEntry msg, boolean deliverFirst) throws AMQException
{
- messages.add(new HeadersExchangeTest.Message(msg));
+ messages.add(new HeadersExchangeTest.Message(msg.getMessage()));
}
}
@@ -267,8 +268,8 @@ public class AbstractHeadersExchangeTestBase extends TestCase
private static TransactionalContext _txnContext = new NonTransactionalContext(_messageStore, _storeContext,
null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
Message(String id, String... headers) throws AMQException
{
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 b584c8c80b..3a3b7f92dd 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
@@ -21,8 +21,7 @@
package org.apache.qpid.server.exchange;
import org.apache.qpid.jms.Session;
-
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
/**
* MessagingTestConfigProperties defines a set of property names and default values for specifying a messaging topology,
diff --git a/java/systests/src/main/java/org/apache/qpid/server/failover/FailoverMethodTest.java b/java/systests/src/main/java/org/apache/qpid/server/failover/FailoverMethodTest.java
new file mode 100644
index 0000000000..9b2a3a6e28
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/server/failover/FailoverMethodTest.java
@@ -0,0 +1,136 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.failover;
+
+import junit.framework.TestCase;
+import org.apache.qpid.AMQDisconnectedException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQConnectionURL;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException;
+import org.apache.qpid.url.URLSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import java.util.concurrent.CountDownLatch;
+
+public class FailoverMethodTest extends TestCase implements ExceptionListener
+{
+ private CountDownLatch _failoverComplete = new CountDownLatch(1);
+
+ public void setUp() throws AMQVMBrokerCreationException
+ {
+ TransportConnection.createVMBroker(1);
+ }
+
+ public void tearDown() throws AMQVMBrokerCreationException
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+
+ /**
+ * Test that the round robin method has the correct delays.
+ * The first connection to vm://:1 will work but the localhost connection should fail but the duration it takes
+ * to report the failure is what is being tested.
+ *
+ * @throws URLSyntaxException
+ * @throws InterruptedException
+ * @throws JMSException
+ */
+ public void testFailoverRoundRobinDelay() throws URLSyntaxException, InterruptedException, JMSException
+ {
+ String connectionString = "amqp://guest:guest@/test?brokerlist='vm://:1;tcp://localhost:5670?connectdelay='2000',retries='3''";
+
+ AMQConnectionURL url = new AMQConnectionURL(connectionString);
+
+ try
+ {
+ long start = System.currentTimeMillis();
+ AMQConnection connection = new AMQConnection(url, null);
+
+ connection.setExceptionListener(this);
+
+ TransportConnection.killAllVMBrokers();
+
+ _failoverComplete.await();
+
+ long end = System.currentTimeMillis();
+
+ //Failover should take less that 10 seconds.
+ // This is calculated by vm://:1 two retries left after initial connection
+ // localhost get three retries so (6s) so 10s in total for connection dropping
+ assertTrue("Failover took less than 6 seconds:"+(end - start), (end - start) > 6000);
+ // The sleep method is not 100% accurate under windows so with 5 sleeps and a 10ms accuracy then there is
+ // the potential for the tests to finish in 500ms sooner than the predicted 10s.
+
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ public void testFailoverSingleDelay() throws URLSyntaxException, AMQVMBrokerCreationException, InterruptedException, JMSException
+ {
+ String connectionString = "amqp://guest:guest@/test?brokerlist='vm://:1?connectdelay='2000',retries='3''";
+
+ AMQConnectionURL url = new AMQConnectionURL(connectionString);
+
+ try
+ {
+ long start = System.currentTimeMillis();
+ AMQConnection connection = new AMQConnection(url, null);
+
+ connection.setExceptionListener(this);
+
+ TransportConnection.killAllVMBrokers();
+
+ _failoverComplete.await();
+
+ long end = System.currentTimeMillis();
+
+ //Failover should take less that 10 seconds.
+ // This is calculated by vm://:1 two retries left after initial connection
+ // so 4s in total for connection dropping
+
+ assertTrue("Failover took less than 3.7 seconds", (end - start) > 3700);
+ // The sleep method is not 100% accurate under windows so with 3 sleeps and a 10ms accuracy then there is
+ // the potential for the tests to finish in 300ms sooner than the predicted 4s.
+
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+
+ public void onException(JMSException e)
+ {
+ if (e.getLinkedException() instanceof AMQDisconnectedException)
+ {
+ _failoverComplete.countDown();
+ }
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/plugins/PluginTest.java b/java/systests/src/main/java/org/apache/qpid/server/plugins/PluginTest.java
new file mode 100644
index 0000000000..0762a7a561
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/server/plugins/PluginTest.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.plugins;
+
+import java.util.Map;
+
+import org.apache.qpid.server.exchange.ExchangeType;
+
+import junit.framework.TestCase;
+
+public class PluginTest extends TestCase
+{
+
+ private static final String TEST_EXCHANGE_CLASS = "org.apache.qpid.extras.exchanges.example.TestExchangeType";
+ private static final String PLUGIN_DIRECTORY = System.getProperty("example.plugin.target");
+
+ public void testLoadExchanges() throws Exception
+ {
+ PluginManager manager = new PluginManager(PLUGIN_DIRECTORY);
+ Map<String, ExchangeType<?>> exchanges = manager.getExchanges();
+ assertNotNull("No exchanges found in "+PLUGIN_DIRECTORY, exchanges);
+ assertEquals("Wrong number of exchanges found in "+PLUGIN_DIRECTORY,
+ 2, exchanges.size());
+ assertNotNull("Wrong exchange found in "+PLUGIN_DIRECTORY,
+ exchanges.get(TEST_EXCHANGE_CLASS));
+ }
+
+ public void testNoExchanges() throws Exception
+ {
+ PluginManager manager = new PluginManager("/path/to/nowhere");
+ Map<String, ExchangeType<?>> exchanges = manager.getExchanges();
+ assertNull("Exchanges found", exchanges);
+ }
+
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
index 66cd1cc7da..e4555e020e 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
@@ -21,6 +21,9 @@
package org.apache.qpid.server.protocol;
import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+
import org.apache.qpid.AMQException;
import org.apache.qpid.codec.AMQCodecFactory;
import org.apache.qpid.framing.AMQShortString;
@@ -28,10 +31,9 @@ import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
-import org.apache.qpid.server.store.MemoryMessageStore;
import org.apache.qpid.server.store.MessageStore;
-import org.apache.qpid.server.txn.MemoryTransactionManager;
-import org.apache.qpid.server.txn.TransactionManager;
+import org.apache.qpid.server.store.SkeletonMessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
import javax.management.JMException;
@@ -41,8 +43,10 @@ import javax.management.JMException;
*/
public class AMQProtocolSessionMBeanTest extends TestCase
{
- private MessageStore _messageStore = new MemoryMessageStore();
- private TransactionManager _txm = new MemoryTransactionManager();
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(AMQProtocolSessionMBeanTest.class);
+
+ private MessageStore _messageStore = new SkeletonMessageStore();
private AMQMinaProtocolSession _protocolSession;
private AMQChannel _channel;
private AMQProtocolSessionMBean _mbean;
@@ -57,7 +61,8 @@ public class AMQProtocolSessionMBeanTest extends TestCase
new AMQShortString("test"),
true,
_protocolSession.getVirtualHost());
- AMQChannel channel = new AMQChannel(_protocolSession, 2, _txm, _messageStore, null);
+ AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore);
+
channel.setDefaultQueue(queue);
_protocolSession.addChannel(channel);
channelCount = _mbean.channels().size();
@@ -68,7 +73,7 @@ public class AMQProtocolSessionMBeanTest extends TestCase
assertTrue(_mbean.getMaximumNumberOfChannels() == 1000L);
// check APIs
- AMQChannel channel3 = new AMQChannel(_protocolSession, 3, _txm, _messageStore, null);
+ AMQChannel channel3 = new AMQChannel(_protocolSession, 3, _messageStore);
channel3.setLocalTransactional();
_protocolSession.addChannel(channel3);
_mbean.rollbackTransactions(2);
@@ -84,23 +89,23 @@ public class AMQProtocolSessionMBeanTest extends TestCase
}
catch (JMException ex)
{
- System.out.println("expected exception is thrown :" + ex.getMessage());
+ log.debug("expected exception is thrown :" + ex.getMessage());
}
// check if closing of session works
- _protocolSession.addChannel(new AMQChannel(_protocolSession, 5, _txm, _messageStore, null));
+ _protocolSession.addChannel(new AMQChannel(_protocolSession, 5, _messageStore));
_mbean.closeConnection();
try
{
channelCount = _mbean.channels().size();
assertTrue(channelCount == 0);
// session is now closed so adding another channel should throw an exception
- _protocolSession.addChannel(new AMQChannel(_protocolSession, 6, _txm, _messageStore, null));
+ _protocolSession.addChannel(new AMQChannel(_protocolSession, 6, _messageStore));
fail();
}
catch (AMQException ex)
{
- System.out.println("expected exception is thrown :" + ex.getMessage());
+ log.debug("expected exception is thrown :" + ex.getMessage());
}
}
@@ -110,12 +115,11 @@ public class AMQProtocolSessionMBeanTest extends TestCase
super.setUp();
IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
- _protocolSession = new AMQMinaProtocolSession(new MockIoSession(),
- appRegistry.getVirtualHostRegistry(),
- new AMQCodecFactory(true),
- null);
+ _protocolSession =
+ new AMQMinaProtocolSession(new MockIoSession(), appRegistry.getVirtualHostRegistry(), new AMQCodecFactory(true),
+ null);
_protocolSession.setVirtualHost(appRegistry.getVirtualHostRegistry().getVirtualHost("test"));
- _channel = new AMQChannel(_protocolSession, 1, _txm, _messageStore, null);
+ _channel = new AMQChannel(_protocolSession, 1, _messageStore);
_protocolSession.addChannel(_channel);
_mbean = (AMQProtocolSessionMBean) _protocolSession.getManagedObject();
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java b/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java
index c1d7b0f598..62f5e0c6bf 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java
@@ -27,6 +27,8 @@ import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
/** Test class to test MBean operations for AMQMinaProtocolSession. */
public class MaxChannelsTest extends TestCase
@@ -55,7 +57,7 @@ public class MaxChannelsTest extends TestCase
{
for (long currentChannel = 0L; currentChannel < maxChannels; currentChannel++)
{
- _protocolSession.addChannel(new AMQChannel(_protocolSession, (int) currentChannel, null, null, null));
+ _protocolSession.addChannel(new AMQChannel(_protocolSession, (int) currentChannel, null));
}
}
catch (AMQException e)
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
index 65ac12463f..2416442b10 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
@@ -34,7 +34,6 @@ import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.store.TestableMemoryMessageStore;
-import org.apache.qpid.server.txn.MemoryTransactionManager;
import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.server.txn.TransactionalContext;
import org.apache.qpid.server.util.NullApplicationRegistry;
@@ -56,8 +55,6 @@ public class AckTest extends TestCase
private TestableMemoryMessageStore _messageStore;
- private MemoryTransactionManager _txm;
-
private StoreContext _storeContext = new StoreContext();
private AMQChannel _channel;
@@ -77,9 +74,8 @@ public class AckTest extends TestCase
{
super.setUp();
_messageStore = new TestableMemoryMessageStore();
- _txm = new MemoryTransactionManager();
_protocolSession = new MockProtocolSession(_messageStore);
- _channel = new AMQChannel(_protocolSession, 5, _txm, _messageStore, null/*dont need exchange registry*/);
+ _channel = new AMQChannel(_protocolSession, 5, _messageStore);
_protocolSession.addChannel(_channel);
_subscriptionManager = new SubscriptionSet();
@@ -94,8 +90,8 @@ public class AckTest extends TestCase
private void publishMessages(int count, boolean persistent) throws AMQException
{
TransactionalContext txnContext = new NonTransactionalContext(_messageStore, _storeContext, null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
MessageHandleFactory factory = new MessageHandleFactory();
for (int i = 1; i <= count; i++)
{
@@ -109,6 +105,11 @@ public class AckTest extends TestCase
return new AMQShortString("someExchange");
}
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public boolean isImmediate()
{
return false;
@@ -144,7 +145,7 @@ public class AckTest extends TestCase
msg.incrementReference();
msg.routingComplete(_messageStore, _storeContext, factory);
// we manually send the message to the subscription
- _subscription.send(msg, _queue);
+ _subscription.send(new QueueEntry(_queue,msg), _queue);
}
}
@@ -172,7 +173,7 @@ public class AckTest extends TestCase
assertTrue(deliveryTag == i);
i++;
UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
- assertTrue(unackedMsg.queue == _queue);
+ assertTrue(unackedMsg.getQueue() == _queue);
}
assertTrue(map.size() == msgCount);
@@ -219,7 +220,7 @@ public class AckTest extends TestCase
{
assertTrue(deliveryTag == i);
UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
- assertTrue(unackedMsg.queue == _queue);
+ assertTrue(unackedMsg.getQueue() == _queue);
// 5 is the delivery tag of the message that *should* be removed
if (++i == 5)
{
@@ -248,7 +249,7 @@ public class AckTest extends TestCase
{
assertTrue(deliveryTag == i + 5);
UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
- assertTrue(unackedMsg.queue == _queue);
+ assertTrue(unackedMsg.getQueue() == _queue);
++i;
}
}
@@ -272,7 +273,7 @@ public class AckTest extends TestCase
{
assertTrue(deliveryTag == i + 5);
UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
- assertTrue(unackedMsg.queue == _queue);
+ assertTrue(unackedMsg.getQueue() == _queue);
++i;
}
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java b/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java
index 6a70fea711..4f92cc94b7 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java
@@ -42,9 +42,9 @@ public class ConcurrencyTestDisabled extends MessageTestHelper
private final List<SubscriptionTestHelper> _subscribers = new ArrayList<SubscriptionTestHelper>();
private final Set<Subscription> _active = new HashSet<Subscription>();
- private final List<AMQMessage> _messages = new ArrayList<AMQMessage>();
+ private final List<QueueEntry> _messages = new ArrayList<QueueEntry>();
private int next = 0;//index to next message to send
- private final List<AMQMessage> _received = Collections.synchronizedList(new ArrayList<AMQMessage>());
+ private final List<QueueEntry> _received = Collections.synchronizedList(new ArrayList<QueueEntry>());
private final Executor _executor = new OnCurrentThreadExecutor();
private final List<Thread> _threads = new ArrayList<Thread>();
@@ -159,7 +159,7 @@ public class ConcurrencyTestDisabled extends MessageTestHelper
}
}
- private AMQMessage nextMessage()
+ private QueueEntry nextMessage()
{
synchronized (_messages)
{
@@ -191,7 +191,7 @@ public class ConcurrencyTestDisabled extends MessageTestHelper
{
void doRun() throws Throwable
{
- AMQMessage msg = nextMessage();
+ QueueEntry msg = nextMessage();
if (msg != null)
{
_deliveryMgr.deliver(null, new AMQShortString(toString()), msg, false);
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java
index dc5a6d3cf6..b33259cfba 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java
@@ -40,7 +40,7 @@ abstract public class DeliveryManagerTest extends MessageTestHelper
public void testStartInQueueingMode() throws AMQException
{
- AMQMessage[] messages = new AMQMessage[10];
+ QueueEntry[] messages = new QueueEntry[10];
for (int i = 0; i < messages.length; i++)
{
messages[i] = message();
@@ -85,7 +85,7 @@ abstract public class DeliveryManagerTest extends MessageTestHelper
public void testStartInDirectMode() throws AMQException
{
- AMQMessage[] messages = new AMQMessage[10];
+ QueueEntry[] messages = new QueueEntry[10];
for (int i = 0; i < messages.length; i++)
{
messages[i] = message();
@@ -132,7 +132,7 @@ abstract public class DeliveryManagerTest extends MessageTestHelper
{
try
{
- AMQMessage msg = message(true);
+ QueueEntry msg = message(true);
_mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, msg, false);
msg.checkDeliveredToConsumer();
fail("expected exception did not occur");
@@ -154,7 +154,7 @@ abstract public class DeliveryManagerTest extends MessageTestHelper
SubscriptionTestHelper s = new SubscriptionTestHelper("A");
_subscriptions.addSubscriber(s);
s.setSuspended(true);
- AMQMessage msg = message(true);
+ QueueEntry msg = message(true);
_mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, msg, false);
msg.checkDeliveredToConsumer();
fail("expected exception did not occur");
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java b/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
index 0d4e3e748b..114c8cac32 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
@@ -36,7 +36,6 @@ import org.apache.qpid.AMQException;
import junit.framework.TestCase;
import java.util.LinkedList;
-import java.util.HashSet;
class MessageTestHelper extends TestCase
{
@@ -45,20 +44,20 @@ class MessageTestHelper extends TestCase
private final StoreContext _storeContext = new StoreContext();
private final TransactionalContext _txnContext = new NonTransactionalContext(_messageStore, _storeContext, null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
MessageTestHelper() throws Exception
{
ApplicationRegistry.initialise(new NullApplicationRegistry());
}
- AMQMessage message() throws AMQException
+ QueueEntry message() throws AMQException
{
return message(false);
}
- AMQMessage message(final boolean immediate) throws AMQException
+ QueueEntry message(final boolean immediate) throws AMQException
{
MessagePublishInfo publish = new MessagePublishInfo()
{
@@ -68,6 +67,11 @@ class MessageTestHelper extends TestCase
return null;
}
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public boolean isImmediate()
{
return immediate;
@@ -84,8 +88,8 @@ class MessageTestHelper extends TestCase
}
};
- return new AMQMessage(_messageStore.getNewMessageId(), publish, _txnContext,
- new ContentHeaderBody());
+ return new QueueEntry(null,new AMQMessage(_messageStore.getNewMessageId(), publish, _txnContext,
+ new ContentHeaderBody()));
}
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java b/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java
index 0ad6502755..cf986e7803 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java
@@ -21,10 +21,7 @@
package org.apache.qpid.server.queue;
import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQDataBlock;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.VersionSpecificRegistry;
+import org.apache.qpid.framing.*;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.output.ProtocolOutputConverter;
import org.apache.qpid.server.output.ProtocolOutputConverterRegistry;
@@ -188,16 +185,53 @@ public class MockProtocolSession implements AMQProtocolSession
return null; //To change body of implemented methods use File | Settings | File Templates.
}
+ public MethodRegistry getMethodRegistry()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void methodFrameReceived(int channelId, AMQMethodBody body)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void contentHeaderReceived(int channelId, ContentHeaderBody body)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void contentBodyReceived(int channelId, ContentBody body)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void heartbeatBodyReceived(int channelId, HeartbeatBody body)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public MethodDispatcher getMethodDispatcher()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public byte getProtocolMajorVersion()
{
- return 8; //To change body of implemented methods use File | Settings | File Templates.
+ return getProtocolVersion().getMajorVersion();
}
public byte getProtocolMinorVersion()
{
- return 0; //To change body of implemented methods use File | Settings | File Templates.
+ return getProtocolVersion().getMinorVersion();
}
+
+ public ProtocolVersion getProtocolVersion()
+ {
+ return ProtocolVersion.getLatestSupportedVersion(); //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
public VersionSpecificRegistry getRegistry()
{
return null; //To change body of implemented methods use File | Settings | File Templates.
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
index fe947ef3bc..1fa70a08d4 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
@@ -28,13 +28,13 @@ import java.util.Queue;
public class SubscriptionTestHelper implements Subscription
{
- private final List<AMQMessage> messages;
+ private final List<QueueEntry> messages;
private final Object key;
private boolean isSuspended;
public SubscriptionTestHelper(Object key)
{
- this(key, new ArrayList<AMQMessage>());
+ this(key, new ArrayList<QueueEntry>());
}
public SubscriptionTestHelper(final Object key, final boolean isSuspended)
@@ -43,18 +43,18 @@ public class SubscriptionTestHelper implements Subscription
setSuspended(isSuspended);
}
- SubscriptionTestHelper(Object key, List<AMQMessage> messages)
+ SubscriptionTestHelper(Object key, List<QueueEntry> messages)
{
this.key = key;
this.messages = messages;
}
- List<AMQMessage> getMessages()
+ List<QueueEntry> getMessages()
{
return messages;
}
- public void send(AMQMessage msg, AMQQueue queue)
+ public void send(QueueEntry msg, AMQQueue queue)
{
messages.add(msg);
}
@@ -69,12 +69,12 @@ public class SubscriptionTestHelper implements Subscription
return isSuspended;
}
- public boolean wouldSuspend(AMQMessage msg)
+ public boolean wouldSuspend(QueueEntry msg)
{
return isSuspended;
}
- public void addToResendQueue(AMQMessage msg)
+ public void addToResendQueue(QueueEntry msg)
{
//no-op
}
@@ -88,6 +88,11 @@ public class SubscriptionTestHelper implements Subscription
{
return null;
}
+
+ public void start()
+ {
+ //no-op
+ }
public void queueDeleted(AMQQueue queue)
{
@@ -98,36 +103,31 @@ public class SubscriptionTestHelper implements Subscription
return false;
}
- public boolean hasInterest(AMQMessage msg)
+ public boolean hasInterest(QueueEntry msg)
{
return true;
}
- public Queue<AMQMessage> getPreDeliveryQueue()
+ public Queue<QueueEntry> getPreDeliveryQueue()
{
return null;
}
- public Queue<AMQMessage> getResendQueue()
+ public Queue<QueueEntry> getResendQueue()
{
return null;
}
- public Queue<AMQMessage> getNextQueue(Queue<AMQMessage> messages)
+ public Queue<QueueEntry> getNextQueue(Queue<QueueEntry> messages)
{
return messages;
}
- public void enqueueForPreDelivery(AMQMessage msg, boolean deliverFirst)
+ public void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst)
{
//no-op
}
- public boolean isAutoClose()
- {
- return false;
- }
-
public void close()
{
//no-op
@@ -157,4 +157,5 @@ public class SubscriptionTestHelper implements Subscription
{
return key.toString();
}
+
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java
index 06956ba52f..a803bf7da5 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java
@@ -25,7 +25,11 @@ import junit.framework.TestCase;
import junit.framework.Assert;
import org.apache.qpid.client.transport.TransportConnection;
import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.AMQException;
import org.apache.log4j.Logger;
import javax.jms.JMSException;
@@ -38,6 +42,7 @@ import javax.jms.Connection;
import javax.jms.Message;
import javax.naming.spi.InitialContextFactory;
import javax.naming.Context;
+import javax.naming.NamingException;
import java.util.Hashtable;
@@ -53,21 +58,37 @@ public class TimeToLiveTest extends TestCase
private final long TIME_TO_LIVE = 1000L;
- Context _context;
-
- private Connection _clientConnection, _producerConnection;
-
- private MessageConsumer _consumer;
- MessageProducer _producer;
- Session _clientSession, _producerSession;
private static final int MSG_COUNT = 50;
+ private static final long SERVER_TTL_TIMEOUT = 60000L;
protected void setUp() throws Exception
{
- if (BROKER.startsWith("vm://"))
+ super.setUp();
+
+ if (usingInVMBroker())
{
TransportConnection.createVMBroker(1);
}
+
+
+ }
+
+ private boolean usingInVMBroker()
+ {
+ return BROKER.startsWith("vm://");
+ }
+
+ protected void tearDown() throws Exception
+ {
+ if (usingInVMBroker())
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+ super.tearDown();
+ }
+
+ public void testPassiveTTL() throws JMSException, NamingException
+ {
InitialContextFactory factory = new PropertiesFileInitialContextFactory();
Hashtable<String, String> env = new Hashtable<String, String>();
@@ -75,56 +96,40 @@ public class TimeToLiveTest extends TestCase
env.put("connectionfactory.connection", "amqp://guest:guest@TTL_TEST_ID" + VHOST + "?brokerlist='" + BROKER + "'");
env.put("queue.queue", QUEUE);
- _context = factory.getInitialContext(env);
+ Context context = factory.getInitialContext(env);
- Queue queue = (Queue) _context.lookup("queue");
+ Queue queue = (Queue) context.lookup("queue");
//Create Client 1
- _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ Connection clientConnection = ((ConnectionFactory) context.lookup("connection")).createConnection();
- _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- _consumer = _clientSession.createConsumer(queue);
+ MessageConsumer consumer = clientSession.createConsumer(queue);
//Create Producer
- _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ Connection producerConnection = ((ConnectionFactory) context.lookup("connection")).createConnection();
- _producerConnection.start();
+ producerConnection.start();
- _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- _producer = _producerSession.createProducer(queue);
- }
+ MessageProducer producer = producerSession.createProducer(queue);
- protected void tearDown() throws Exception
- {
- _clientConnection.close();
-
- _producerConnection.close();
- super.tearDown();
-
- if (BROKER.startsWith("vm://"))
- {
- TransportConnection.killAllVMBrokers();
- }
- }
-
- public void test() throws JMSException
- {
//Set TTL
int msg = 0;
- _producer.send(nextMessage(String.valueOf(msg), true));
+ producer.send(nextMessage(String.valueOf(msg), true, producerSession, producer));
- _producer.setTimeToLive(TIME_TO_LIVE);
+ producer.setTimeToLive(TIME_TO_LIVE);
for (; msg < MSG_COUNT - 2; msg++)
{
- _producer.send(nextMessage(String.valueOf(msg), false));
+ producer.send(nextMessage(String.valueOf(msg), false, producerSession, producer));
}
//Reset TTL
- _producer.setTimeToLive(0L);
- _producer.send(nextMessage(String.valueOf(msg), false));
+ producer.setTimeToLive(0L);
+ producer.send(nextMessage(String.valueOf(msg), false, producerSession, producer));
try
{
@@ -136,31 +141,71 @@ public class TimeToLiveTest extends TestCase
}
- _clientConnection.start();
+ clientConnection.start();
//Receive Message 0
- Message received = _consumer.receive(100);
+ Message received = consumer.receive(1000);
Assert.assertNotNull("First message not received", received);
Assert.assertTrue("First message doesn't have first set.", received.getBooleanProperty("first"));
Assert.assertEquals("First message has incorrect TTL.", 0L, received.getLongProperty("TTL"));
- received = _consumer.receive(100);
+ received = consumer.receive(1000);
Assert.assertNotNull("Final message not received", received);
Assert.assertFalse("Final message has first set.", received.getBooleanProperty("first"));
Assert.assertEquals("Final message has incorrect TTL.", 0L, received.getLongProperty("TTL"));
- received = _consumer.receive(100);
+ received = consumer.receive(1000);
Assert.assertNull("More messages received", received);
+
+ clientConnection.close();
+
+ producerConnection.close();
}
- private Message nextMessage(String msg, boolean first) throws JMSException
+ private Message nextMessage(String msg, boolean first, Session producerSession, MessageProducer producer) throws JMSException
{
- Message send = _producerSession.createTextMessage("Message " + msg);
+ Message send = producerSession.createTextMessage("Message " + msg);
send.setBooleanProperty("first", first);
- send.setLongProperty("TTL", _producer.getTimeToLive());
+ send.setLongProperty("TTL", producer.getTimeToLive());
return send;
}
+ /**
+ * Tests the expired messages get actively deleted even on queues which have no consumers
+ */
+ public void testActiveTTL() throws URLSyntaxException, AMQException, JMSException, InterruptedException
+ {
+ Connection producerConnection = new AMQConnection(BROKER,"guest","guest","activeTTLtest","test");
+ AMQSession producerSession = (AMQSession) producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = producerSession.createTemporaryQueue();
+ producerSession.declareAndBind((AMQDestination) queue);
+ MessageProducer producer = producerSession.createProducer(queue);
+ producer.setTimeToLive(1000L);
+
+ // send Messages
+ for(int i = 0; i < MSG_COUNT; i++)
+ {
+ producer.send(producerSession.createTextMessage("Message: "+i));
+ }
+ long failureTime = System.currentTimeMillis() + 2*SERVER_TTL_TIMEOUT;
+
+ // check Queue depth for up to TIMEOUT seconds
+ long messageCount;
+
+ do
+ {
+ Thread.sleep(100);
+ messageCount = producerSession.getQueueDepth((AMQDestination) queue);
+ }
+ while(messageCount > 0L && System.currentTimeMillis() < failureTime);
+
+ assertEquals("Messages not automatically expired: ", 0L, messageCount);
+
+ producer.close();
+ producerSession.close();
+ producerConnection.close();
+ }
+
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java b/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java
new file mode 100644
index 0000000000..9ba0f6024c
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java
@@ -0,0 +1,607 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.server.security.acl;
+
+import junit.framework.TestCase;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.client.*;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.*;
+import javax.jms.IllegalStateException;
+import java.io.File;
+
+
+public class SimpleACLTest extends TestCase implements ConnectionListener
+{
+ private String BROKER = "vm://:1";//"tcp://localhost:5672";
+
+ public void setUp() throws Exception
+ {
+ // Initialise ACLs.
+ final String QpidExampleHome = System.getProperty("QPID_EXAMPLE_HOME");
+ final File defaultaclConfigFile = new File(QpidExampleHome, "etc/acl.config.xml");
+
+ if (!defaultaclConfigFile.exists())
+ {
+ System.err.println("Configuration file not found:" + defaultaclConfigFile);
+ fail("Configuration file not found:" + defaultaclConfigFile);
+ }
+
+ if (System.getProperty("QPID_HOME") == null)
+ {
+ fail("QPID_HOME not set");
+ }
+
+ ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(defaultaclConfigFile);
+
+ ApplicationRegistry.initialise(config, 1);
+
+ TransportConnection.createVMBroker(1);
+ }
+
+ public void tearDown()
+ {
+ ApplicationRegistry.remove(1);
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public String createConnectionString(String username, String password, String broker)
+ {
+
+ return "amqp://" + username + ":" + password + "@clientid/test?brokerlist='" + broker + "'";
+ }
+
+ public void testAccessAuthorized() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED);
+
+ conn.start();
+
+ //Do something to show connection is active.
+ sesh.rollback();
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Connection was not created due to:" + e.getMessage());
+ }
+ }
+
+ public void testAccessNoRights() throws URLSyntaxException, JMSException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("guest", "guest", BROKER));
+
+ //Attempt to do do things to test connection.
+ Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED);
+ conn.start();
+ sesh.rollback();
+
+ conn.close();
+ fail("Connection was created.");
+ }
+ catch (AMQException amqe)
+ {
+ if (amqe.getCause().getClass() == Exception.class)
+ {
+ System.err.println("QPID-594 : WARNING RACE CONDITION. Unable to determine cause of Connection Failure.");
+ return;
+ }
+ assertEquals("Linked Exception Incorrect", JMSException.class, amqe.getCause().getClass());
+ Exception linked = ((JMSException) amqe.getCause()).getLinkedException();
+ assertEquals("Exception was wrong type", AMQAuthenticationException.class, linked.getClass());
+ assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) linked).getErrorCode().getCode());
+ }
+ }
+
+ public void testClientConsumeFromTempQueueValid() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ sesh.createConsumer(sesh.createTemporaryQueue());
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Test failed due to:" + e.getMessage());
+ }
+ }
+
+ public void testClientConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ //Prevent Failover
+ ((AMQConnection) conn).setConnectionListener(this);
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ sesh.createConsumer(sesh.createQueue("IllegalQueue"));
+ fail("Test failed as consumer was created.");
+ //conn will be automatically closed
+ }
+ catch (JMSException e)
+ {
+ Throwable cause = e.getLinkedException();
+
+ assertNotNull("There was no liked exception", cause);
+ assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass());
+ assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode());
+ }
+ }
+
+ public void testClientCreateTemporaryQueue() throws JMSException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ //Create Temporary Queue - can't use the createTempQueue as QueueName is null.
+ ((AMQSession) sesh).createQueue(new AMQShortString("doesnt_matter_as_autodelete_means_tmp"),
+ true, false, false);
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Test failed due to:" + e.getMessage());
+ }
+ }
+
+ public void testClientCreateNamedQueue() throws JMSException, URLSyntaxException, AMQException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ //Create a Named Queue
+ ((AMQSession) sesh).createQueue(new AMQShortString("IllegalQueue"), false, false, false);
+
+ fail("Test failed as Queue creation succeded.");
+ //conn will be automatically closed
+ }
+ catch (AMQAuthenticationException amqe)
+ {
+ assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) amqe).getErrorCode().getCode());
+ }
+ }
+
+ public void testClientPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ ((AMQConnection) conn).setConnectionListener(this);
+
+ Session sesh = conn.createSession(true, Session.SESSION_TRANSACTED);
+
+ conn.start();
+
+ MessageProducer sender = sesh.createProducer(sesh.createQueue("example.RequestQueue"));
+
+ sender.send(sesh.createTextMessage("test"));
+
+ //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker.
+ sesh.commit();
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Test publish failed:" + e);
+ }
+ }
+
+ public void testClientPublishValidQueueSuccess() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ ((AMQConnection) conn).setConnectionListener(this);
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ MessageProducer sender = ((AMQSession) sesh).createProducer(null);
+
+ Queue queue = sesh.createQueue("example.RequestQueue");
+
+ // Send a message that we will wait to be sent, this should give the broker time to process the msg
+ // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not
+ // queue existence.
+ ((org.apache.qpid.jms.MessageProducer) sender).send(queue, sesh.createTextMessage("test"),
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Test publish failed:" + e);
+ }
+ }
+
+ public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ ((AMQConnection) conn).setConnectionListener(this);
+
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ MessageProducer sender = ((AMQSession) session).createProducer(null);
+
+ Queue queue = session.createQueue("Invalid");
+
+ // Send a message that we will wait to be sent, this should give the broker time to close the connection
+ // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not
+ // queue existence.
+ ((org.apache.qpid.jms.MessageProducer) sender).send(queue, session.createTextMessage("test"),
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+
+ // Test the connection with a valid consumer
+ // This may fail as the session may be closed before the queue or the consumer created.
+ session.createConsumer(session.createTemporaryQueue()).close();
+
+ //Connection should now be closed and will throw the exception caused by the above send
+ conn.close();
+
+ fail("Close is not expected to succeed.");
+ }
+ catch (IllegalStateException ise)
+ {
+ System.err.println("QPID-826 : WARNING : Unable to determine cause of failure due to closure as we don't " +
+ "record it for reporting after connection closed asynchronously");
+ }
+ catch (JMSException e)
+ {
+ Throwable cause = e.getLinkedException();
+ assertEquals("Incorrect exception", AMQAuthenticationException.class, cause.getClass());
+ assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode());
+ }
+ }
+
+ public void testServerConsumeFromNamedQueueValid() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("server", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ sesh.createConsumer(sesh.createQueue("example.RequestQueue"));
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Test failed due to:" + e.getMessage());
+ }
+ }
+
+ public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ sesh.createConsumer(sesh.createQueue("Invalid"));
+
+ fail("Test failed as consumer was created.");
+ //conn will be automatically closed
+ }
+ catch (JMSException e)
+ {
+ Throwable cause = e.getLinkedException();
+
+ assertNotNull("There was no liked exception", cause);
+ assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass());
+ assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode());
+ }
+ }
+
+ public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("server", "guest", BROKER));
+
+ //Prevent Failover
+ ((AMQConnection) conn).setConnectionListener(this);
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ sesh.createConsumer(sesh.createTemporaryQueue());
+ fail("Test failed as consumer was created.");
+ //conn will be automatically closed
+ }
+ catch (JMSException e)
+ {
+ Throwable cause = e.getLinkedException();
+
+ assertNotNull("There was no liked exception", cause);
+ assertEquals("Wrong linked exception type", AMQAuthenticationException.class, cause.getClass());
+ assertEquals("Incorrect error code received", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode());
+ }
+ }
+
+ public void testServerCreateNamedQueueValid() throws JMSException, URLSyntaxException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("server", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ //Create Temporary Queue
+ ((AMQSession) sesh).createQueue(new AMQShortString("example.RequestQueue"), false, false, false);
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Test failed due to:" + e.getMessage());
+ }
+ }
+
+ public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("server", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ //Create a Named Queue
+ ((AMQSession) sesh).createQueue(new AMQShortString("IllegalQueue"), false, false, false);
+
+ fail("Test failed as creation succeded.");
+ //conn will be automatically closed
+ }
+ catch (AMQAuthenticationException amqe)
+ {
+ assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode());
+ }
+ }
+
+ public void testServerCreateTemporyQueueInvalid() throws JMSException, URLSyntaxException, AMQException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("server", "guest", BROKER));
+
+ Session sesh = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ ((AMQSession) sesh).createQueue(new AMQShortString("again_ensure_auto_delete_queue_for_temporary"),
+ true, false, false);
+
+ fail("Test failed as creation succeded.");
+ //conn will be automatically closed
+ }
+ catch (AMQAuthenticationException amqe)
+ {
+ assertEquals("Incorrect error code thrown", 403, amqe.getErrorCode().getCode());
+ }
+ }
+
+ /**
+ * This test uses both the cilent and sender to validate that the Server is able to publish to a temporary queue.
+ * The reason the client must be in volved is that the Serve is unable to create its own Temporary Queues.
+ *
+ * @throws AMQException
+ * @throws URLSyntaxException
+ * @throws JMSException
+ */
+ public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException
+ {
+ //Set up the Server
+ Connection serverConnection = new AMQConnection(createConnectionString("server", "guest", BROKER));
+
+ ((AMQConnection) serverConnection).setConnectionListener(this);
+
+ Session serverSession = serverConnection.createSession(true, Session.SESSION_TRANSACTED);
+
+ Queue requestQueue = serverSession.createQueue("example.RequestQueue");
+
+ MessageConsumer server = serverSession.createConsumer(requestQueue);
+
+ serverConnection.start();
+
+ //Set up the consumer
+ Connection clientConnection = new AMQConnection(createConnectionString("client", "guest", BROKER));
+
+ //Send a test mesage
+ Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Queue responseQueue = clientSession.createTemporaryQueue();
+
+ MessageConsumer clientResponse = clientSession.createConsumer(responseQueue);
+
+ clientConnection.start();
+
+ Message request = clientSession.createTextMessage("Request");
+
+ assertNotNull("Response Queue is null", responseQueue);
+
+ request.setJMSReplyTo(responseQueue);
+
+ clientSession.createProducer(requestQueue).send(request);
+
+ try
+ {
+ Message msg = null;
+
+ msg = server.receive(2000);
+
+ while (msg != null && !((TextMessage) msg).getText().equals("Request"))
+ {
+ msg = server.receive(2000);
+ }
+
+ assertNotNull("Message not received", msg);
+
+ assertNotNull("Reply-To is Null", msg.getJMSReplyTo());
+
+ MessageProducer sender = serverSession.createProducer(msg.getJMSReplyTo());
+
+ sender.send(serverSession.createTextMessage("Response"));
+
+ //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker.
+ serverSession.commit();
+
+ serverConnection.close();
+
+ //Ensure Response is received.
+ Message clientResponseMsg = clientResponse.receive(2000);
+ assertNotNull("Client did not receive response message,", clientResponseMsg);
+ assertEquals("Incorrect message received", "Response", ((TextMessage) clientResponseMsg).getText());
+
+ clientConnection.close();
+ }
+ catch (Exception e)
+ {
+ fail("Test publish failed:" + e);
+ }
+ }
+
+ public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException
+ {
+ try
+ {
+ Connection conn = new AMQConnection(createConnectionString("server", "guest", BROKER));
+
+ ((AMQConnection) conn).setConnectionListener(this);
+
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ conn.start();
+
+ MessageProducer sender = ((AMQSession) session).createProducer(null);
+
+ Queue queue = session.createQueue("Invalid");
+
+ // Send a message that we will wait to be sent, this should give the broker time to close the connection
+ // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not
+ // queue existence.
+ ((org.apache.qpid.jms.MessageProducer) sender).send(queue, session.createTextMessage("test"),
+ DeliveryMode.NON_PERSISTENT, 0, 0L, false, false, true);
+
+ // Test the connection with a valid consumer
+ // This may not work as the session may be closed before the queue or consumer creation can occur.
+ // The correct JMSexception with linked error will only occur when the close method is recevied whilst in
+ // the failover safe block
+ session.createConsumer(session.createQueue("example.RequestQueue")).close();
+
+ //Connection should now be closed and will throw the exception caused by the above send
+ conn.close();
+
+ fail("Close is not expected to succeed.");
+ }
+ catch (IllegalStateException ise)
+ {
+ System.err.println("QPID-826 : WARNING : Unable to determine cause of failure due to closure as we don't " +
+ "record it for reporting after connection closed asynchronously");
+ }
+ catch (JMSException e)
+ {
+ Throwable cause = e.getLinkedException();
+ assertEquals("Incorrect exception", AMQAuthenticationException.class, cause.getClass());
+ assertEquals("Incorrect error code thrown", 403, ((AMQAuthenticationException) cause).getErrorCode().getCode());
+ }
+ }
+
+ // Connection Listener Interface - Used here to block failover
+
+ public void bytesSent(long count)
+ {
+ }
+
+ public void bytesReceived(long count)
+ {
+ }
+
+ public boolean preFailover(boolean redirect)
+ {
+ //Prevent failover.
+ return false;
+ }
+
+ public boolean preResubscribe()
+ {
+ return false;
+ }
+
+ public void failoverComplete()
+ {
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java b/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
index ab6d9742e4..c7984d5d33 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
@@ -23,7 +23,6 @@ package org.apache.qpid.server.store;
import junit.framework.TestCase;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.BasicContentHeaderProperties;
-import org.apache.qpid.framing.BasicPublishBody;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
@@ -61,6 +60,11 @@ public class TestReferenceCounting extends TestCase
return null;
}
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public boolean isImmediate()
{
return false;
@@ -78,7 +82,7 @@ public class TestReferenceCounting extends TestCase
};
AMQMessage message = new AMQMessage(_store.getNewMessageId(), info,
- new NonTransactionalContext(_store, _storeContext, null, null, null),
+ new NonTransactionalContext(_store, _storeContext, null, null),
createPersistentContentHeader());
message = message.takeReference();
@@ -109,6 +113,11 @@ public class TestReferenceCounting extends TestCase
return null;
}
+ public void setExchange(AMQShortString exchange)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public boolean isImmediate()
{
return false;
@@ -127,7 +136,7 @@ public class TestReferenceCounting extends TestCase
AMQMessage message = new AMQMessage(_store.getNewMessageId(),
info,
- new NonTransactionalContext(_store, _storeContext, null, null, null),
+ new NonTransactionalContext(_store, _storeContext, null, null),
createPersistentContentHeader());
message = message.takeReference();
diff --git a/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java b/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java
index 58ea392306..b34f28b1a8 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java
@@ -27,6 +27,7 @@ import org.apache.qpid.server.store.TestableMemoryMessageStore;
import org.apache.qpid.server.store.StoreContext;
import java.util.LinkedList;
+import java.util.NoSuchElementException;
public class TxnBufferTest extends TestCase
{
@@ -78,7 +79,15 @@ public class TxnBufferTest extends TestCase
buffer.enlist(new FailedPrepare());
buffer.enlist(new MockOp());
- buffer.commit(null);
+ try
+ {
+ buffer.commit(null);
+ }
+ catch (NoSuchElementException e)
+ {
+
+ }
+
validateOps();
store.validate();
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java
index bd7ed60d1d..83b4665be6 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java
@@ -23,6 +23,7 @@ package org.apache.qpid.server.util;
import org.apache.qpid.server.exchange.ExchangeFactory;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.management.ManagedObjectRegistry;
+import org.apache.qpid.server.plugins.PluginManager;
import org.apache.qpid.server.queue.QueueRegistry;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
@@ -30,8 +31,8 @@ import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager;
import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager;
import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager;
-import org.apache.qpid.server.security.access.AccessManager;
-import org.apache.qpid.server.security.access.AllowAll;
+import org.apache.qpid.server.security.access.ACLPlugin;
+import org.apache.qpid.server.security.access.plugins.AllowAll;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.TestableMemoryMessageStore;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -53,7 +54,7 @@ public class TestApplicationRegistry extends ApplicationRegistry
private ManagedObjectRegistry _managedObjectRegistry;
- private AccessManager _accessManager;
+ private ACLPlugin _accessManager;
private PrincipalDatabaseManager _databaseManager;
@@ -136,14 +137,25 @@ public class TestApplicationRegistry extends ApplicationRegistry
return null; //To change body of implemented methods use File | Settings | File Templates.
}
- public AccessManager getAccessManager()
+ public ACLPlugin getAccessManager()
{
return _accessManager;
}
+ public void setAccessManager(ACLPlugin newManager)
+ {
+ _accessManager = newManager;
+ }
+
public MessageStore getMessageStore()
{
return _messageStore;
}
+
+ public PluginManager getPluginManager()
+ {
+ return null;
+ }
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/FailoverBaseCase.java b/java/systests/src/main/java/org/apache/qpid/test/FailoverBaseCase.java
new file mode 100644
index 0000000000..4dd957c121
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/FailoverBaseCase.java
@@ -0,0 +1,76 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import org.apache.qpid.test.VMTestCase;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.NamingException;
+import java.util.Hashtable;
+import java.util.Map;
+
+public class FailoverBaseCase extends VMTestCase
+{
+ private boolean failedOver = true;
+
+ public void setUp() throws Exception
+ {
+ // Make Broker 2 the first one so we can kill it and allow VMTestCase to clean up vm://:1
+ _brokerlist = "vm://:2;vm://:1";
+ _clientID = this.getClass().getName();
+ _virtualhost = "/test";
+
+ _connections.put("connection1", "amqp://guest:guest@" + _clientID + _virtualhost + "?brokerlist='vm://:1'");
+ _connections.put("connection2", "amqp://guest:guest@" + _clientID + _virtualhost + "?brokerlist='vm://:2'");
+
+ try
+ {
+ TransportConnection.createVMBroker(2);
+ }
+ catch (Exception e)
+ {
+ fail("Unable to create broker: " + e);
+ }
+
+ super.setUp();
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (!failedOver)
+ {
+ TransportConnection.killVMBroker(2);
+ ApplicationRegistry.remove(2);
+ }
+ super.tearDown();
+ }
+
+
+ public void failBroker()
+ {
+ failedOver = true;
+ TransportConnection.killVMBroker(2);
+ ApplicationRegistry.remove(2);
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java b/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java
index 4d8ddeaddd..dcdfb11618 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java
@@ -19,24 +19,28 @@
*/
package org.apache.qpid.test;
-import junit.extensions.TestSetup;
-import junit.framework.Test;
import junit.framework.TestCase;
-
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.AMQException;
-import javax.naming.Context;
-import javax.naming.spi.InitialContextFactory;
-import javax.jms.Queue;
+import javax.jms.Connection;
import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Queue;
import javax.jms.Session;
-import javax.jms.Connection;
-import javax.jms.MessageProducer;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Hashtable;
-import java.util.List;
import java.util.LinkedList;
+import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
@@ -85,22 +89,22 @@ public class VMTestCase extends TestCase
_brokerlist = "vm://:1";
}
- env.put("connectionfactory.connection", "amqp://guest:guest@" +
- _clientID + _virtualhost + "?brokerlist='" + _brokerlist + "'");
+ env.put("connectionfactory.connection", "amqp://guest:guest@" + _clientID + _virtualhost + "?brokerlist='"
+ + _brokerlist + "'");
for (Map.Entry<String, String> c : _connections.entrySet())
{
env.put("connectionfactory." + c.getKey(), c.getValue());
}
- env.put("queue.queue", "queue");
+ _queues.put("queue", "queue");
for (Map.Entry<String, String> q : _queues.entrySet())
{
env.put("queue." + q.getKey(), q.getValue());
}
- env.put("topic.topic", "topic");
+ _topics.put("topic", "topic");
for (Map.Entry<String, String> t : _topics.entrySet())
{
@@ -112,14 +116,50 @@ public class VMTestCase extends TestCase
protected void tearDown() throws Exception
{
+ //Disabled
+// checkQueuesClean();
+
TransportConnection.killVMBroker(1);
ApplicationRegistry.remove(1);
super.tearDown();
}
- public void testDummyinVMTestCase()
+ private void checkQueuesClean() throws NamingException, JMSException
+ {
+ Connection connection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ connection.start();
+
+ Iterator<String> queueNames = new HashSet<String>(_queues.values()).iterator();
+
+ assertTrue("QueueNames doesn't have next", queueNames.hasNext());
+
+ while (queueNames.hasNext())
+ {
+ Queue queue = session.createQueue(queueNames.next());
+
+ //Validate that the queue are reporting empty.
+ long queueDepth = 0;
+ try
+ {
+ queueDepth = ((AMQSession) session).getQueueDepth((AMQDestination) queue);
+ }
+ catch (AMQException e)
+ {
+ //ignore
+ }
+
+ assertEquals("Session reports Queue depth not as expected", 0, queueDepth);
+ }
+
+ connection.close();
+ }
+
+ public int getMessageCount(String queueName)
{
- // keep maven happy
+ return ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(_virtualhost.substring(1))
+ .getQueueRegistry().getQueue(new AMQShortString(queueName)).getMessageCount();
}
}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/CancelTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/CancelTest.java
new file mode 100644
index 0000000000..2b02f1cbbf
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/CancelTest.java
@@ -0,0 +1,111 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.test.client;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.test.VMTestCase;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.Session;
+import javax.jms.JMSException;
+import javax.naming.NamingException;
+import java.util.Enumeration;
+public class CancelTest extends VMTestCase
+{
+ private static final Logger _logger = Logger.getLogger(CancelTest.class);
+
+ private Connection _clientConnection;
+ private Session _clientSession;
+ private Queue _queue;
+
+ public void setUp() throws Exception
+ {
+
+ super.setUp();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+ }
+
+ /**
+ * Simply
+ */
+ public void test() throws JMSException, NamingException
+ {
+ Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(_queue);
+ producer.send(producerSession.createTextMessage());
+ producerConnection.close();
+
+
+ QueueBrowser browser = _clientSession.createBrowser(_queue);
+ Enumeration e = browser.getEnumeration();
+
+
+ while (e.hasMoreElements())
+ {
+ e.nextElement();
+ }
+
+ browser.close();
+
+ MessageConsumer consumer = _clientSession.createConsumer(_queue);
+ consumer.receive();
+ consumer.close();
+ }
+
+ public void loop()
+ {
+ try
+ {
+ int run = 0;
+ while (true)
+ {
+ System.err.println(run++);
+ test();
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error(e, e);
+ }
+ }
+
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java
new file mode 100644
index 0000000000..463946e14a
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java
@@ -0,0 +1,160 @@
+package org.apache.qpid.test.client;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.VMTestCase;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.NamingException;
+import java.util.concurrent.CountDownLatch;/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+public class DupsOkTest extends VMTestCase
+{
+
+ private Queue _queue;
+ private static final int MSG_COUNT = 9999;
+ private CountDownLatch _awaitCompletion = new CountDownLatch(1);
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //CreateQueue
+ ((ConnectionFactory) _context.lookup("connection")).createConnection().createSession(false, Session.AUTO_ACKNOWLEDGE).createConsumer(_queue).close();
+
+ //Create Producer put some messages on the queue
+ Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ for (int count = 1; count <= MSG_COUNT; count++)
+ {
+ Message msg = producerSession.createTextMessage("Message " + count);
+ msg.setIntProperty("count", count);
+ producer.send(msg);
+ }
+
+ producerConnection.close();
+ }
+
+ public void testDupsOK() throws NamingException, JMSException, InterruptedException, AMQException
+ {
+ //Create Client
+ Connection clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ clientConnection.start();
+
+ final Session clientSession = clientConnection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
+
+ MessageConsumer consumer = clientSession.createConsumer(_queue);
+
+ consumer.setMessageListener(new MessageListener()
+ {
+ int _msgCount = 0;
+
+ public void onMessage(Message message)
+ {
+ _msgCount++;
+ if (message == null)
+ {
+ fail("Should not get null messages");
+ }
+
+ if (message instanceof TextMessage)
+ {
+ try
+ {
+ /*if (message.getIntProperty("count") == 5000)
+ {
+ assertEquals("The queue should have 4999 msgs left", 4999, getMessageCount(_queue.getQueueName()));
+ }*/
+
+ if (message.getIntProperty("count") == MSG_COUNT)
+ {
+ try
+ {
+ long remainingMessages = ((AMQSession) clientSession).getQueueDepth((AMQDestination) _queue);
+ if(remainingMessages != 0)
+ {
+
+ assertEquals("The queue should have 0 msgs left, seen " + _msgCount + " messages.", 0, getMessageCount(_queue.getQueueName()));
+ }
+ }
+ catch (AMQException e)
+ {
+ assertNull("Got AMQException", e);
+ }
+ finally
+ {
+ //This is the last message so release test.
+ _awaitCompletion.countDown();
+ }
+ }
+
+ }
+ catch (JMSException e)
+ {
+ fail("Unable to get int property 'count'");
+ }
+ }
+ else
+ {
+ fail("");
+ }
+ }
+ });
+
+ try
+ {
+ _awaitCompletion.await();
+ }
+ catch (InterruptedException e)
+ {
+ fail("Unable to wait for test completion");
+ throw e;
+ }
+
+// consumer.close();
+
+ assertEquals("The queue should have 0 msgs left", 0, ((AMQSession) clientSession).getQueueDepth((AMQDestination) _queue));
+ clientConnection.close();
+
+ }
+
+
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java
new file mode 100644
index 0000000000..9beaa9844a
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java
@@ -0,0 +1,510 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.client;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.FailoverBaseCase;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.NamingException;
+import java.util.Enumeration;
+import java.util.Random;
+
+public class QueueBrowserAutoAckTest extends FailoverBaseCase
+{
+ private static final Logger _logger = Logger.getLogger(QueueBrowserAutoAckTest.class);
+
+ protected Connection _clientConnection;
+ protected Session _clientSession;
+ protected Queue _queue;
+ protected static final String MESSAGE_ID_PROPERTY = "MessageIDProperty";
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure there are no messages on the queue to start with.
+ checkQueueDepth(0);
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (_clientConnection != null)
+ {
+ _clientConnection.close();
+ }
+
+ super.tearDown();
+ }
+
+ protected void sendMessages(int num) throws JMSException
+ {
+ Connection producerConnection = null;
+ try
+ {
+ producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ }
+ catch (NamingException e)
+ {
+ fail("Unable to lookup connection in JNDI.");
+ }
+
+ sendMessages(producerConnection, num);
+ }
+
+ protected void sendMessages(String connection, int num) throws JMSException
+ {
+ Connection producerConnection = null;
+ try
+ {
+ producerConnection = ((ConnectionFactory) _context.lookup(connection)).createConnection();
+ }
+ catch (NamingException e)
+ {
+ fail("Unable to lookup connection in JNDI.");
+ }
+ sendMessages(producerConnection, num);
+ }
+
+
+ protected void sendMessages(Connection producerConnection, int num) throws JMSException
+ {
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ producerSession.createConsumer(_queue).close();
+
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ for (int messsageID = 0; messsageID < num; messsageID++)
+ {
+ TextMessage textMsg = producerSession.createTextMessage("Message " + messsageID);
+ textMsg.setIntProperty(MESSAGE_ID_PROPERTY, messsageID);
+ producer.send(textMsg);
+ }
+
+ producerConnection.close();
+ }
+
+ protected void checkQueueDepth(int depth) throws JMSException
+ {
+
+ // create QueueBrowser
+ _logger.info("Creating Queue Browser");
+
+ QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
+
+ // check for messages
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Checking for " + depth + " messages with QueueBrowser");
+ }
+
+ //Check what the session believes the queue count to be.
+ long queueDepth = 0;
+
+ try
+ {
+ queueDepth = ((AMQSession) _clientSession).getQueueDepth((AMQDestination) _queue);
+ }
+ catch (AMQException e)
+ {
+ }
+
+ assertEquals("Session reports Queue depth not as expected", depth, queueDepth);
+
+ // Browse the queue to get a second opinion
+ int msgCount = 0;
+ Enumeration msgs = queueBrowser.getEnumeration();
+
+ while (msgs.hasMoreElements())
+ {
+ msgs.nextElement();
+ msgCount++;
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Found " + msgCount + " messages total in browser");
+ }
+
+ // check to see if all messages found
+ assertEquals("Browser did not find all messages", depth, msgCount);
+
+ //Close browser
+ queueBrowser.close();
+ }
+
+ protected void closeBrowserBeforeAfterGetNext(int messageCount) throws JMSException
+ {
+ QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
+
+ Enumeration msgs = queueBrowser.getEnumeration();
+
+ int msgCount = 0;
+
+ while (msgs.hasMoreElements() && msgCount < messageCount)
+ {
+ msgs.nextElement();
+ msgCount++;
+ }
+
+ try
+ {
+ queueBrowser.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Close should happen without error:" + e.getMessage());
+ }
+ }
+
+
+ protected void checkMultipleGetEnum(int sentMessages, int browserCount) throws JMSException
+ {
+ QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
+
+ for (int count = 0; count < browserCount; count++)
+ {
+ Enumeration msgs = queueBrowser.getEnumeration();
+
+ int msgCount = 0;
+
+ while (msgs.hasMoreElements())
+ {
+ msgs.nextElement();
+ msgCount++;
+ }
+ assertEquals(msgCount, sentMessages);
+ }
+
+ try
+ {
+ queueBrowser.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Close should happen without error:" + e.getMessage());
+ }
+ }
+
+ protected void checkOverlappingMultipleGetEnum(int browserCount, int expectedMessages) throws JMSException
+ {
+ checkOverlappingMultipleGetEnum(browserCount, expectedMessages, null);
+ }
+
+ protected void checkOverlappingMultipleGetEnum(int browserCount, int expectedMessages, String selector) throws JMSException
+ {
+ QueueBrowser queueBrowser = selector == null ?
+ _clientSession.createBrowser(_queue, selector) :
+ _clientSession.createBrowser(_queue);
+
+ Enumeration[] msgs = new Enumeration[browserCount];
+ int[] msgCount = new int[browserCount];
+
+ //create Enums
+ for (int count = 0; count < browserCount; count++)
+ {
+ msgs[count] = queueBrowser.getEnumeration();
+ }
+
+ //interleave reads
+ for (int cnt = 0; cnt < expectedMessages; cnt++)
+ {
+ for (int count = 0; count < browserCount; count++)
+ {
+ if (msgs[count].hasMoreElements())
+ {
+ msgs[count].nextElement();
+ msgCount[count]++;
+ }
+ }
+ }
+
+ //validate all browsers get right message count.
+ for (int count = 0; count < browserCount; count++)
+ {
+ assertEquals(msgCount[count], expectedMessages);
+ }
+
+ try
+ {
+ queueBrowser.close();
+ }
+ catch (JMSException e)
+ {
+ fail("Close should happen without error:" + e.getMessage());
+ }
+ }
+
+ protected void validate(int messages) throws JMSException
+ {
+ //Create a new connection to validate message content
+ Connection connection = null;
+
+ try
+ {
+ connection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ }
+ catch (NamingException e)
+ {
+ fail("Unable to make validation connection");
+ }
+
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ connection.start();
+
+ MessageConsumer consumer = session.createConsumer(_queue);
+
+ _logger.info("Verify messages are still on the queue");
+
+ Message tempMsg;
+
+ for (int msgCount = 0; msgCount < messages; msgCount++)
+ {
+ tempMsg = (TextMessage) consumer.receive(RECEIVE_TIMEOUT);
+ if (tempMsg == null)
+ {
+ fail("Message " + msgCount + " not retrieved from queue");
+ }
+ }
+
+ //Close this new connection
+ connection.close();
+
+ _logger.info("All messages recevied from queue");
+
+ //ensure no message left.
+ checkQueueDepth(0);
+ }
+
+ protected void checkQueueDepthWithSelectors(int clients, int totalMessages) throws JMSException
+ {
+
+ String selector = MESSAGE_ID_PROPERTY + " % " + clients;
+
+ checkOverlappingMultipleGetEnum(clients, totalMessages / clients, selector);
+ }
+
+
+ /**
+ * This tests you can browse an empty queue, see QPID-785
+ *
+ * @throws Exception
+ */
+ public void testBrowsingEmptyQueue() throws Exception
+ {
+ checkQueueDepth(0);
+ }
+
+ /*
+ * Test Messages Remain on Queue
+ * Create a queu and send messages to it. Browse them and then receive them all to verify they were still there
+ *
+ */
+ public void testQueueBrowserMsgsRemainOnQueue() throws Exception
+ {
+ int messages = 10;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ validate(messages);
+ }
+
+
+ public void testClosingBrowserMidReceiving() throws NamingException, JMSException
+ {
+ int messages = 100;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ closeBrowserBeforeAfterGetNext(10);
+
+ validate(messages);
+
+ }
+
+ public void testMultipleGetEnum() throws NamingException, JMSException
+ {
+ int messages = 100;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ checkMultipleGetEnum(messages, 5);
+
+ validate(messages);
+ }
+
+ public void testMultipleOverlappingGetEnum() throws NamingException, JMSException
+ {
+ int messages = 25;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ checkOverlappingMultipleGetEnum(5, messages);
+
+ validate(messages);
+ }
+
+
+ public void testBrowsingWithSelector() throws JMSException
+ {
+ int messages = 40;
+
+ sendMessages(messages);
+
+ checkQueueDepth(messages);
+
+ for (int clients = 2; clients <= 10; clients++)
+ {
+ checkQueueDepthWithSelectors(clients, messages);
+ }
+
+ validate(messages);
+ }
+
+ public void testFailoverWithQueueBrowser() throws JMSException
+ {
+ int messages = 50;
+
+ sendMessages("connection1", messages);
+ sendMessages("connection2", messages);
+
+
+ checkQueueDepth(messages);
+
+
+ _logger.info("Creating Queue Browser");
+
+ QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
+
+ long queueDepth = 0;
+
+ try
+ {
+ queueDepth = ((AMQSession) _clientSession).getQueueDepth((AMQDestination) _queue);
+ }
+ catch (AMQException e)
+ {
+ }
+
+ assertEquals("Session reports Queue depth not as expected", messages, queueDepth);
+
+
+ int msgCount = 0;
+
+ int failPoint = 0;
+
+ failPoint = new Random().nextInt(messages) + 1;
+
+ Enumeration msgs = queueBrowser.getEnumeration();
+
+ while (msgs.hasMoreElements())
+ {
+ msgs.nextElement();
+ msgCount++;
+
+ if (msgCount == failPoint)
+ {
+ failBroker();
+ }
+ }
+
+ assertTrue("We should get atleast " + messages + " msgs.", msgCount >= messages);
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("QBAAT Found " + msgCount + " messages total in browser");
+ }
+
+ //Close browser
+ queueBrowser.close();
+
+ //Validate all messages still on Broker 1
+ validate(messages);
+ }
+
+ public void testFailoverAsQueueBrowserCreated() throws JMSException
+ {
+ // The IoServiceListenerSupport seems to get stuck in with a managedSession that isn't closing when requested.
+ // So it hangs waiting for the session.
+ int messages = 50;
+
+ sendMessages("connection1", messages);
+ sendMessages("connection2", messages);
+
+ failBroker();
+
+ checkQueueDepth(messages);
+
+ //Validate all messages still on Broker 1
+ validate(messages);
+ }
+
+ public void loop() throws JMSException
+ {
+ int run = 0;
+ try
+ {
+ while (true)
+ {
+ System.err.println(run++ + ":************************************************************************");
+ testQueueBrowserMsgsRemainOnQueue();
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error(e, e);
+ }
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java
new file mode 100644
index 0000000000..0ef0217234
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java
@@ -0,0 +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.test.client;
+
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+import javax.jms.Session;
+
+public class QueueBrowserClientAckTest extends QueueBrowserAutoAckTest
+{
+ public void setUp() throws Exception
+ {
+
+ super.setUp();
+
+ _clientConnection.close();
+ _clientSession.close();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java
new file mode 100644
index 0000000000..80d74b1b79
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java
@@ -0,0 +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.test.client;
+
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+import javax.jms.Session;
+
+public class QueueBrowserDupsOkTest extends QueueBrowserAutoAckTest
+{
+ public void setUp() throws Exception
+ {
+
+ super.setUp();
+
+ _clientConnection.close();
+ _clientSession.close();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java
new file mode 100644
index 0000000000..1bc5f07b4e
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java
@@ -0,0 +1,50 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.client;
+
+import org.apache.qpid.client.AMQSession;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.Queue;
+
+public class QueueBrowserNoAckTest extends QueueBrowserAutoAckTest
+{
+ public void setUp() throws Exception
+ {
+
+ super.setUp();
+
+ _clientConnection.close();
+ _clientSession.close();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java
new file mode 100644
index 0000000000..42e13c89e4
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java
@@ -0,0 +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.test.client;
+
+import org.apache.qpid.client.AMQSession;
+
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+
+public class QueueBrowserPreAckTest extends QueueBrowserAutoAckTest
+{
+ public void setUp() throws Exception
+ {
+
+ super.setUp();
+
+ _clientConnection.close();
+ _clientSession.close();
+
+ _clientConnection.close();
+ _clientSession.close();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, AMQSession.PRE_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+ }
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java
deleted file mode 100644
index ec9df8f1b3..0000000000
--- a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-package org.apache.qpid.test.client;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.test.VMTestCase;
-
-import javax.jms.Queue;
-import javax.jms.ConnectionFactory;
-import javax.jms.Session;
-import javax.jms.Connection;
-import javax.jms.MessageProducer;
-import javax.jms.MessageConsumer;
-import javax.jms.QueueBrowser;
-import javax.jms.TextMessage;
-import javax.jms.JMSException;
-import javax.jms.QueueReceiver;
-import javax.jms.Message;
-import java.util.Enumeration;
-
-import junit.framework.TestCase;
-
-public class QueueBrowserTest extends VMTestCase
-{
- private static final Logger _logger = Logger.getLogger(QueueBrowserTest.class);
-
- private static final int MSG_COUNT = 10;
-
- private Connection _clientConnection;
- private Session _clientSession;
- private Queue _queue;
-
- public void setUp() throws Exception
- {
-
- super.setUp();
-
- _queue = (Queue) _context.lookup("queue");
-
- //Create Client
- _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
-
- _clientConnection.start();
-
- _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- //Ensure _queue is created
- _clientSession.createConsumer(_queue).close();
-
- //Create Producer put some messages on the queue
- Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
-
- producerConnection.start();
-
- Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
- MessageProducer producer = producerSession.createProducer(_queue);
-
- for (int msg = 0; msg < MSG_COUNT; msg++)
- {
- producer.send(producerSession.createTextMessage("Message " + msg));
- }
-
- producerConnection.close();
-
- }
-
- /*
- * Test Messages Remain on Queue
- * Create a queu and send messages to it. Browse them and then receive them all to verify they were still there
- *
- */
-
- public void testQueueBrowserMsgsRemainOnQueue() throws JMSException
- {
-
- // create QueueBrowser
- _logger.info("Creating Queue Browser");
-
- QueueBrowser queueBrowser = _clientSession.createBrowser(_queue);
-
- // check for messages
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Checking for " + MSG_COUNT + " messages with QueueBrowser");
- }
-
- int msgCount = 0;
- Enumeration msgs = queueBrowser.getEnumeration();
-
- while (msgs.hasMoreElements())
- {
- msgs.nextElement();
- msgCount++;
- }
-
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Found " + msgCount + " messages total in browser");
- }
-
- // check to see if all messages found
-// assertEquals("browser did not find all messages", MSG_COUNT, msgCount);
- if (msgCount != MSG_COUNT)
- {
- _logger.warn(msgCount + "/" + MSG_COUNT + " messages received.");
- }
-
- //Close browser
- queueBrowser.close();
-
- // VERIFY
-
- // continue and try to receive all messages
- MessageConsumer consumer = _clientSession.createConsumer(_queue);
-
- _logger.info("Verify messages are still on the queue");
-
- Message tempMsg;
-
- for (msgCount = 0; msgCount < MSG_COUNT; msgCount++)
- {
- tempMsg = (TextMessage) consumer.receive(RECEIVE_TIMEOUT);
- if (tempMsg == null)
- {
- fail("Message " + msgCount + " not retrieved from queue");
- }
- }
-
- _logger.info("All messages recevied from queue");
- }
-
-
-}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java
new file mode 100644
index 0000000000..0d63373e61
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java
@@ -0,0 +1,51 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.client;
+
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+import javax.jms.Session;
+
+public class QueueBrowserTransactedTest extends QueueBrowserAutoAckTest
+{
+ public void setUp() throws Exception
+ {
+
+ super.setUp();
+
+ _clientConnection.close();
+ _clientSession.close();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(true, Session.SESSION_TRANSACTED);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+ }
+
+
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java
new file mode 100644
index 0000000000..e7d7c7eba6
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java
@@ -0,0 +1,257 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.test.client.failover;
+
+import junit.framework.TestCase;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.log4j.Logger;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import java.util.concurrent.CountDownLatch;
+
+public class FailoverTest extends TestCase implements ConnectionListener
+{
+ private static final Logger _logger = Logger.getLogger(FailoverTest.class);
+
+ private static final int NUM_BROKERS = 2;
+ private static final String BROKER = "amqp://guest:guest@/test?brokerlist='vm://:%d;vm://:%d'";
+ private static final String QUEUE = "queue";
+ private static final int NUM_MESSAGES = 10;
+ private Connection con;
+ private AMQConnectionFactory conFactory;
+ private Session prodSess;
+ private AMQQueue q;
+ private MessageProducer prod;
+ private Session conSess;
+ private MessageConsumer consumer;
+
+ private static int usedBrokers = 0;
+ private CountDownLatch failoverComplete;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ // Create two VM brokers
+
+ for (int i = 0; i < NUM_BROKERS; i++)
+ {
+ usedBrokers++;
+
+ TransportConnection.createVMBroker(usedBrokers);
+ }
+
+ conFactory = new AMQConnectionFactory(String.format(BROKER, usedBrokers - 1, usedBrokers));
+ _logger.info("Connecting on:" + conFactory.getConnectionURL());
+ con = conFactory.createConnection();
+ ((AMQConnection) con).setConnectionListener(this);
+ con.start();
+ failoverComplete = new CountDownLatch(1);
+ }
+
+ private void init(boolean transacted, int mode) throws JMSException
+ {
+ prodSess = con.createSession(transacted, mode);
+ q = new AMQQueue("amq.direct", QUEUE);
+ prod = prodSess.createProducer(q);
+ conSess = con.createSession(transacted, mode);
+ consumer = conSess.createConsumer(q);
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ try
+ {
+ con.close();
+ }
+ catch (Exception e)
+ {
+
+ }
+
+ try
+ {
+ TransportConnection.killAllVMBrokers();
+ ApplicationRegistry.removeAll();
+ }
+ catch (Exception e)
+ {
+ fail("Unable to clean up");
+ }
+ super.tearDown();
+ }
+
+ private void consumeMessages(int toConsume) throws JMSException
+ {
+ Message msg;
+ for (int i = 0; i < toConsume; i++)
+ {
+ msg = consumer.receive(1000);
+ assertNotNull("Message " + i + " was null!", msg);
+ assertEquals("message " + i, ((TextMessage) msg).getText());
+ }
+ }
+
+ private void sendMessages(int totalMessages) throws JMSException
+ {
+ for (int i = 0; i < totalMessages; i++)
+ {
+ prod.send(prodSess.createTextMessage("message " + i));
+ }
+
+// try
+// {
+// Thread.sleep(100 * totalMessages);
+// }
+// catch (InterruptedException e)
+// {
+// //evil ignoring of IE
+// }
+ }
+
+ public void testP2PFailover() throws Exception
+ {
+ testP2PFailover(NUM_MESSAGES, true);
+ }
+
+ public void testP2PFailoverWithMessagesLeft() throws Exception
+ {
+ testP2PFailover(NUM_MESSAGES, false);
+ }
+
+ private void testP2PFailover(int totalMessages, boolean consumeAll) throws JMSException
+ {
+ Message msg = null;
+ init(false, Session.AUTO_ACKNOWLEDGE);
+ sendMessages(totalMessages);
+
+ // Consume some messages
+ int toConsume = totalMessages;
+ if (!consumeAll)
+ {
+ toConsume = totalMessages / 2;
+ }
+
+ consumeMessages(toConsume);
+
+ _logger.info("Failing over");
+
+ causeFailure();
+
+ msg = consumer.receive(500);
+ //todo: reinstate
+ assertNull("Should not have received message from new broker!", msg);
+ // Check that messages still sent / received
+ sendMessages(totalMessages);
+ consumeMessages(totalMessages);
+ }
+
+ private void causeFailure()
+ {
+ _logger.info("Failover");
+
+ TransportConnection.killVMBroker(usedBrokers - 1);
+ ApplicationRegistry.remove(usedBrokers - 1);
+
+ _logger.info("Awaiting Failover completion");
+ try
+ {
+ failoverComplete.await();
+ }
+ catch (InterruptedException e)
+ {
+ //evil ignore IE.
+ }
+ }
+
+ public void testClientAckFailover() throws Exception
+ {
+ init(false, Session.CLIENT_ACKNOWLEDGE);
+ sendMessages(1);
+ Message msg = consumer.receive();
+ assertNotNull("Expected msgs not received", msg);
+
+
+ causeFailure();
+
+ Exception failure = null;
+ try
+ {
+ msg.acknowledge();
+ }
+ catch (Exception e)
+ {
+ failure = e;
+ }
+ assertNotNull("Exception should be thrown", failure);
+ }
+
+ // This test disabled so that it doesn't add 4 minnutes to the length of time it takes to run, which would be lame
+ public void txest4MinuteFailover() throws Exception
+ {
+ conFactory = new AMQConnectionFactory("amqp://guest:guest@/test?brokerlist='vm://:"+(usedBrokers-1)+"?connectdelay='60000'&retries='2''");
+ _logger.info("Connecting on:" + conFactory.getConnectionURL());
+ con = conFactory.createConnection();
+ ((AMQConnection) con).setConnectionListener(this);
+ con.start();
+
+ long failTime = System.currentTimeMillis() + 60000;
+ causeFailure();
+ assertTrue("Failover did not take long enough", System.currentTimeMillis() > failTime);
+ }
+
+ 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()
+ {
+ failoverComplete.countDown();
+ }
+}
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
new file mode 100644
index 0000000000..706d99ffe2
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide assertion that the publishers received a no consumers error code on every message.
+ * <tr><td> Provide assertion that the publishers received a no route error code on every message.
+ * </table>
+ */
+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
new file mode 100644
index 0000000000..e8b7da2537
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Indicate whether or not a test case is using an in-vm broker.
+ * <tr><td> Track which in-vm broker is currently in use.
+ * <tr><td> Accept setting of a failure mechanism. <td> {@link CauseFailure}.
+ * </table>
+ *
+ * @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 <tt>true</tt> if the test is using in-vm brokers, <tt>false</tt> 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
new file mode 100644
index 0000000000..8a5a9560a0
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Cause messaging broker failure.
+ * </table>
+ */
+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
new file mode 100644
index 0000000000..6b96ade674
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Cause messaging broker failure.
+ * </table>
+ */
+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/CircuitEndBase.java b/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java
index d971aa5385..d5a33514df 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java
@@ -51,11 +51,14 @@ public class CircuitEndBase implements CircuitEnd
ExceptionMonitor exceptionMonitor;
/**
- * Creates a circuit end point on the specified producer, consumer and controlSession.
+ * 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 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 CircuitEndBase(MessageProducer producer, MessageConsumer consumer, Session session, MessageMonitor messageMonitor,
ExceptionMonitor exceptionMonitor)
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java b/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java
index 1ac94b5244..7d06aba1c0 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java
@@ -12,6 +12,20 @@
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
@@ -36,7 +50,7 @@ import java.util.List;
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Record all exceptions received. <td> {@link ExceptionListener}
+ * <tr><td> Record all exceptions received.
* </table>
*/
public class ExceptionMonitor implements ExceptionListener
@@ -45,14 +59,14 @@ public class ExceptionMonitor implements ExceptionListener
private final Logger log = Logger.getLogger(ExceptionMonitor.class);
/** Holds the received exceptions. */
- List<JMSException> exceptions = new ArrayList<JMSException>();
+ List<Exception> exceptions = new ArrayList<Exception>();
/**
* Receives incoming exceptions.
*
* @param e The exception to record.
*/
- public void onException(JMSException e)
+ public synchronized void onException(JMSException e)
{
log.debug("public void onException(JMSException e): called", e);
@@ -64,7 +78,7 @@ public class ExceptionMonitor implements ExceptionListener
*
* @return <tt>true</tt> if no exceptions have been received, <tt>false</tt> otherwise.
*/
- public boolean assertNoExceptions()
+ public synchronized boolean assertNoExceptions()
{
return exceptions.isEmpty();
}
@@ -74,7 +88,7 @@ public class ExceptionMonitor implements ExceptionListener
*
* @return <tt>true</tt> if exactly one exception been received, <tt>false</tt> otherwise.
*/
- public boolean assertOneJMSException()
+ public synchronized boolean assertOneJMSException()
{
return exceptions.size() == 1;
}
@@ -82,20 +96,27 @@ public class ExceptionMonitor implements ExceptionListener
/**
* Checks that exactly one exception, with a linked cause of the specified type, has been received.
*
+ * @param aClass The type of the linked cause.
+ *
* @return <tt>true</tt> if exactly one exception, with a linked cause of the specified type, been received,
* <tt>false</tt> otherwise.
*/
- public boolean assertOneJMSExceptionWithLinkedCause(Class aClass)
+ public synchronized boolean assertOneJMSExceptionWithLinkedCause(Class aClass)
{
if (exceptions.size() == 1)
{
- JMSException e = exceptions.get(0);
-
- Exception linkedCause = e.getLinkedException();
+ Exception e = exceptions.get(0);
- if ((linkedCause != null) && aClass.isInstance(linkedCause))
+ if (e instanceof JMSException)
{
- return true;
+ JMSException jmse = (JMSException) e;
+
+ Exception linkedCause = jmse.getLinkedException();
+
+ if ((linkedCause != null) && aClass.isInstance(linkedCause))
+ {
+ return true;
+ }
}
}
@@ -103,11 +124,37 @@ public class ExceptionMonitor implements ExceptionListener
}
/**
+ * Checks that at least one exception of the the specified type, has been received.
+ *
+ * @param exceptionClass The type of the exception.
+ *
+ * @return <tt>true</tt> if at least one exception of the specified type has been received, <tt>false</tt> otherwise.
+ */
+ public synchronized boolean assertExceptionOfType(Class exceptionClass)
+ {
+ // Start by assuming that the exception has no been received.
+ boolean passed = false;
+
+ // Scan all the exceptions for a match.
+ for (Exception e : exceptions)
+ {
+ if (exceptionClass.isInstance(e))
+ {
+ passed = true;
+
+ break;
+ }
+ }
+
+ return passed;
+ }
+
+ /**
* Reports the number of exceptions held by this monitor.
*
* @return The number of exceptions held by this monitor.
*/
- public int size()
+ public synchronized int size()
{
return exceptions.size();
}
@@ -115,9 +162,9 @@ public class ExceptionMonitor implements ExceptionListener
/**
* Clears the record of received exceptions.
*/
- public void reset()
+ public synchronized void reset()
{
- exceptions = new ArrayList();
+ exceptions = new ArrayList<Exception>();
}
/**
@@ -126,11 +173,11 @@ public class ExceptionMonitor implements ExceptionListener
*
* @return A string containing a dump of the stack traces of all exceptions.
*/
- public String toString()
+ public synchronized String toString()
{
String result = "ExceptionMonitor: holds " + exceptions.size() + " exceptions.\n\n";
- for (JMSException ex : exceptions)
+ for (Exception ex : exceptions)
{
result += getStackTrace(ex) + "\n";
}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java
index fa70e14d16..51b053d2b2 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java
@@ -20,23 +20,20 @@
*/
package org.apache.qpid.test.framework;
-import junit.framework.TestCase;
-
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
-import org.apache.qpid.client.transport.TransportConnection;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl;
+import org.apache.qpid.test.framework.BrokerLifecycleAware;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.AsymptoticTestCase;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.AsymptoticTestCase;
+import org.apache.qpid.junit.extensions.SetupTaskAware;
+import org.apache.qpid.junit.extensions.SetupTaskHandler;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
import java.util.ArrayList;
import java.util.List;
-import java.util.Properties;
/**
* FrameworkBaseCase provides a starting point for writing test cases against the test framework. Its main purpose is
@@ -50,13 +47,26 @@ import java.util.Properties;
* <tr><td> Convert failed assertions to error messages.
* </table>
*/
-public class FrameworkBaseCase extends AsymptoticTestCase
+public class FrameworkBaseCase extends AsymptoticTestCase implements FrameworkTestContext, SetupTaskAware,
+ BrokerLifecycleAware
{
/** Used for debugging purposes. */
private static final Logger log = Logger.getLogger(FrameworkBaseCase.class);
/** Holds the test sequencer to create and run test circuits with. */
- protected CircuitFactory circuitFactory = new DefaultCircuitFactory();
+ protected CircuitFactory circuitFactory = new LocalCircuitFactory();
+
+ /** Used to read the tests configurable properties through. */
+ protected ParsedProperties testProps;
+
+ /** A default setup task processor to delegate setup tasks to. */
+ protected SetupTaskHandler taskHandler = new SetupTaskHandler();
+
+ /** Flag used to track whether the test is in-vm or not. */
+ protected boolean isUsingInVM;
+
+ /** Holds the failure mechanism. */
+ protected CauseFailure failureMechanism = new CauseFailureUserPrompt();
/**
* Creates a new test case with the specified name.
@@ -92,6 +102,26 @@ public class FrameworkBaseCase extends AsymptoticTestCase
}
/**
+ * Reports the current test case name.
+ *
+ * @return The current test case name.
+ */
+ public TestCaseVector getTestCaseVector()
+ {
+ return new TestCaseVector(this.getName(), 0);
+ }
+
+ /**
+ * Reports the current test case parameters.
+ *
+ * @return The current test case parameters.
+ */
+ public MessagingTestConfigProperties getTestParameters()
+ {
+ return new MessagingTestConfigProperties(testProps);
+ }
+
+ /**
* Creates a list of assertions.
*
* @param asserts The assertions to compile in a list.
@@ -116,7 +146,7 @@ public class FrameworkBaseCase extends AsymptoticTestCase
*
* @param asserts The list of failed assertions.
*/
- protected void assertNoFailures(List<Assertion> asserts)
+ protected static void assertNoFailures(List<Assertion> asserts)
{
log.debug("protected void assertNoFailures(List<Assertion> asserts = " + asserts + "): called");
@@ -140,7 +170,7 @@ public class FrameworkBaseCase extends AsymptoticTestCase
*
* @return The error message.
*/
- protected String assertionsToString(List<Assertion> asserts)
+ protected static String assertionsToString(List<Assertion> asserts)
{
String errorMessage = "";
@@ -161,8 +191,10 @@ public class FrameworkBaseCase extends AsymptoticTestCase
{
NDC.push(getName());
- // Ensure that the in-vm broker is created.
- TransportConnection.createVMBroker(1);
+ testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
+
+ // Process all optional setup tasks. This may include in-vm broker creation, if a decorator has added it.
+ taskHandler.runSetupTasks();
}
/**
@@ -170,16 +202,30 @@ public class FrameworkBaseCase extends AsymptoticTestCase
*/
protected void tearDown()
{
- try
- {
- // Ensure that the in-vm broker is cleaned up so that the next test starts afresh.
- TransportConnection.killVMBroker(1);
- ApplicationRegistry.remove(1);
- }
- finally
- {
- NDC.pop();
- }
+ NDC.pop();
+
+ // Process all optional tear down tasks. This may include in-vm broker clean up, if a decorator has added it.
+ taskHandler.runTearDownTasks();
+ }
+
+ /**
+ * Adds the specified task to the tests setup.
+ *
+ * @param task The task to add to the tests setup.
+ */
+ public void chainSetupTask(Runnable task)
+ {
+ taskHandler.chainSetupTask(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)
+ {
+ taskHandler.chainTearDownTask(task);
}
/**
@@ -197,84 +243,46 @@ public class FrameworkBaseCase extends AsymptoticTestCase
return methodName;
}
+ public void setInVmBrokers()
+ {
+ isUsingInVM = true;
+ }
+
/**
- * DefaultCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted
- * on the same JVM.
+ * Indicates whether or not a test case is using in-vm brokers.
+ *
+ * @return <tt>true</tt> if the test is using in-vm brokers, <tt>false</tt> otherwise.
*/
- public class DefaultCircuitFactory implements CircuitFactory
+ public boolean usingInVmBroker()
{
- /**
- * 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<Assertion> assertions, Properties testProperties)
- {
- 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)
- {
- return LocalCircuitImpl.createCircuit(testProperties);
- }
-
- /**
- * 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.");
- }
+ return isUsingInVM;
+ }
- /**
- * Supplies the sending test client.
- *
- * @return The sending test client.
- */
- public TestClientDetails getSender()
- {
- throw new RuntimeException("Not implemented.");
- }
+ /**
+ * Sets the currently live in-vm broker.
+ *
+ * @param i The currently live in-vm broker.
+ */
+ public void setLiveBroker(int i)
+ { }
- /**
- * Supplies the receiving test client.
- *
- * @return The receiving test client.
- */
- public List<TestClientDetails> getReceivers()
- {
- throw new RuntimeException("Not implemented.");
- }
+ /**
+ * Reports the currently live in-vm broker.
+ *
+ * @return The currently live in-vm broker.
+ */
+ public int getLiveBroker()
+ {
+ return 0;
+ }
- /**
- * 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.");
- }
+ /**
+ * Accepts a failure mechanism.
+ *
+ * @param failureMechanism The failure mechanism.
+ */
+ public void setFailureMechanism(CauseFailure failureMechanism)
+ {
+ this.failureMechanism = failureMechanism;
}
}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java
index 7bd65618e3..2322955253 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java
@@ -1,3 +1,23 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
package org.apache.qpid.test.framework;
/**
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
new file mode 100644
index 0000000000..e7268db8eb
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide the name of the current test case.
+ * <tr><td> Provide the test parameters.
+ * </table>
+ */
+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
new file mode 100644
index 0000000000..d1fcad9cc0
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a standard test procedure over a test circuit.
+ * <tr><td> Construct test circuits appropriate to a tests context.
+ * <tr><td> Construct test circuits the support AMQP specific options.
+ * </table>
+ */
+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
new file mode 100644
index 0000000000..38a924a4ee
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a standard test procedure over a test circuit.
+ * <tr><td> Construct test circuits appropriate to a tests context.
+ * </table>
+ */
+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<Assertion> 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<TestClientDetails> 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
new file mode 100644
index 0000000000..b672b9c3ce
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Identify a test case, a handling client id, a circuit end within the client, and a test cycle number.
+ * </table>
+ */
+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 <tt>true</tt> if the identity vector is identical to this one by all fields, <tt>false</tt> 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/MessagingTestConfigProperties.java b/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java
index 9e91286683..27f9261d94 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java
@@ -20,9 +20,9 @@
*/
package org.apache.qpid.test.framework;
-import org.apache.qpid.jms.Session;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import javax.jms.Session;
import java.util.Properties;
@@ -181,10 +181,16 @@ public class MessagingTestConfigProperties extends ParsedProperties
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";
+ public static final String TRANSACTED_PUBLISHER_PROPNAME = "transactedPublisher";
/** Holds the transactional mode to use for the test. */
- public static final boolean TRANSACTED_DEFAULT = false;
+ public static final boolean TRANSACTED_PUBLISHER_DEFAULT = false;
+
+ /** Holds the name of the property to get the test transactional mode from. */
+ public static final String TRANSACTED_RECEIVER_PROPNAME = "transactedReceiver";
+
+ /** Holds the transactional mode to use for the test. */
+ public static final boolean TRANSACTED_RECEIVER_DEFAULT = false;
/** Holds the name of the property to set the no local flag from. */
public static final String NO_LOCAL_PROPNAME = "noLocal";
@@ -204,7 +210,7 @@ public class MessagingTestConfigProperties extends ParsedProperties
/** Defines the default value of the durable subscriptions flag. */
public static final boolean DURABLE_SUBSCRIPTION_DEFAULT = false;
- // ====================== Qpid Options and Flags ================================
+ // ====================== Qpid/AMQP Options and Flags ================================
/** Holds the name of the property to set the exclusive flag from. */
public static final String EXCLUSIVE_PROPNAME = "exclusive";
@@ -230,7 +236,7 @@ public class MessagingTestConfigProperties extends ParsedProperties
/** 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. */
+ /** Holds the name of the property to set the prefetch size from. */
public static final String PREFETCH_PROPNAME = "prefetch";
/** Defines the default prefetch size to use when consuming messages. */
@@ -274,6 +280,26 @@ public class MessagingTestConfigProperties extends ParsedProperties
/** 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 publisher rollback flag from. */
+ public static final String ROLLBACK_PUBLISHER_PROPNAME = "rollbackPublisher";
+
+ /** Holds the default publisher roll back setting. */
+ public static final boolean ROLLBACK_PUBLISHER_DEFAULT = false;
+
+ /** Holds the name of the property to get the publisher rollback flag from. */
+ public static final String ROLLBACK_RECEIVER_PROPNAME = "rollbackReceiver";
+
+ /** Holds the default publisher roll back setting. */
+ public static final boolean ROLLBACK_RECEIVER_DEFAULT = false;
+
+ // ====================== Options that control the bahviour of the test framework. =========================
+
+ /** Holds the name of the property to get the behavioural mode of not applicable assertions. */
+ public static final String NOT_APPLICABLE_ASSERTION_PROPNAME = "notApplicableAssertion";
+
+ /** Holds the default behavioral mode of not applicable assertions, which is logging them as a warning. */
+ public static final String NOT_APPLICABLE_ASSERTION_DEFAULT = "warn";
+
/** Holds the name of the property to get the verbose mode proeprty from. */
public static final String VERBOSE_PROPNAME = "verbose";
@@ -286,8 +312,11 @@ public class MessagingTestConfigProperties extends 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(BROKER_PROPNAME, BROKER_DEFAULT);
+ defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT);
+ defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT);
+ defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT);
+
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);
@@ -296,28 +325,33 @@ public class MessagingTestConfigProperties extends ParsedProperties
defaults.setPropertyIfNull(RECEIVER_CONSUMER_ACTIVE_PROPNAME, RECEIVER_CONSUMER_ACTIVE_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(PUBSUB_PROPNAME, PUBSUB_DEFAULT);
+
+ defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT);
+ defaults.setPropertyIfNull(TRANSACTED_PUBLISHER_PROPNAME, TRANSACTED_PUBLISHER_DEFAULT);
+ defaults.setPropertyIfNull(TRANSACTED_RECEIVER_PROPNAME, TRANSACTED_RECEIVER_DEFAULT);
+ defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_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(PREFETCH_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);
+ defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT);
+ defaults.setPropertyIfNull(PREFETCH_PROPNAME, PREFETCH_DEFAULT);
+
+ defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT);
+ defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT);
+ defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT);
+ defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT);
+ defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT);
+ defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT);
+ defaults.setPropertyIfNull(ROLLBACK_PUBLISHER_PROPNAME, ROLLBACK_PUBLISHER_DEFAULT);
+ defaults.setPropertyIfNull(ROLLBACK_RECEIVER_PROPNAME, ROLLBACK_RECEIVER_DEFAULT);
+
+ defaults.setPropertyIfNull(NOT_APPLICABLE_ASSERTION_PROPNAME, NOT_APPLICABLE_ASSERTION_DEFAULT);
+ defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT);
}
/**
@@ -338,148 +372,314 @@ public class MessagingTestConfigProperties extends ParsedProperties
super(properties);
}
+ /**
+ * The size of test messages to send.
+ *
+ * @return The size of test messages to send.
+ */
public int getMessageSize()
{
return getPropertyAsInteger(MESSAGE_SIZE_PROPNAME);
}
+ /**
+ * Flag to indicate that the publishing producer should be set up to publish to a destination.
+ *
+ * @return Flag to indicate that the publishing producer should be set up to publish to a destination.
+ */
public boolean getPublisherProducerBind()
{
return getPropertyAsBoolean(PUBLISHER_PRODUCER_BIND_PROPNAME);
}
+ /**
+ * Flag to indicate that the publishing consumer should be set up to receive from a destination.
+ *
+ * @return Flag to indicate that the publishing consumer should be set up to receive from a destination.
+ */
public boolean getPublisherConsumerBind()
{
return getPropertyAsBoolean(PUBLISHER_CONSUMER_BIND_PROPNAME);
}
+ /**
+ * Flag to indicate that the receiving producer should be set up to publish to a destination.
+ *
+ * @return Flag to indicate that the receiving producer should be set up to publish to a destination.
+ */
public boolean getReceiverProducerBind()
{
return getPropertyAsBoolean(RECEIVER_PRODUCER_BIND_PROPNAME);
}
+ /**
+ * Flag to indicate that the receiving consumer should be set up to receive from a destination.
+ *
+ * @return Flag to indicate that the receiving consumer should be set up to receive from a destination.
+ */
public boolean getReceiverConsumerBind()
{
return getPropertyAsBoolean(RECEIVER_CONSUMER_BIND_PROPNAME);
}
+ /**
+ * Flag to indicate that the publishing consumer should be created and actively listening.
+ *
+ * @return Flag to indicate that the publishing consumer should be created.
+ */
public boolean getPublisherConsumerActive()
{
return getPropertyAsBoolean(PUBLISHER_CONSUMER_ACTIVE_PROPNAME);
}
+ /**
+ * Flag to indicate that the receiving consumers should be created and actively listening.
+ *
+ * @return Flag to indicate that the receiving consumers should be created and actively listening.
+ */
public boolean getReceiverConsumerActive()
{
return getPropertyAsBoolean(RECEIVER_CONSUMER_ACTIVE_PROPNAME);
}
+ /**
+ * A root to create all test destination names from.
+ *
+ * @return A root to create all test destination names from.
+ */
public String getSendDestinationNameRoot()
{
return getProperty(SEND_DESTINATION_NAME_ROOT_PROPNAME);
}
+ /**
+ * A root to create all receiving destination names from.
+ *
+ * @return A root to create all receiving destination names from.
+ */
public String getReceiveDestinationNameRoot()
{
return getProperty(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME);
}
+ /**
+ * Flag to indicate that persistent messages should be used.
+ *
+ * @return Flag to indicate that persistent messages should be used.
+ */
public boolean getPersistentMode()
{
return getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME);
}
- public boolean getTransacted()
+ /**
+ * Flag to indicate that transactional messages should be sent by the publisher.
+ *
+ * @return Flag to indicate that transactional messages should be sent by the publisher.
+ */
+ public boolean getPublisherTransacted()
{
- return getPropertyAsBoolean(TRANSACTED_PROPNAME);
+ return getPropertyAsBoolean(TRANSACTED_PUBLISHER_PROPNAME);
}
- public String getBroker()
+ /**
+ * Flag to indicate that transactional receives should be used by the receiver.
+ *
+ * @return Flag to indicate that transactional receives should be used by the receiver.
+ */
+ public boolean getReceiverTransacted()
{
- return getProperty(BROKER_PROPNAME);
+ return getPropertyAsBoolean(TRANSACTED_PUBLISHER_PROPNAME);
}
+ /**
+ * The name of the virtual host to run all tests over.
+ *
+ * @return The name of the virtual host to run all tests over.
+ */
public String getVirtualHost()
{
return getProperty(VIRTUAL_HOST_PROPNAME);
}
+ /**
+ * Limiting rate for each sender in messages per second, or zero for unlimited.
+ *
+ * @return Limiting rate for each sender in messages per second, or zero for unlimited.
+ */
public String getRate()
{
return getProperty(RATE_PROPNAME);
}
+ /**
+ * Flag to indicate that test messages should be received publish/subscribe style by all receivers.
+ *
+ * @return Flag to indicate that test messages should be received publish/subscribe style by all receivers.
+ */
public boolean getPubsub()
{
return getPropertyAsBoolean(PUBSUB_PROPNAME);
}
+ /**
+ * The username credentials to run tests with.
+ *
+ * @return The username credentials to run tests with.
+ */
public String getUsername()
{
return getProperty(USERNAME_PROPNAME);
}
+ /**
+ * The password credentials to run tests with.
+ *
+ * @return The password credentials to run tests with.
+ */
public String getPassword()
{
return getProperty(PASSWORD_PROPNAME);
}
- public int getDestinationCount()
- {
- return getPropertyAsInteger(DESTINATION_COUNT_PROPNAME);
- }
-
+ /**
+ * The timeout duration to fail tests on, should they receive no messages within it.
+ *
+ * @return The timeout duration to fail tests on, should they receive no messages within it.
+ */
public long getTimeout()
{
return getPropertyAsLong(TIMEOUT_PROPNAME);
}
+ /**
+ * The number of messages to batch into each transaction in transational tests.
+ *
+ * @return The number of messages to batch into each transaction in transational tests.
+ */
public int getTxBatchSize()
{
return getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME);
}
+ /**
+ * Flag to indicate that tests should use durable destinations.
+ *
+ * @return Flag to indicate that tests should use durable destinations.
+ */
public boolean getDurableDests()
{
return getPropertyAsBoolean(DURABLE_DESTS_PROPNAME);
}
+ /**
+ * The ack mode for message receivers to use.
+ *
+ * @return The ack mode for message receivers to use.
+ */
public int getAckMode()
{
return getPropertyAsInteger(ACK_MODE_PROPNAME);
}
+ /**
+ * Flag to indicate that tests should use durable subscriptions.
+ *
+ * @return Flag to indicate that tests should use durable subscriptions.
+ */
public boolean getDurableSubscription()
{
return getPropertyAsBoolean(DURABLE_SUBSCRIPTION_PROPNAME);
}
+ /**
+ * The maximum amount of in-flight data, in bytes, that tests should send at any time.
+ *
+ * @return The maximum amount of in-flight data, in bytes, that tests should send at any time.
+ */
public int getMaxPending()
{
return getPropertyAsInteger(MAX_PENDING_PROPNAME);
}
- public int getPrefecth()
+ /**
+ * The size of the prefetch queue to use.
+ *
+ * @return The size of the prefetch queue to use.
+ */
+ public int getPrefetch()
{
return getPropertyAsInteger(PREFETCH_PROPNAME);
}
+ /**
+ * Flag to indicate that subscriptions should be no-local.
+ *
+ * @return Flag to indicate that subscriptions should be no-local.
+ */
public boolean getNoLocal()
{
return getPropertyAsBoolean(NO_LOCAL_PROPNAME);
}
+ /**
+ * Flag to indicate that subscriptions should be exclusive.
+ *
+ * @return Flag to indicate that subscriptions should be exclusive.
+ */
public boolean getExclusive()
{
return getPropertyAsBoolean(EXCLUSIVE_PROPNAME);
}
+ /**
+ * Flag to indicate that messages must be delivered immediately.
+ *
+ * @return Flag to indicate that messages must be delivered immediately.
+ */
public boolean getImmediate()
{
return getPropertyAsBoolean(IMMEDIATE_PROPNAME);
}
+ /**
+ * Flag to indicate that messages must be routable.
+ *
+ * @return Flag to indicate that messages must be routable.
+ */
public boolean getMandatory()
{
return getPropertyAsBoolean(MANDATORY_PROPNAME);
}
+
+ /**
+ * Gets the value of a flag to indicate that the publisher should rollback all messages sent.
+ *
+ * @return A flag to indicate that the publisher should rollback all messages sent.
+ */
+ public boolean getRollbackPublisher()
+ {
+ return getPropertyAsBoolean(ROLLBACK_PUBLISHER_PROPNAME);
+ }
+
+ /**
+ * Gets the value of a flag to indicate that the receiver should rollback all messages received, then receive them
+ * again.
+ *
+ * @return A flag to indicate that the publisher should rollback all messages received.
+ */
+ public boolean getRollbackReceiver()
+ {
+ return getPropertyAsBoolean(ROLLBACK_RECEIVER_PROPNAME);
+ }
+
+ /**
+ * Gets the behavioural mode of not applicable assertions. Should be one of 'quiet', 'warn' or 'fail'.
+ *
+ * @return The behavioural mode of not applicable assertions.
+ */
+ public String getNotApplicableAssertionMode()
+ {
+ return getProperty(NOT_APPLICABLE_ASSERTION_PROPNAME);
+ }
}
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
new file mode 100644
index 0000000000..63c7fd61c3
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Quitely pass.
+ * <tr><td> Log a warning.
+ * <tr><td> Raise a test failure.
+ * </table>
+ */
+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 <tt>true</tt> if the assertion passes, <tt>false</tt> 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/Publisher.java b/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java
index 8e61deedcf..2c8be4f787 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java
@@ -20,37 +20,55 @@
*/
package org.apache.qpid.test.framework;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+
/**
- * A Publisher is a {@link CircuitEnd} that represents the status of the publishing side of a test circuit. Its main
- * purpose is to provide assertions that can be applied to test the behaviour of the publishers.
+ * A Publisher represents the status of the publishing side of a test circuit. Its main purpose is to provide assertions
+ * that can be applied to test the behaviour of the publishers.
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities
* <tr><td> Provide assertion that the publishers received no exceptions.
- * <tr><td> Provide assertion that the publishers received a no consumers error code on every message.
- * <tr><td> Provide assertion that the publishers received a no route error code on every message.
* </table>
+ *
+ * @todo There are mixtures of AMQP and JMS assertions in this interface. Either keep them here, but quietly (or with a
+ * warning or error) drop them from test cases where they are not relevant, or push them down into sub-classes.
+ * I am tempted to go with the dropping/warning/error approach, that would imply that it makes sense to pull
+ * the assertions back from AMQPPublisher to here.
*/
public interface Publisher
{
+ // Assertions that are meaningfull to AMQP and to JMS.
+
/**
* Provides an assertion that the publisher encountered no exceptions.
*
+ * @param testProps The test configuration properties.
+ *
* @return An assertion that the publisher encountered no exceptions.
*/
- public Assertion noExceptionsAssertion();
+ public Assertion noExceptionsAssertion(ParsedProperties testProps);
+
+ // Assertions that are meaningfull only to AMQP.
/**
- * Provides an assertion that the publisher got a no consumers exception on every message.
+ * Provides an assertion that the AMQP channel was forcibly closed by an error condition.
+ *
+ * @param testProps The test configuration properties.
*
- * @return An assertion that the publisher got a no consumers exception on every message.
+ * @return An assertion that the AMQP channel was forcibly closed by an error condition.
*/
- public Assertion noConsumersAssertion();
+ public Assertion channelClosedAssertion(ParsedProperties testProps);
+
+ // Assertions that are meaningfull only to Java/JMS.
/**
- * Provides an assertion that the publisher got a no rout exception on every message.
+ * Provides an assertion that the publisher got a given exception during the test.
+ *
+ * @param testProps The test configuration properties.
+ * @param exceptionClass The exception class to check for.
*
- * @return An assertion that the publisher got a no rout exception on every message.
+ * @return An assertion that the publisher got a given exception during the test.
*/
- public Assertion noRouteAssertion();
+ public Assertion exceptionAssertion(ParsedProperties testProps, Class<? extends Exception> exceptionClass);
}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java b/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java
index f1343b9997..19dc4d90e7 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java
@@ -20,6 +20,8 @@
*/
package org.apache.qpid.test.framework;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+
/**
* A Receiver is a {@link CircuitEnd} that represents the status of the receiving side of a test circuit. Its main
* purpose is to provide assertions that can be applied to check the behaviour of the receivers.
@@ -29,20 +31,62 @@ package org.apache.qpid.test.framework;
* <tr><td> Provide assertion that the receivers received no exceptions.
* <tr><td> Provide assertion that the receivers received all test messages sent to it.
* </table>
+ *
+ * @todo There are mixtures of AMQP and JMS assertions in this interface. Either keep them here, but quietly (or with a
+ * warning or error) drop them from test cases where they are not relevant, or push them down into sub-classes.
+ * I am tempted to go with the dropping/warning/error approach.
*/
public interface Receiver
{
+ // Assertions that are meaningfull to AMQP and to JMS.
+
/**
* Provides an assertion that the receivers encountered no exceptions.
*
+ * @param testProps The test configuration properties.
+ *
* @return An assertion that the receivers encountered no exceptions.
*/
- public Assertion noExceptionsAssertion();
+ public Assertion noExceptionsAssertion(ParsedProperties testProps);
/**
* Provides an assertion that the receivers got all messages that were sent to it.
*
+ * @param testProps The test configuration properties.
+ *
* @return An assertion that the receivers got all messages that were sent to it.
*/
- public Assertion allMessagesAssertion();
+ public Assertion allMessagesReceivedAssertion(ParsedProperties testProps);
+
+ /**
+ * Provides an assertion that the receivers got none of the messages that were sent to it.
+ *
+ * @param testProps The test configuration properties.
+ *
+ * @return An assertion that the receivers got none of the messages that were sent to it.
+ */
+ public Assertion noMessagesReceivedAssertion(ParsedProperties testProps);
+
+ // Assertions that are meaningfull only to AMQP.
+
+ /**
+ * Provides an assertion that the AMQP channel was forcibly closed by an error condition.
+ *
+ * @param testProps The test configuration properties.
+ *
+ * @return An assertion that the AMQP channel was forcibly closed by an error condition.
+ */
+ public Assertion channelClosedAssertion(ParsedProperties testProps);
+
+ // Assertions that are meaningfull only to Java/JMS.
+
+ /**
+ * Provides an assertion that the receiver got a given exception during the test.
+ *
+ * @param testProps The test configuration properties.
+ * @param exceptionClass The exception class to check for.
+ *
+ * @return An assertion that the receiver got a given exception during the test.
+ */
+ public Assertion exceptionAssertion(ParsedProperties testProps, Class<? extends Exception> exceptionClass);
}
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
new file mode 100644
index 0000000000..0518a827ba
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java
@@ -0,0 +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;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+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/TestUtils.java b/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java
index c855ee21fa..d7a6f83527 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java
@@ -24,7 +24,7 @@ import org.apache.log4j.Logger;
import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.*;
import javax.naming.Context;
@@ -32,7 +32,6 @@ import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Map;
-import java.util.Properties;
/**
* TestUtils provides static helper methods that are usefull for writing tests against QPid.
@@ -40,7 +39,9 @@ import java.util.Properties;
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
* <tr><td> Create connections from test properties. <td> {@link MessagingTestConfigProperties}
+ * <tr><td> Create test messages.
* <tr><td> Inject a short pause in a test.
+ * <tr><td> Serialize properties into a message.
* </table>
*/
public class TestUtils
@@ -48,7 +49,8 @@ public class TestUtils
/** Used for debugging. */
private static Logger log = Logger.getLogger(TestUtils.class);
- private static byte[] MESSAGE_DATA_BYTES =
+ /** Some dummy data to stuff all test messages with. */
+ private static final byte[] MESSAGE_DATA_BYTES =
"Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- "
.getBytes();
@@ -118,7 +120,7 @@ public class TestUtils
*
* @return A bytes message, of the specified size, filled with dummy data.
*
- *
+ * @throws JMSException Any underlying JMSExceptions are allowed to fall through.
*/
public static Message createTestMessageOfSize(Session session, int size) throws JMSException
{
@@ -187,3 +189,4 @@ public class TestUtils
}
}
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java
index 51b839d51e..00cc2d8966 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java
@@ -21,8 +21,8 @@
package org.apache.qpid.test.framework.clocksynch;
/**
- * ClockSynchFailureException represents failure of a {@link ClockSynchronizer} to achieve synchronization. This could
- * be because a reference signal is not available, or because a desired accurracy cannot be attained, for example.
+ * ClockSynchFailureException represents failure of a {@link ClockSynchronizer} to achieve synchronization. For example,
+ * this could be because a reference signal is not available, or because a desired accurracy cannot be attained.
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java
index 88f0043067..3d4c4f7d12 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java
@@ -22,8 +22,8 @@ package org.apache.qpid.test.framework.clocksynch;
import org.apache.log4j.Logger;
-import uk.co.thebadgerset.junit.extensions.ShutdownHookable;
-import uk.co.thebadgerset.junit.extensions.Throttle;
+import org.apache.qpid.junit.extensions.ShutdownHookable;
+import org.apache.qpid.junit.extensions.Throttle;
/**
* ClockSynchThread is a convenient utility for running a thread that periodically synchronizes the clock against
@@ -121,3 +121,4 @@ public class ClockSynchThread extends Thread implements ShutdownHookable
});
}
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java
index 96485edbea..a92c551bc2 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java
@@ -32,7 +32,7 @@ package org.apache.qpid.test.framework.clocksynch;
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Trigger a clock synchronziation.
+ * <tr><td> Trigger a clock synchronization.
* <tr><td> Compute a clock delta to apply to the local clock.
* <tr><td> Estimate the error in the synchronzation.
* </table>
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java
index 8b68097158..8bce752f68 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java
@@ -20,14 +20,12 @@
*/
package org.apache.qpid.test.framework.clocksynch;
-import org.apache.log4j.Logger;
+import org.apache.qpid.junit.extensions.ShutdownHookable;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
-import uk.co.thebadgerset.junit.extensions.ShutdownHookable;
-
/**
* UDPClockReference supplies a refernce clock signal (generated from System.nanoTime()).
*
@@ -49,7 +47,7 @@ public class UDPClockReference implements Runnable, ShutdownHookable
private static final int TIMEOUT = 200;
/** Defines the port to run the clock reference on. */
- public static final int REFERENCE_PORT = 4445;
+ public static final int REFERENCE_PORT = 4444;
/** Holds the socket to receive clock reference requests on. */
protected DatagramSocket socket = null;
@@ -164,3 +162,4 @@ public class UDPClockReference implements Runnable, ShutdownHookable
}
}
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java
index a5ae0a0db6..c89112eff8 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java
@@ -20,10 +20,8 @@
*/
package org.apache.qpid.test.framework.clocksynch;
-import org.apache.log4j.Logger;
-
-import uk.co.thebadgerset.junit.extensions.util.CommandLineParser;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.CommandLineParser;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import java.io.IOException;
import java.net.*;
@@ -462,3 +460,4 @@ public class UDPClockSynchronizer implements ClockSynchronizer
}
}
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java
index 159f364825..aefeb17d59 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java
@@ -25,9 +25,9 @@ import org.apache.log4j.Logger;
import org.apache.qpid.test.framework.*;
import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.TimingController;
-import uk.co.thebadgerset.junit.extensions.TimingControllerAware;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+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.Destination;
import javax.jms.JMSException;
@@ -232,7 +232,7 @@ public class DistributedCircuitImpl implements Circuit, TimingControllerAware
}
/**
- * Used by tests cases that can supply a {@link uk.co.thebadgerset.junit.extensions.TimingController} to set the
+ * Used by tests cases that can supply a {@link org.apache.qpid.junit.extensions.TimingController} to set the
* controller on an aware test.
*
* @param controller The timing controller.
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java
index 810e1ae685..c51f710494 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java
@@ -23,10 +23,17 @@ package org.apache.qpid.test.framework.distributedcircuit;
import org.apache.qpid.test.framework.Assertion;
import org.apache.qpid.test.framework.Publisher;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+
/**
+ * DistributedPublisherImpl represents the status of the publishing side of a test circuit. Its main purpose is to
+ * provide assertions that can be applied to verify the behaviour of a non-local publisher.
+ *
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
+ * <tr><td> Provide assertion that the publishers received no exceptions.
+ * <tr><td> Provide assertion that the publishers received a no consumers error code on every message.
+ * <tr><td> Provide assertion that the publishers received a no route error code on every message.
* </table>
*/
public class DistributedPublisherImpl implements Publisher
@@ -35,8 +42,9 @@ public class DistributedPublisherImpl implements Publisher
* Provides an assertion that the publisher encountered no exceptions.
*
* @return An assertion that the publisher encountered no exceptions.
+ * @param testProps
*/
- public Assertion noExceptionsAssertion()
+ public Assertion noExceptionsAssertion(ParsedProperties testProps)
{
throw new RuntimeException("Not implemented.");
}
@@ -60,4 +68,28 @@ public class DistributedPublisherImpl implements Publisher
{
throw new RuntimeException("Not implemented.");
}
+
+ /**
+ * Provides an assertion that the AMQP channel was forcibly closed by an error condition.
+ *
+ * @param testProps The test configuration properties.
+ * @return An assertion that the AMQP channel was forcibly closed by an error condition.
+ */
+ public Assertion channelClosedAssertion(ParsedProperties testProps)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Provides an assertion that the publisher got a given exception during the test.
+ *
+ * @param testProps The test configuration properties.
+ * @param exceptionClass The exception class to check for.
+ * @return An assertion that the publisher got a given exception during the test.
+ */
+ public Assertion exceptionAssertion(ParsedProperties testProps, Class<? extends Exception> exceptionClass)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java
index db85921b85..863921e387 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java
@@ -23,10 +23,16 @@ package org.apache.qpid.test.framework.distributedcircuit;
import org.apache.qpid.test.framework.Assertion;
import org.apache.qpid.test.framework.Receiver;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+
/**
+ * DistributedReceiverImpl represents the status of the receiving side of a test circuit. Its main purpose is to
+ * provide assertions that can be applied to verify the behaviour of a non-local receiver.
+ *
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
+ * <tr><td> Provide assertion that the receivers received no exceptions.
+ * <tr><td> Provide assertion that the receivers received all test messages sent to it.
* </table>
*/
public class DistributedReceiverImpl implements Receiver
@@ -35,8 +41,9 @@ public class DistributedReceiverImpl implements Receiver
* Provides an assertion that the receivers encountered no exceptions.
*
* @return An assertion that the receivers encountered no exceptions.
+ * @param testProps
*/
- public Assertion noExceptionsAssertion()
+ public Assertion noExceptionsAssertion(ParsedProperties testProps)
{
throw new RuntimeException("Not implemented.");
}
@@ -45,9 +52,44 @@ public class DistributedReceiverImpl implements Receiver
* Provides an assertion that the receivers got all messages that were sent to it.
*
* @return An assertion that the receivers got all messages that were sent to it.
+ * @param testProps
+ */
+ public Assertion allMessagesReceivedAssertion(ParsedProperties testProps)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Provides an assertion that the receivers got none of the messages that were sent to it.
+ *
+ * @return An assertion that the receivers got none of the messages that were sent to it.
+ * @param testProps
+ */
+ public Assertion noMessagesReceivedAssertion(ParsedProperties testProps)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Provides an assertion that the AMQP channel was forcibly closed by an error condition.
+ *
+ * @param testProps The test configuration properties.
+ * @return An assertion that the AMQP channel was forcibly closed by an error condition.
+ */
+ public Assertion channelClosedAssertion(ParsedProperties testProps)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Provides an assertion that the receiver got a given exception during the test.
+ *
+ * @param testProps
+ *@param exceptionClass The exception class to check for. @return An assertion that the receiver got a given exception during the test.
*/
- public Assertion allMessagesAssertion()
+ public Assertion exceptionAssertion(ParsedProperties testProps, Class<? extends Exception> exceptionClass)
{
throw new RuntimeException("Not implemented.");
}
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java
index 95f02aa70e..dce2706bc4 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java
@@ -24,10 +24,9 @@ import org.apache.log4j.Logger;
import org.apache.qpid.test.framework.*;
import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
-import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
import javax.jms.*;
@@ -48,7 +47,15 @@ import javax.jms.*;
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
+ * <tr><td> Provide a message producer for sending messages.
+ * <td> {@link CircuitEnd}, {@link LocalCircuitFactory}, {@link TestUtils}
+ * <tr><td> Provide a message consumer for receiving messages.
+ * <td> {@link CircuitEnd}, {@link LocalCircuitFactory}, {@link TestUtils}
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters. <td> {@link MessagingTestConfigProperties}
+ * <tr><td> Adapt to assigned roles. <td> {@link TestClientControlledTest.Roles}
+ * <tr><td> Perform test case actions. <td> {@link MessageMonitor}
+ * <tr><td> Generate test reports. <td> {@link MessageMonitor}
* </table>
*/
public class TestClientCircuitEnd implements CircuitEnd, TestClientControlledTest
@@ -145,13 +152,15 @@ public class TestClientCircuitEnd implements CircuitEnd, TestClientControlledTes
connection = TestUtils.createConnection(testProps);
// Create a circuit end that matches the assigned role and test parameters.
+ LocalCircuitFactory circuitFactory = new LocalCircuitFactory();
+
switch (role)
{
// Check if the sender role is being assigned, and set up a message producer if so.
case SENDER:
// Set up the publisher.
- circuitEnd = LocalCircuitImpl.createPublisherCircuitEnd(connection, testProps, 0L);
+ circuitEnd = circuitFactory.createPublisherCircuitEnd(connection, testProps, 0L);
// Create a custom message monitor that will be updated on every message sent.
messageMonitor = new MessageMonitor();
@@ -162,7 +171,7 @@ public class TestClientCircuitEnd implements CircuitEnd, TestClientControlledTes
case RECEIVER:
// Set up the receiver.
- circuitEnd = LocalCircuitImpl.createReceiverCircuitEnd(connection, testProps, 0L);
+ circuitEnd = circuitFactory.createReceiverCircuitEnd(connection, testProps, 0L);
// Use the message monitor from the consumer for stats.
messageMonitor = getMessageMonitor();
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java
index f79cde9dcc..55f05ec6f2 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java
@@ -20,33 +20,31 @@
*/
package org.apache.qpid.test.framework.distributedtesting;
+import java.net.InetAddress;
+import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import javax.jms.*;
+
import junit.framework.Test;
import junit.framework.TestResult;
import junit.framework.TestSuite;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
-
import org.apache.qpid.test.framework.FrameworkBaseCase;
import org.apache.qpid.test.framework.MessagingTestConfigProperties;
import org.apache.qpid.test.framework.TestClientDetails;
import org.apache.qpid.test.framework.TestUtils;
import org.apache.qpid.test.framework.clocksynch.UDPClockReference;
import org.apache.qpid.util.ConversationFactory;
-import org.apache.qpid.util.PrettyPrintingUtils;
-
-import uk.co.thebadgerset.junit.extensions.TKTestRunner;
-import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
-import uk.co.thebadgerset.junit.extensions.util.CommandLineParser;
-import uk.co.thebadgerset.junit.extensions.util.MathUtils;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
-
-import javax.jms.*;
-import java.net.InetAddress;
-import java.util.*;
-import java.util.concurrent.LinkedBlockingQueue;
+import org.apache.qpid.junit.extensions.TKTestRunner;
+import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
+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;
/**
* <p/>Implements the coordinator client described in the interop testing specification
@@ -56,7 +54,7 @@ import java.util.concurrent.LinkedBlockingQueue;
* <p><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
* <tr><td> Find out what test clients are available. <td> {@link ConversationFactory}
- * <tr><td> Decorate available tests to run all available clients. <td> {@link DistributedTestDecorator}
+ * <tr><td> Decorate available tests to run on all available clients. <td> {@link DistributedTestDecorator}
* <tr><td> Attach XML test result logger.
* <tr><td> Terminate the interop testing framework.
* </table>
@@ -64,8 +62,6 @@ import java.util.concurrent.LinkedBlockingQueue;
* @todo Should accumulate failures over all tests, and return with success or fail code based on all results. May need
* to write a special TestResult to do this properly. At the moment only the last one used will be tested for
* errors, as the start method creates a fresh one for each test case run.
- *
- * @todo Remove hard coding of test cases and put on command line instead.
*/
public class Coordinator extends TKTestRunner
{
@@ -119,27 +115,28 @@ public class Coordinator extends TKTestRunner
/**
* Creates an interop test coordinator on the specified broker and virtual host.
*
- * @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 verbose Whether to print comments during test run.
- * @param brokerUrl The URL of the broker to connect to.
- * @param virtualHost The virtual host to run all tests on. Optional, may be <tt>null</tt>.
- * @param engine The distributed test engine type to run the tests with.
- * @param terminate <tt>true</tt> if test client nodes should be terminated at the end of the tests.
- * @param csv <tt>true</tt> if the CSV results listener should be attached.
- * @param xml <tt>true</tt> if the XML results listener should be attached.
+ * @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 verbose Whether to print comments during test run.
+ * @param brokerUrl The URL of the broker to connect to.
+ * @param virtualHost The virtual host to run all tests on. Optional, may be <tt>null</tt>.
+ * @param engine The distributed test engine type to run the tests with.
+ * @param terminate <tt>true</tt> if test client nodes should be terminated at the end of the tests.
+ * @param csv <tt>true</tt> if the CSV results listener should be attached.
+ * @param xml <tt>true</tt> if the XML results listener should be attached.
+ * @param decoratorFactories List of factories for user specified decorators.
*/
public Coordinator(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName,
String reportDir, String runName, boolean verbose, String brokerUrl, String virtualHost, TestEngine engine,
- boolean terminate, boolean csv, boolean xml)
+ boolean terminate, boolean csv, boolean xml, List<TestDecoratorFactory> decoratorFactories)
{
- super(repetitions, duration, threads, delay, params, testCaseName, reportDir, runName, csv, xml, verbose);
+ super(repetitions, duration, threads, delay, params, testCaseName, reportDir, runName, csv, xml, decoratorFactories);
log.debug("public Coordinator(Integer repetitions = " + repetitions + " , Long duration = " + duration
+ ", int[] threads = " + Arrays.toString(threads) + ", int delay = " + delay + ", int[] params = "
@@ -221,7 +218,11 @@ public class Coordinator extends TKTestRunner
},
{ "s", "The size parameter to run tests with.", "size", "false", MathUtils.SEQUENCE_REGEXP },
{ "v", "Verbose mode.", null, "false" },
- { "n", "A name for this test run, used to name the output file.", "name", "true" }
+ { "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"
+ }
}), testContextProperties));
// Extract the command line options.
@@ -234,13 +235,13 @@ public class Coordinator extends TKTestRunner
boolean terminate = options.getPropertyAsBoolean("t");
boolean csvResults = options.getPropertyAsBoolean("-csv");
boolean xmlResults = options.getPropertyAsBoolean("-xml");
-
String threadsString = options.getProperty("c");
Integer repetitions = options.getPropertyAsInteger("r");
String durationString = options.getProperty("d");
String paramsString = options.getProperty("s");
boolean verbose = options.getPropertyAsBoolean("v");
String testRunName = options.getProperty("n");
+ String decorators = options.getProperty("X:decorators");
int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString);
int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString);
@@ -253,6 +254,9 @@ public class Coordinator extends TKTestRunner
Collection<Class<? extends FrameworkBaseCase>> testCaseClasses =
new ArrayList<Class<? extends FrameworkBaseCase>>();
+ // Create a list of test decorator factories for use specified decorators to be applied.
+ List<TestDecoratorFactory> decoratorFactories = parseDecorators(decorators);
+
// Scan for available test cases using a classpath scanner.
// ClasspathScanner.getMatches(DistributedTestCase.class, "^Test.*", true);
@@ -306,7 +310,7 @@ public class Coordinator extends TKTestRunner
// Create a coordinator and begin its test procedure.
Coordinator coordinator =
new Coordinator(repetitions, duration, threads, 0, params, null, reportDir, testRunName, verbose, brokerUrl,
- virtualHost, engine, terminate, csvResults, xmlResults);
+ virtualHost, engine, terminate, csvResults, xmlResults, decoratorFactories);
TestResult testResult = coordinator.start(testClassNames);
@@ -324,6 +328,7 @@ public class Coordinator extends TKTestRunner
{
log.debug("Top level handler caught execption.", e);
console.info(e.getMessage());
+ e.printStackTrace();
System.exit(EXCEPTION_EXIT);
}
}
@@ -339,8 +344,7 @@ public class Coordinator extends TKTestRunner
*/
public TestResult start(String[] testClassNames) throws Exception
{
- log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames)
- + ": called");
+ log.debug("public TestResult start(String[] testClassNames = " + Arrays.toString(testClassNames) + ": called");
// Connect to the broker.
connection = TestUtils.createConnection(TestContextProperties.getInstance());
@@ -474,7 +478,7 @@ public class Coordinator extends TKTestRunner
{
log.debug("targetTest is a TestSuite");
- TestSuite suite = (TestSuite) test;
+ TestSuite suite = (TestSuite)test;
int numTests = suite.countTestCases();
log.debug("There are " + numTests + " in the suite.");
@@ -494,6 +498,9 @@ public class Coordinator extends TKTestRunner
log.debug("Wrapped with a WrappedSuiteTestDecorator.");
}
+ // Apply any optional user specified decorators.
+ targetTest = applyOptionalUserDecorators(targetTest);
+
// Wrap the tests in a suitable distributed test decorator, to perform the invite/test cycle.
targetTest = newTestDecorator(targetTest, enlistedClients, conversationFactory, connection);
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java
index d11348cbad..d2f8ca896c 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java
@@ -29,7 +29,7 @@ import org.apache.qpid.test.framework.TestClientDetails;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
import javax.jms.Connection;
import javax.jms.Destination;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java
index 6fc3b2937e..d6ae390a4a 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java
@@ -32,14 +32,13 @@ import org.apache.qpid.test.framework.sequencers.CircuitFactory;
import org.apache.qpid.test.framework.sequencers.FanOutCircuitFactory;
import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
-import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java
index 130173cc96..38ab66d6ae 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java
@@ -31,7 +31,7 @@ import org.apache.qpid.test.framework.sequencers.CircuitFactory;
import org.apache.qpid.test.framework.sequencers.InteropCircuitFactory;
import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
import javax.jms.Connection;
@@ -50,7 +50,7 @@ import java.util.*;
* <tr><td> Broadcast test invitations and collect enlists. <td> {@link org.apache.qpid.util.ConversationFactory}.
* <tr><td> Output test failures for clients unwilling to run the test case. <td> {@link Coordinator}
* <tr><td> Execute distributed test cases. <td> {@link FrameworkBaseCase}
- * <tr><td> Fail non participating pairings. <td> {@link OptOutTestCase}
+ * <tr><td> Fail non-participating pairings. <td> {@link OptOutTestCase}
* </table>
*/
public class InteropTestDecorator extends DistributedTestDecorator
@@ -206,3 +206,4 @@ public class InteropTestDecorator extends DistributedTestDecorator
}
}
}
+
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
new file mode 100644
index 0000000000..1c138fe575
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java
@@ -0,0 +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}.
+ *
+ * <p/><table><caption>Messages Handled by TestClient</caption>
+ * <tr><th> Message <th> Action
+ * <tr><td> Invite(compulsory) <td> Reply with Enlist.
+ * <tr><td> Invite(test case) <td> Reply with Enlist if test case available.
+ * <tr><td> AssignRole(test case) <td> Reply with Accept Role if matches an enlisted test. Keep test parameters.
+ * <tr><td> Start <td> Send test messages defined by test parameters. Send report on messages sent.
+ * <tr><td> Status Request <td> Send report on messages received.
+ * <tr><td> Terminate <td> Terminate the test client.
+ * <tr><td> ClockSynch <td> Synch clock against the supplied UDP address.
+ * </table>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Handle all incoming control messages. <td> {@link TestClientControlledTest}
+ * <tr><td> Configure and look up test cases by name. <td> {@link TestClientControlledTest}
+ * </table>
+ */
+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 <tt>null</tt>, 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<String, TestClientControlledTest> testCases = new HashMap<String, TestClientControlledTest>();
+
+ /** 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:
+ *
+ * <p/><table>
+ * <tr><td> -b <td> The broker URL. <td> Optional.
+ * <tr><td> -h <td> The virtual host. <td> Optional.
+ * <tr><td> -n <td> The test client name. <td> Optional.
+ * <tr><td> name=value <td> Trailing argument define name/value pairs. Added to system properties. <td> Optional.
+ * </table>
+ *
+ * @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<Class<? extends TestClientControlledTest>> testCaseClasses =
+ new ArrayList<Class<? extends TestClientControlledTest>>();
+ // 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<Class<? extends TestClientControlledTest>> loadTestCases(String... classNames)
+ {
+ List<Class<? extends TestClientControlledTest>> testCases =
+ new LinkedList<Class<? extends TestClientControlledTest>>();
+
+ for (String className : classNames)
+ {
+ try
+ {
+ Class<?> cls = ReflectionUtils.forName(className);
+ testCases.add((Class<? extends TestClientControlledTest>) 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<Class<? extends TestClientControlledTest>> testCaseClasses) throws JMSException
+ {
+ log.debug("protected void start(Collection<Class<? extends TestClientControlledTest>> testCaseClasses = "
+ + testCaseClasses + "): called");
+
+ // Create all the test case implementations and index them by the test names.
+ for (Class<? extends TestClientControlledTest> 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/listeners/XMLTestListener.java b/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java
index 3752888a9e..8a82b12832 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java
@@ -26,8 +26,8 @@ import junit.framework.TestCase;
import org.apache.log4j.Logger;
-import uk.co.thebadgerset.junit.extensions.ShutdownHookable;
-import uk.co.thebadgerset.junit.extensions.listeners.TKTestListener;
+import org.apache.qpid.junit.extensions.ShutdownHookable;
+import org.apache.qpid.junit.extensions.listeners.TKTestListener;
import java.io.IOException;
import java.io.PrintWriter;
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
new file mode 100644
index 0000000000..14ae108da8
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+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/localcircuit/LocalCircuitImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java
index 3a5ff49fd1..391091266c 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java
@@ -22,16 +22,14 @@ package org.apache.qpid.test.framework.localcircuit;
import org.apache.log4j.Logger;
-import org.apache.qpid.client.AMQSession;
import org.apache.qpid.test.framework.*;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.*;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicLong;
/**
* LocalCircuitImpl provides an implementation of the test circuit. This is a local only circuit implementation that
@@ -47,7 +45,7 @@ import java.util.concurrent.atomic.AtomicLong;
* <tr><td> Apply assertions against the circuits state. <td> {@link Assertion}
* <tr><td> Send test messages over the circuit.
* <tr><td> Perform the default test procedure on the circuit.
- * <tr><td> Provide access to connection and controlSession exception monitors <td> {@link ExceptionMonitor}
+ * <tr><td> Provide access to connection and controlSession exception monitors. <td> {@link ExceptionMonitor}
* </table>
*/
public class LocalCircuitImpl implements Circuit
@@ -55,9 +53,6 @@ public class LocalCircuitImpl implements Circuit
/** Used for debugging. */
private static final Logger log = Logger.getLogger(LocalCircuitImpl.class);
- /** Used to create unique destination names for each test. */
- private static AtomicLong uniqueDestsId = new AtomicLong();
-
/** Holds the test configuration for the circuit. */
private ParsedProperties testProps;
@@ -86,7 +81,7 @@ public class LocalCircuitImpl implements Circuit
* @param connection The connection.
* @param connectionExceptionMonitor The connection exception monitor.
*/
- protected LocalCircuitImpl(ParsedProperties testProps, LocalPublisherImpl publisher, LocalReceiverImpl receiver,
+ public LocalCircuitImpl(ParsedProperties testProps, LocalPublisherImpl publisher, LocalReceiverImpl receiver,
Connection connection, ExceptionMonitor connectionExceptionMonitor)
{
this.testProps = testProps;
@@ -102,159 +97,6 @@ public class LocalCircuitImpl implements Circuit
}
/**
- * Creates a local test circuit from the specified test parameters.
- *
- * @param testProps The test parameters.
- *
- * @return A connected and ready to start, test circuit.
- */
- public static Circuit createCircuit(ParsedProperties testProps)
- {
- // Create a standard publisher/receivers test client pair on a shared connection, individual sessions.
- try
- {
- // Cast the test properties into a typed interface for convenience.
- MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
-
- // 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(testProps);
-
- // 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 = new LocalPublisherImpl(publisherEnd);
- LocalReceiverImpl receiver = new LocalReceiverImpl(receiverEnd);
-
- return new LocalCircuitImpl(testProps, publisher, receiver, connection, publisher.getExceptionMonitor());
- }
- catch (JMSException e)
- {
- throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e);
- }
- }
-
- /**
- * 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 static CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
- throws JMSException
- {
- log.debug(
- "public static 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.getTransacted(), 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 static CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
- throws JMSException
- {
- log.debug(
- "public static 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.getTransacted(), 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);
- }
-
- /**
* Gets the interface on the publishing end of the circuit.
*
* @return The publishing end of the circuit.
@@ -342,7 +184,7 @@ public class LocalCircuitImpl implements Circuit
}
catch (JMSException e)
{
- throw new RuntimeException("Got JMSException during close.", e);
+ throw new RuntimeException("Got JMSException during close:" + e.getMessage(), e);
}
}
@@ -351,16 +193,24 @@ public class LocalCircuitImpl implements Circuit
*/
protected void send()
{
- boolean transactional = testProps.getPropertyAsBoolean(MessagingTestConfigProperties.TRANSACTED_PROPNAME);
+ // Cast the test properties into a typed interface for convenience.
+ MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
+
+ boolean transactional = props.getPublisherTransacted();
+ boolean rollback = props.getRollbackPublisher();
- // Send an immediate message through the publisher and ensure that it results in a JMSException.
+ // Send a message through the publisher and log any exceptions raised.
try
{
CircuitEnd end = getLocalPublisherCircuitEnd();
end.send(createTestMessage(end));
- if (transactional)
+ if (rollback)
+ {
+ end.getSession().rollback();
+ }
+ else if (transactional)
{
end.getSession().commit();
}
@@ -406,12 +256,12 @@ public class LocalCircuitImpl implements Circuit
// Request a status report.
check();
- // Apply all of the requested assertions, keeping record of any that fail.
- List<Assertion> failures = applyAssertions(assertions);
-
// Clean up the publisher/receivers/controlSession/connections.
close();
+ // Apply all of the requested assertions, keeping record of any that fail.
+ List<Assertion> failures = applyAssertions(assertions);
+
// Return any failed assertions to the caller.
return failures;
}
@@ -427,7 +277,10 @@ public class LocalCircuitImpl implements Circuit
*/
private Message createTestMessage(CircuitEnd client) throws JMSException
{
- return client.getSession().createTextMessage("Hello");
+ // Cast the test properties into a typed interface for convenience.
+ MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
+
+ return TestUtils.createTestMessageOfSize(client.getSession(), props.getMessageSize());
}
/**
@@ -450,3 +303,4 @@ public class LocalCircuitImpl implements Circuit
return exceptionMonitor;
}
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java
index 5c5807dcd9..3ec3f62538 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java
@@ -20,10 +20,10 @@
*/
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;
@@ -31,7 +31,7 @@ import javax.jms.Session;
/**
* Provides an implementation of the {@link Publisher} interface and wraps a single message producer and consumer on
* a single controlSession, as a {@link CircuitEnd}. A local publisher also acts as a circuit end, because for a locally
- * located circuit the assertions may be applied directly, there does not need to be any inter process messaging
+ * located circuit the assertions may be applied directly, there does not need to be any inter-process messaging
* between the publisher and its single circuit end, in order to ascertain its status.
*
* <p/><table id="crc"><caption>CRC Card</caption>
@@ -46,14 +46,17 @@ import javax.jms.Session;
public class LocalPublisherImpl extends CircuitEndBase implements Publisher
{
/** Holds a reference to the containing circuit. */
- private LocalCircuitImpl circuit;
+ protected LocalCircuitImpl circuit;
/**
- * Creates a circuit end point on the specified producer, consumer and controlSession.
+ * 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 LocalPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session,
MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor)
@@ -74,9 +77,11 @@ public class LocalPublisherImpl extends CircuitEndBase implements Publisher
/**
* Provides an assertion that the publisher encountered no exceptions.
*
+ * @param testProps
+ *
* @return An assertion that the publisher encountered no exceptions.
*/
- public Assertion noExceptionsAssertion()
+ public Assertion noExceptionsAssertion(ParsedProperties testProps)
{
return new AssertionBase()
{
@@ -109,41 +114,26 @@ public class LocalPublisherImpl extends CircuitEndBase implements Publisher
}
/**
- * Provides an assertion that the publisher got a no consumers exception on every message.
+ * Provides an assertion that the AMQP channel was forcibly closed by an error condition.
+ *
+ * @param testProps The test configuration properties.
*
- * @return An assertion that the publisher got a no consumers exception on every message.
+ * @return An assertion that the AMQP channel was forcibly closed by an error condition.
*/
- public Assertion noConsumersAssertion()
+ public Assertion channelClosedAssertion(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;
- }
- };
+ return new NotApplicableAssertion(testProps);
}
/**
- * Provides an assertion that the publisher got a no rout exception on every message.
+ * Provides an assertion that the publisher got a given exception during the test.
+ *
+ * @param testProps The test configuration properties.
+ * @param exceptionClass The exception class to check for.
*
- * @return An assertion that the publisher got a no rout exception on every message.
+ * @return An assertion that the publisher got a given exception during the test.
*/
- public Assertion noRouteAssertion()
+ public Assertion exceptionAssertion(ParsedProperties testProps, final Class<? extends Exception> exceptionClass)
{
return new AssertionBase()
{
@@ -152,11 +142,11 @@ public class LocalPublisherImpl extends CircuitEndBase implements Publisher
boolean passed = true;
ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor();
- if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class))
+ if (!connectionExceptionMonitor.assertExceptionOfType(exceptionClass))
{
passed = false;
- addError("Was expecting linked exception type " + AMQNoRouteException.class.getName()
+ addError("Was expecting linked exception type " + exceptionClass.getName()
+ " on the connection.\n");
addError((connectionExceptionMonitor.size() > 0)
? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor)
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java
index 0c5dae096e..74f414c974 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java
@@ -22,6 +22,8 @@ package org.apache.qpid.test.framework.localcircuit;
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;
@@ -46,11 +48,14 @@ public class LocalReceiverImpl extends CircuitEndBase implements Receiver
private LocalCircuitImpl circuit;
/**
- * Creates a circuit end point on the specified producer, consumer and controlSession.
+ * 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 LocalReceiverImpl(MessageProducer producer, MessageConsumer consumer, Session session,
MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor)
@@ -71,21 +76,60 @@ public class LocalReceiverImpl extends CircuitEndBase implements Receiver
/**
* Provides an assertion that the receivers encountered no exceptions.
*
+ * @param testProps The test configuration properties.
+ *
* @return An assertion that the receivers encountered no exceptions.
*/
- public Assertion noExceptionsAssertion()
+ public Assertion noExceptionsAssertion(ParsedProperties testProps)
+ {
+ return new NotApplicableAssertion(testProps);
+ }
+
+ /**
+ * Provides an assertion that the AMQP channel was forcibly closed by an error condition.
+ *
+ * @param testProps The test configuration properties.
+ *
+ * @return An assertion that the AMQP channel was forcibly closed by an error condition.
+ */
+ public Assertion channelClosedAssertion(ParsedProperties testProps)
{
- return null;
+ return new NotApplicableAssertion(testProps);
}
/**
* Provides an assertion that the receivers got all messages that were sent to it.
*
+ * @param testProps The test configuration properties.
+ *
* @return An assertion that the receivers got all messages that were sent to it.
*/
- public Assertion allMessagesAssertion()
+ public Assertion allMessagesReceivedAssertion(ParsedProperties testProps)
+ {
+ return new NotApplicableAssertion(testProps);
+ }
+
+ /**
+ * Provides an assertion that the receivers got none of the messages that were sent to it.
+ *
+ * @param testProps The test configuration properties.
+ *
+ * @return An assertion that the receivers got none of the messages that were sent to it.
+ */
+ public Assertion noMessagesReceivedAssertion(ParsedProperties testProps)
+ {
+ return new NotApplicableAssertion(testProps);
+ }
+
+ /**
+ * Provides an assertion that the receiver got a given exception during the test.
+ *
+ * @param testProps The test configuration properties.
+ * @param exceptionClass The exception class to check for. @return An assertion that the receiver got a given exception during the test.
+ */
+ public Assertion exceptionAssertion(ParsedProperties testProps, Class<? extends Exception> exceptionClass)
{
- return null;
+ return new NotApplicableAssertion(testProps);
}
/**
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
new file mode 100644
index 0000000000..4545e3c164
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Substitute the circuit factory with an AMQP/Qpid specific one.
+ * </table>
+ *
+ * @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
new file mode 100644
index 0000000000..3a048ac042
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java
@@ -0,0 +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}.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Setup automatic failures for in-vm brokers. <td> {@link CauseFailureInVM}
+ * <tr><td> Setup user generated failures for external brokers. <td> {@link CauseFailureUserPrompt}.
+ * <tr><td>
+ * </table>
+ *
+ * @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
new file mode 100644
index 0000000000..b63ac43601
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java
@@ -0,0 +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;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Cause messaging broker failure on the active in-vm broker.
+ * <td> {@link TransportConnection}, {@link ApplicationRegistry}
+ * </table>
+ */
+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
new file mode 100644
index 0000000000..bcf052ea06
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java
@@ -0,0 +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}.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create/Destroy an in-vm broker on every test run.
+ * </table>
+ *
+ * @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/framework/sequencers/BaseCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java
index 4514fb836c..c7bde1ae03 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java
@@ -31,13 +31,20 @@ import java.util.List;
import java.util.Properties;
/**
+ * BaseCircuitFactory provides some functionality common to all {@link CircuitFactory}s, such as the details of
+ * all {@link org.apache.qpid.test.framework.distributedtesting.TestClient}s that make up the end-points of
+ * the circuits that the factory creates, and an active {@link ConversationFactory} that can be used to generate
+ * control conversations with those circuit end-points.
+ *
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
+ * <tr><td> Hold the details of the sending and receiving end-points to create circuits from.
+ * <tr><td> Provide a conversation factory to create control conversations with the end-points.
* </table>
*/
public abstract class BaseCircuitFactory implements CircuitFactory
{
+
/** Used for debugging. */
private final Logger log = Logger.getLogger(BaseCircuitFactory.class);
@@ -126,3 +133,4 @@ public abstract class BaseCircuitFactory implements CircuitFactory
return conversationFactory;
}
}
+
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java
index bf7fedcffc..fff617c583 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java
@@ -25,17 +25,13 @@ import org.apache.qpid.test.framework.Circuit;
import org.apache.qpid.test.framework.TestClientDetails;
import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-
-import javax.jms.JMSException;
-import javax.jms.Message;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import java.util.List;
-import java.util.Map;
import java.util.Properties;
/**
- * A TestCaseSequence is responsibile for creating test circuits appropriate to the context that a test case is
+ * A CircuitFactory is responsibile for creating test circuits appropriate to the context that a test case is
* running in, and providing an implementation of a standard test procedure over a test circuit.
*
* <p/><table id="crc"><caption>CRC Card</caption>
@@ -43,12 +39,6 @@ import java.util.Properties;
* <tr><td> Provide a standard test procedure over a test circuit.
* <tr><td> Construct test circuits appropriate to a tests context.
* </table>
- *
- * @todo The sequence test method is deprecated, in favour of using test circuits instead. This interface might be
- * better renamed to somethign like CircuitFactory, also the split between this interface and
- * DistributedTestSequencer could be removed and DistributedTestCase functionality merged into FrameworkBaseCase.
- * This is so that any test case written on top of the framework base case can be distributed, without having
- * to extend a different base test class.
*/
public interface CircuitFactory
{
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java
index 2ff6725365..d1c39ff3ff 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java
@@ -29,7 +29,7 @@ import org.apache.qpid.test.framework.TestUtils;
import org.apache.qpid.test.framework.distributedcircuit.DistributedCircuitImpl;
import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.Destination;
import javax.jms.JMSException;
@@ -46,7 +46,7 @@ import java.util.Properties;
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
+ * <tr><td> Create distributed circuits from one to many test nodes, for fanout style testing.
* </table>
*
* @todo Adapt this to be an n*m topology circuit factory. Need to add circuit topology definitions to the test
@@ -57,7 +57,7 @@ import java.util.Properties;
*
* @todo The createCircuit methods on this and InteropCircuitFactory are going to be identical. This is because the
* partitioning into senders and receivers is already done by the test decorators. Either eliminate these factories
- * as unnesesary, or move the partitioning functionaility into the factories, in which case the test decorators
+ * as unnesesary, or move the partitioning functionality into the factories, in which case the test decorators
* can probably be merged or eliminated. There is confusion over the placement of responsibilities between the
* factories and the test decorators... although the test decorators may well do more than just circuit creation
* in the future. For example, there may have to be a special decorator for test repetition that does one circuit
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java
index 75df5c65ea..feb87e7b9c 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java
@@ -29,7 +29,7 @@ import org.apache.qpid.test.framework.TestUtils;
import org.apache.qpid.test.framework.distributedcircuit.DistributedCircuitImpl;
import org.apache.qpid.util.ConversationFactory;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.Destination;
import javax.jms.JMSException;
@@ -41,10 +41,20 @@ import java.util.List;
import java.util.Properties;
/**
+ * InteropCircuitFactory is a circuit factory that creates distributed test circuits. Given a set of participating
+ * test client nodes, it assigns one node to the SENDER role and one the RECEIVER role.
+ *
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td>
+ * <tr><td> Create distributed circuits from pairs of test nodes, for interop style testing.
* </table>
+ *
+ * @todo The partitioning of a set of nodes into sender and receiver roles is actually done by the interop test
+ * decorator. See the todo comment in FanOutCircuitFactory about merging the factories with the decorators, or
+ * more carefully dividing up responsibilities between them.
+ *
+ * @todo The squenceTest code is deprecated, but currently still used by the interop tests. It will be removed once it
+ * have been fully replaced by the default test procedure.
*/
public class InteropCircuitFactory extends BaseCircuitFactory
{
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
new file mode 100644
index 0000000000..e7d874ffa9
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ *
+ * @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
new file mode 100644
index 0000000000..845c3ed9c8
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java
@@ -0,0 +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.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is
+ * connected.
+ * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is
+ * connected.
+ * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is
+ * connected.
+ * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is
+ * connected.
+ * </table>
+ *
+ * @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
new file mode 100644
index 0000000000..066b4e24ba
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java
@@ -0,0 +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.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected
+ * but the route exists.
+ * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
+ * the route exists.
+ * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is
+ * connected.
+ * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is
+ * connected.
+ * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected
+ * but the route exists.
+ * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
+ * the route exists.
+ * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is
+ * connected.
+ * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is
+ * connected.
+ * </table>
+ *
+ * @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
new file mode 100644
index 0000000000..f39d22bc67
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java
@@ -0,0 +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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check messages sent but rolled back are never received.
+ * <tr><td> Check messages received but rolled back are redelivered on subsequent receives.
+ * <tr><td> Attempting to rollback outside of a transaction results in an IllegalStateException.
+ * </table>
+ */
+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
new file mode 100644
index 0000000000..f752ccba00
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java
@@ -0,0 +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.
+ *
+ * <p/>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.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ *
+ * @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<Message> receivedMessages = new LinkedList<Message>();
+
+ // 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/test/unit/ack/AcknowledgeTest.java b/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java
new file mode 100644
index 0000000000..8fddf651b4
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java
@@ -0,0 +1,174 @@
+package org.apache.qpid.test.unit.ack;
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+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;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.test.VMTestCase;
+
+public class AcknowledgeTest extends VMTestCase
+{
+ private static final int NUM_MESSAGES = 50;
+ private Connection _con;
+ private Queue _queue;
+ private MessageProducer _producer;
+ private Session _producerSession;
+ private Session _consumerSession;
+ private MessageConsumer _consumerA;
+ private MessageConsumer _consumerB;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _queue = (Queue) _context.lookup("queue");
+
+ //CreateQueue
+ ((ConnectionFactory) _context.lookup("connection")).createConnection().createSession(false, Session.AUTO_ACKNOWLEDGE).createConsumer(_queue).close();
+
+ //Create Producer put some messages on the queue
+ _con = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ _con.start();
+ }
+
+ private void init(boolean transacted, int mode) throws JMSException {
+ _producerSession = _con.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _consumerSession = _con.createSession(transacted, mode);
+ _producer = _producerSession.createProducer(_queue);
+ _consumerA = _consumerSession.createConsumer(_queue);
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ try
+ {
+ TransportConnection.killAllVMBrokers();
+ ApplicationRegistry.removeAll();
+ }
+ catch (Exception e)
+ {
+ fail("Unable to clean up");
+ }
+
+ }
+
+ private void consumeMessages(int toConsume, MessageConsumer consumer) throws JMSException
+ {
+ Message msg;
+ for (int i = 0; i < toConsume; i++)
+ {
+ msg = consumer.receive(1000);
+ assertNotNull("Message " + i + " was null!", msg);
+ assertEquals("message " + i, ((TextMessage) msg).getText());
+ }
+ }
+
+ private void sendMessages(int totalMessages) throws JMSException
+ {
+ for (int i = 0; i < totalMessages; i++)
+ {
+ _producer.send(_producerSession.createTextMessage("message " + i));
+ }
+ }
+
+ private void testMessageAck(boolean transacted, int mode) throws Exception
+ {
+ init(transacted, mode);
+ sendMessages(NUM_MESSAGES/2);
+ Thread.sleep(1500);
+ _consumerB = _consumerSession.createConsumer(_queue);
+ sendMessages(NUM_MESSAGES/2);
+ int count = 0;
+ Message msg = _consumerB.receive(1500);
+ while (msg != null)
+ {
+ if (mode == Session.CLIENT_ACKNOWLEDGE)
+ {
+ msg.acknowledge();
+ }
+ count++;
+ msg = _consumerB.receive(1500);
+ }
+ if (transacted)
+ {
+ _consumerSession.commit();
+ }
+ _consumerA.close();
+ _consumerB.close();
+ _consumerSession.close();
+ assertEquals("Wrong number of messages on queue", NUM_MESSAGES - count,
+ ((AMQSession) _producerSession).getQueueDepth((AMQDestination) _queue));
+
+ // Clean up messages that may be left on the queue
+ _consumerSession = _con.createSession(transacted, mode);
+ _consumerA = _consumerSession.createConsumer(_queue);
+ msg = _consumerA.receive(1500);
+ while (msg != null)
+ {
+ if (mode == Session.CLIENT_ACKNOWLEDGE)
+ {
+ msg.acknowledge();
+ }
+ msg = _consumerA.receive(1500);
+ }
+ _consumerA.close();
+ if (transacted)
+ {
+ _consumerSession.commit();
+ }
+ _consumerSession.close();
+ super.tearDown();
+ }
+
+ public void test2ConsumersAutoAck() throws Exception
+ {
+ testMessageAck(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ public void test2ConsumersClientAck() throws Exception
+ {
+ testMessageAck(true, Session.CLIENT_ACKNOWLEDGE);
+ }
+
+ public void test2ConsumersTx() throws Exception
+ {
+ testMessageAck(true, Session.AUTO_ACKNOWLEDGE);
+ }
+
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java b/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java
index 352cb80211..00cb458c86 100644
--- a/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java
+++ b/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java
@@ -92,7 +92,7 @@ import java.util.concurrent.atomic.AtomicLong;
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><th> Associate messages to an ongoing conversation using correlation ids.
+ * <tr><td> Associate messages to an ongoing conversation using correlation ids.
* <tr><td> Auto manage sessions for conversations.
* <tr><td> Store messages not in a conversation in dead letter box.
* </table>