summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2008-04-21 13:55:26 +0000
committerRobert Godfrey <rgodfrey@apache.org>2008-04-21 13:55:26 +0000
commit1d69ea16b30dd67ac32683e6dc512f4c58ef93f1 (patch)
treeb7d326570570b53936c6512d58c465c76244e925
parenta53cbaad17df415e98f22cc42f2512467936bbc6 (diff)
parentc0c3c38f032200e786cf5a4404cfa40a0c95f5e8 (diff)
downloadqpid-python-1d69ea16b30dd67ac32683e6dc512f4c58ef93f1.tar.gz
create branch for broker refactoring
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/broker-queue-refactor@650146 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--cpp/gen/Makefile.am9
-rw-r--r--cpp/lib/broker/SessionHandlerImpl.cpp27
-rw-r--r--cpp/lib/broker/SessionHandlerImpl.h22
-rw-r--r--cpp/lib/client/ClientChannel.cpp16
-rw-r--r--cpp/lib/client/Connector.cpp2
-rw-r--r--cpp/lib/client/Connector.h2
-rw-r--r--cpp/lib/common/Makefile.am3
-rw-r--r--cpp/lib/common/framing/AMQDataBlock.cpp (renamed from java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java)13
-rw-r--r--cpp/lib/common/framing/AMQDataBlock.h4
-rw-r--r--cpp/lib/common/framing/AMQFrame.cpp12
-rw-r--r--cpp/lib/common/framing/AMQFrame.h2
-rw-r--r--cpp/lib/common/framing/FrameList.cpp69
-rw-r--r--cpp/lib/common/framing/FrameList.h (renamed from java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java)34
-rw-r--r--cpp/lib/common/framing/OutputHandler.h3
-rw-r--r--cpp/lib/common/framing/ProtocolInitiation.cpp6
-rw-r--r--cpp/lib/common/framing/ProtocolInitiation.h1
-rw-r--r--cpp/lib/common/sys/apr/LFSessionContext.cpp8
-rw-r--r--cpp/lib/common/sys/apr/LFSessionContext.h6
-rw-r--r--cpp/tests/ChannelTest.cpp4
-rw-r--r--cpp/tests/InMemoryContentTest.cpp5
-rw-r--r--cpp/tests/LazyLoadedContentTest.cpp4
-rw-r--r--cpp/tests/MessageTest.cpp4
-rw-r--r--cpp/tests/client_test.cpp34
-rwxr-xr-xcpp/tests/run-python-tests2
-rw-r--r--dotnet/Qpid.Client/Client/AMQConnection.cs5
-rw-r--r--dotnet/Qpid.Client/Client/AmqChannel.cs30
-rw-r--r--dotnet/Qpid.Client/Client/BasicMessageConsumer.cs37
-rw-r--r--dotnet/Qpid.Integration.Tests/interactive/SendReceiveTest.cs181
-rw-r--r--dotnet/Qpid.Integration.Tests/testcases/BaseMessagingTestFixture.cs14
-rw-r--r--dotnet/Qpid.Integration.Tests/testcases/DurableSubscriptionTest.cs22
-rw-r--r--dotnet/Qpid.Integration.Tests/testcases/HeadersExchangeTest.cs3
-rw-r--r--dotnet/Qpid.Integration.Tests/testcases/MandatoryMessageTest.cs50
-rw-r--r--dotnet/Qpid.Integration.Tests/testcases/ProducerMultiConsumerTest.cs8
-rw-r--r--dotnet/default.build2
-rw-r--r--gentools/src/org/apache/qpid/gentools/Generator.java2
-rw-r--r--gentools/templ.cpp/method/MethodBodyClass.h.tmpl (renamed from gentools/templ.cpp/MethodBodyClass.h.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_ClientOperations.h.tmpl (renamed from gentools/templ.cpp/AMQP_ClientOperations.h.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_ClientProxy.cpp.tmpl (renamed from gentools/templ.cpp/AMQP_ClientProxy.cpp.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_ClientProxy.h.tmpl (renamed from gentools/templ.cpp/AMQP_ClientProxy.h.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_Constants.h.tmpl (renamed from gentools/templ.cpp/AMQP_Constants.h.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_HighestVersion.h.tmpl (renamed from gentools/templ.cpp/AMQP_HighestVersion.h.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_MethodVersionMap.cpp.tmpl (renamed from gentools/templ.cpp/AMQP_MethodVersionMap.cpp.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_MethodVersionMap.h.tmpl (renamed from gentools/templ.cpp/AMQP_MethodVersionMap.h.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_ServerOperations.h.tmpl (renamed from gentools/templ.cpp/AMQP_ServerOperations.h.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_ServerProxy.cpp.tmpl (renamed from gentools/templ.cpp/AMQP_ServerProxy.cpp.tmpl)0
-rw-r--r--gentools/templ.cpp/model/AMQP_ServerProxy.h.tmpl (renamed from gentools/templ.cpp/AMQP_ServerProxy.h.tmpl)0
-rw-r--r--java/broker/bin/qpid-server2
-rw-r--r--java/broker/etc/acl.config.xml225
-rw-r--r--java/broker/etc/config.xml78
-rw-r--r--java/broker/etc/debug.log4j.xml4
-rw-r--r--java/broker/etc/passwd3
-rw-r--r--java/broker/etc/persistent_config.xml23
-rw-r--r--java/broker/etc/qpid.passwd3
-rw-r--r--java/broker/etc/transient_config.xml5
-rw-r--r--java/broker/pom.xml2
-rw-r--r--java/broker/src/main/grammar/SelectorParser.jj17
-rw-r--r--java/broker/src/main/java/log4j.properties2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java78
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/Main.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java9
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java10
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java7
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java346
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java7
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java27
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java8
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java20
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java22
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java9
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java6
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java6
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java24
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java223
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java33
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java198
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java98
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java26
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java34
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java97
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java25
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java75
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java8
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java151
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java89
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java10
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java23
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java6
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java (renamed from java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java)70
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java58
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java12
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java42
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java41
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java183
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java (renamed from java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java)71
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java108
-rwxr-xr-xjava/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java579
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java (renamed from java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java)3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/management/UserManagement.java (renamed from java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java)2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java68
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java57
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java342
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java17
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java130
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java8
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java8
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java1
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java8
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java568
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java128
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java5
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java7
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java7
-rw-r--r--java/client-java14/pom.xml448
-rw-r--r--java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java171
-rw-r--r--java/client/pom.xml2
-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.java300
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java50
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQSession.java392
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java268
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java12
-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/BasicDeliverMethodHandler.java5
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java5
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java5
-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/ConnectionCloseMethodHandler.java7
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java64
-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/UnprocessedMessage.java235
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java186
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java74
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java19
-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.java59
-rw-r--r--java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java1
-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/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/test/unit/basic/PropertyValueTest.java9
-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/client/connection/ConnectionTest.java106
-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/close/CloseBeforeAckTest.java4
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java31
-rw-r--r--java/cluster/doc/design.docbin70144 -> 0 bytes
-rw-r--r--java/cluster/pom.xml69
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java46
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java91
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java26
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java247
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java26
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java368
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java73
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java139
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java63
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java60
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java190
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java133
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java80
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java396
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java72
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java107
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java31
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java90
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java26
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java107
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java117
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java31
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java26
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java29
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java28
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java44
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java272
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java36
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java30
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java28
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java98
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java166
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java55
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java73
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java136
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java51
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java239
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java55
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java25
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java79
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java38
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java60
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java62
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java59
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java69
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java90
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java125
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java56
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java85
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java31
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java31
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java31
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java29
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java31
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java83
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java32
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java77
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java37
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java311
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java83
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java72
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java53
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java175
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java102
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java116
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java64
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java60
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java91
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java176
-rw-r--r--java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java56
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java270
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java237
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java43
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java106
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java29
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java45
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java57
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java70
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java29
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java47
-rw-r--r--java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java269
-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/codec/QpidProtocolCodecFilter.java440
-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.java5
-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.java97
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQBody.java5
-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/AMQMethodBodyImpl.java7
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java1130
-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.java8
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java8
-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/FieldTable.java96
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java8
-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/abstraction/MessagePublishInfo.java2
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java11
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java7
-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.java107
-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.java12
-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.java9
-rw-r--r--java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java11
-rw-r--r--java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java62
-rw-r--r--java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java82
-rw-r--r--java/integrationtests/pom.xml292
-rw-r--r--java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java4
-rw-r--r--java/junit-toolkit-maven-plugin/pom.xml61
-rw-r--r--java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java113
-rw-r--r--java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java274
-rw-r--r--java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java148
-rw-r--r--java/junit-toolkit/pom.xml57
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java (renamed from java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java)24
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java (renamed from java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java)27
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java239
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java485
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java145
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/package.html7
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java303
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java170
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java98
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java94
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java199
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java66
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java92
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java172
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java375
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java55
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java92
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java (renamed from java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java)90
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java81
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java625
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java694
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java131
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java49
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java73
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java175
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java43
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java134
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java532
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java264
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java132
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java400
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html6
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html12
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java787
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java494
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java428
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java390
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java94
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java131
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java202
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestUtils.java54
-rw-r--r--java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/package.html6
-rw-r--r--java/perftests/distribution/pom.xml4
-rwxr-xr-xjava/perftests/etc/scripts/PT-Qpid-13.sh12
-rwxr-xr-xjava/perftests/etc/scripts/PT-Qpid-14.sh12
-rw-r--r--java/perftests/etc/scripts/Test-ActiveMQ.sh2
-rw-r--r--java/perftests/etc/scripts/Test-SwiftMQ.sh2
-rw-r--r--java/perftests/pom.xml10
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java4
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java4
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java9
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java8
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java6
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java8
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java8
-rw-r--r--java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java12
-rw-r--r--java/plugins/pom.xml4
-rw-r--r--java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java3
-rw-r--r--java/pom.xml29
-rw-r--r--java/skimtests/README.txt10
-rw-r--r--java/skimtests/etc/jar-with-dependencies.xml91
-rw-r--r--java/skimtests/pom.xml212
-rwxr-xr-xjava/systests/etc/bin/fail.py88
-rw-r--r--java/systests/pom.xml52
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java5
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/failover/FailoverMethodTest.java66
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/plugins/PluginTest.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java15
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java12
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java9
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java5
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java20
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java10
-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.java10
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java13
-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.java55
-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.java36
-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.java (renamed from java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java)45
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java (renamed from java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java)48
-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.java (renamed from java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java)60
-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.java16
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java10
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java6
-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.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java51
-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.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java10
-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.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java6
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java3
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java27
-rw-r--r--python/tests/unbind.py76
-rw-r--r--specs/amqp.0-9.no-wip.xml4445
413 files changed, 25066 insertions, 11649 deletions
diff --git a/cpp/gen/Makefile.am b/cpp/gen/Makefile.am
index 3398f01330..dc63c8005e 100644
--- a/cpp/gen/Makefile.am
+++ b/cpp/gen/Makefile.am
@@ -32,15 +32,18 @@ DISTCLEANFILES = $(BUILT_SOURCES) timestamp gen-src.mk
#
if CAN_GENERATE_CODE
+
gentools_dir = $(srcdir)/../../gentools
spec_dir = $(srcdir)/../../specs
-spec = $(spec_dir)/amqp.0-8.xml
+spec = $(spec_dir)/amqp.0-9.no-wip.xml
gentools_srcdir = $(gentools_dir)/src/org/apache/qpid/gentools
+gentools_libs = $(gentools_dir)/lib/velocity-1.4.jar:$(gentools_dir)/lib/velocity-dep-1.4.jar
$(BUILT_SOURCES) timestamp: $(spec) $(java_sources) $(cxx_templates)
rm -f $(generated_sources)
- cd $(gentools_srcdir) && rm -f *.class && $(JAVAC) *.java
- $(JAVA) -cp $(gentools_dir)/src org.apache.qpid.gentools.Main \
+ cd $(gentools_srcdir) && rm -f *.class
+ $(JAVAC) -cp $(gentools_libs) -sourcepath $(gentools_srcdir) -d $(gentools_dir)/src $(gentools_srcdir)/*.java
+ $(JAVA) -cp $(gentools_dir)/src:$(gentools_libs) org.apache.qpid.gentools.Main \
-c -o . -t $(gentools_dir)/templ.cpp $(spec)
touch timestamp
diff --git a/cpp/lib/broker/SessionHandlerImpl.cpp b/cpp/lib/broker/SessionHandlerImpl.cpp
index b23432e29d..d1b1d996a4 100644
--- a/cpp/lib/broker/SessionHandlerImpl.cpp
+++ b/cpp/lib/broker/SessionHandlerImpl.cpp
@@ -223,7 +223,7 @@ void SessionHandlerImpl::ChannelHandlerImpl::open(u_int16_t channel, const strin
if (parent->channels[channel] == 0) {
parent->channels[channel] = new Channel(parent->client->getProtocolVersion() , parent->context, channel, parent->framemax,
parent->queues->getStore(), parent->settings.stagingThreshold);
- parent->client->getChannel().openOk(channel);
+ parent->client->getChannel().openOk(channel, "");
} else {
std::stringstream out;
out << "Channel already open: " << channel;
@@ -337,6 +337,25 @@ void SessionHandlerImpl::QueueHandlerImpl::bind(u_int16_t channel, u_int16_t /*t
}
}
+
+void SessionHandlerImpl::QueueHandlerImpl::unbind(u_int16_t channel,
+ u_int16_t /*ticket*/,
+ const string& queueName,
+ const string& exchangeName,
+ const string& routingKey,
+ const FieldTable& arguments)
+{
+ Queue::shared_ptr queue = parent->getQueue(queueName, channel);
+ Exchange::shared_ptr exchange = parent->exchanges->get(exchangeName);
+ if(exchange){
+ exchange->unbind(queue, routingKey, &arguments);
+ }else{
+ throw ChannelException(404, "Unbind failed. No such exchange: " + exchangeName);
+ }
+
+ parent->client->getQueue().unbindOk(channel);
+}
+
void SessionHandlerImpl::QueueHandlerImpl::purge(u_int16_t channel, u_int16_t /*ticket*/, const string& queueName, bool nowait){
Queue::shared_ptr queue = parent->getQueue(queueName, channel);
@@ -446,7 +465,11 @@ void SessionHandlerImpl::BasicHandlerImpl::reject(u_int16_t /*channel*/, u_int64
void SessionHandlerImpl::BasicHandlerImpl::recover(u_int16_t channel, bool requeue){
parent->getChannel(channel)->recover(requeue);
- parent->client->getBasic().recoverOk(channel);
+}
+
+void SessionHandlerImpl::BasicHandlerImpl::recoverSync(u_int16_t channel, bool requeue){
+ parent->getChannel(channel)->recover(requeue);
+ parent->client->getBasic().recoverSyncOk(channel);
}
void SessionHandlerImpl::TxHandlerImpl::select(u_int16_t channel){
diff --git a/cpp/lib/broker/SessionHandlerImpl.h b/cpp/lib/broker/SessionHandlerImpl.h
index 7e631b4505..1d81b6503f 100644
--- a/cpp/lib/broker/SessionHandlerImpl.h
+++ b/cpp/lib/broker/SessionHandlerImpl.h
@@ -162,7 +162,7 @@ class SessionHandlerImpl : public virtual qpid::sys::SessionHandler,
u_int16_t classId, u_int16_t methodId);
virtual void closeOk(u_int16_t channel);
-
+
virtual ~ChannelHandlerImpl(){}
};
@@ -177,7 +177,12 @@ class SessionHandlerImpl : public virtual qpid::sys::SessionHandler,
// Change to match new code generator function signature (adding const to string&) - kpvdr 2006-11-20
virtual void delete_(u_int16_t channel, u_int16_t ticket, const string& exchange, bool ifUnused, bool nowait);
-
+
+ virtual void bound( u_int16_t /*channel*/,
+ const string& /*exchange*/,
+ const string& /*routingKey*/,
+ const string& /*queue*/ ) {}
+
virtual ~ExchangeHandlerImpl(){}
};
@@ -195,6 +200,13 @@ class SessionHandlerImpl : public virtual qpid::sys::SessionHandler,
const string& exchange, const string& routingKey, bool nowait,
const qpid::framing::FieldTable& arguments);
+ virtual void unbind(u_int16_t channel,
+ u_int16_t ticket,
+ const string& queue,
+ const string& exchange,
+ const string& routingKey,
+ const framing::FieldTable& arguments);
+
virtual void purge(u_int16_t channel, u_int16_t ticket, const string& queue,
bool nowait);
@@ -230,6 +242,8 @@ class SessionHandlerImpl : public virtual qpid::sys::SessionHandler,
virtual void reject(u_int16_t channel, u_int64_t deliveryTag, bool requeue);
virtual void recover(u_int16_t channel, bool requeue);
+
+ virtual void recoverSync(u_int16_t channel, bool requeue);
virtual ~BasicHandlerImpl(){}
};
@@ -257,10 +271,6 @@ class SessionHandlerImpl : public virtual qpid::sys::SessionHandler,
inline virtual StreamHandler* getStreamHandler(){ throw ConnectionException(540, "Stream class not implemented"); }
inline virtual DtxHandler* getDtxHandler(){ throw ConnectionException(540, "Dtx class not implemented"); }
inline virtual TunnelHandler* getTunnelHandler(){ throw ConnectionException(540, "Tunnel class not implemented"); }
-
- // Temporary add-in to resolve version conflicts: AMQP v8.0 still defines class Test;
- // however v0.9 will not - kpvdr 2006-11-17
- inline virtual TestHandler* getTestHandler(){ throw ConnectionException(540, "Test class not implemented"); }
};
}
diff --git a/cpp/lib/client/ClientChannel.cpp b/cpp/lib/client/ClientChannel.cpp
index a97d79dcf9..92f8ae63ca 100644
--- a/cpp/lib/client/ClientChannel.cpp
+++ b/cpp/lib/client/ClientChannel.cpp
@@ -23,6 +23,7 @@
#include <ClientMessage.h>
#include <QpidError.h>
#include <MethodBodyInstances.h>
+#include <framing/FrameList.h>
using namespace boost; //to use dynamic_pointer_cast
using namespace qpid::client;
@@ -219,19 +220,25 @@ void Channel::publish(Message& msg, const Exchange& exchange, const std::string&
string e = exchange.getName();
string key = routingKey;
- out->send(new AMQFrame(version, id, new BasicPublishBody(version, 0, e, key, mandatory, immediate)));
+ std::auto_ptr<FrameList> message(new FrameList());
+
+ message->add(new AMQFrame(version, id, new BasicPublishBody(version, 0, e, key, mandatory, immediate)));
//break msg up into header frame and content frame(s) and send these
string data = msg.getData();
msg.header->setContentSize(data.length());
AMQBody::shared_ptr body(static_pointer_cast<AMQBody, AMQHeaderBody>(msg.header));
- out->send(new AMQFrame(version, id, body));
+ message->add(new AMQFrame(version, id, body));
u_int64_t data_length = data.length();
if(data_length > 0){
u_int32_t frag_size = con->getMaxFrameSize() - 8;//frame itself uses 8 bytes
- if(data_length < frag_size){
- out->send(new AMQFrame(version, id, new AMQContentBody(data)));
+ if(data_length + message->size() < frag_size){
+ message->add(new AMQFrame(version, id, new AMQContentBody(data)));
+ } else if(data_length < frag_size){
+ out->send(message.release());
+ out->send(new AMQFrame(version, id, new AMQContentBody(data)));
}else{
+ out->send(message.release());
u_int32_t offset = 0;
u_int32_t remaining = data_length - offset;
while (remaining > 0) {
@@ -244,6 +251,7 @@ void Channel::publish(Message& msg, const Exchange& exchange, const std::string&
}
}
}
+ if (message.get()) out->send(message.release());
}
void Channel::commit(){
diff --git a/cpp/lib/client/Connector.cpp b/cpp/lib/client/Connector.cpp
index a99360b840..291ebb2107 100644
--- a/cpp/lib/client/Connector.cpp
+++ b/cpp/lib/client/Connector.cpp
@@ -78,7 +78,7 @@ OutputHandler* Connector::getOutputHandler(){
return this;
}
-void Connector::send(AMQFrame* frame){
+void Connector::send(AMQDataBlock* frame){
writeBlock(frame);
if(debug) std::cout << "SENT: " << *frame << std::endl;
delete frame;
diff --git a/cpp/lib/client/Connector.h b/cpp/lib/client/Connector.h
index 44112369dc..49dcf6bb7a 100644
--- a/cpp/lib/client/Connector.h
+++ b/cpp/lib/client/Connector.h
@@ -85,7 +85,7 @@ namespace client {
virtual void setTimeoutHandler(qpid::sys::TimeoutHandler* handler);
virtual void setShutdownHandler(qpid::sys::ShutdownHandler* handler);
virtual qpid::framing::OutputHandler* getOutputHandler();
- virtual void send(qpid::framing::AMQFrame* frame);
+ virtual void send(qpid::framing::AMQDataBlock* frame);
virtual void setReadTimeout(u_int16_t timeout);
virtual void setWriteTimeout(u_int16_t timeout);
};
diff --git a/cpp/lib/common/Makefile.am b/cpp/lib/common/Makefile.am
index b2e2de2cf1..b584e93ca5 100644
--- a/cpp/lib/common/Makefile.am
+++ b/cpp/lib/common/Makefile.am
@@ -76,6 +76,7 @@ libqpidcommon_la_SOURCES = \
$(platform_src) \
$(framing)/AMQBody.cpp \
$(framing)/AMQContentBody.cpp \
+ $(framing)/AMQDataBlock.cpp \
$(framing)/AMQFrame.cpp \
$(framing)/AMQHeaderBody.cpp \
$(framing)/AMQHeartbeatBody.cpp \
@@ -84,6 +85,7 @@ libqpidcommon_la_SOURCES = \
$(framing)/BodyHandler.cpp \
$(framing)/Buffer.cpp \
$(framing)/FieldTable.cpp \
+ $(framing)/FrameList.cpp \
$(framing)/FramingContent.cpp \
$(framing)/InitiationHandler.cpp \
$(framing)/ProtocolInitiation.cpp \
@@ -114,6 +116,7 @@ nobase_pkginclude_HEADERS = \
$(framing)/BodyHandler.h \
$(framing)/Buffer.h \
$(framing)/FieldTable.h \
+ $(framing)/FrameList.h \
$(framing)/FramingContent.h \
$(framing)/HeaderProperties.h \
$(framing)/InitiationHandler.h \
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java b/cpp/lib/common/framing/AMQDataBlock.cpp
index 591e652e32..9c4d6bee63 100644
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java
+++ b/cpp/lib/common/framing/AMQDataBlock.cpp
@@ -18,11 +18,16 @@
* under the License.
*
*/
-package org.apache.qpid.server.cluster;
+#include "AMQDataBlock.h"
-import java.util.List;
-public interface MembershipChangeListener
+namespace qpid {
+namespace framing {
+
+std::ostream& operator<<(std::ostream& out, const AMQDataBlock& b)
{
- public void changed(List<MemberHandle> members);
+ b.print(out);
+ return out;
}
+
+}}
diff --git a/cpp/lib/common/framing/AMQDataBlock.h b/cpp/lib/common/framing/AMQDataBlock.h
index ac91c52164..36de2beea5 100644
--- a/cpp/lib/common/framing/AMQDataBlock.h
+++ b/cpp/lib/common/framing/AMQDataBlock.h
@@ -33,10 +33,12 @@ public:
virtual void encode(Buffer& buffer) = 0;
virtual bool decode(Buffer& buffer) = 0;
virtual u_int32_t size() const = 0;
+ virtual void print(std::ostream& out) const = 0;
+
+ friend std::ostream& operator<<(std::ostream& out, const AMQDataBlock& block);
};
}
}
-
#endif
diff --git a/cpp/lib/common/framing/AMQFrame.cpp b/cpp/lib/common/framing/AMQFrame.cpp
index 6fa5b9ae51..0530dc805c 100644
--- a/cpp/lib/common/framing/AMQFrame.cpp
+++ b/cpp/lib/common/framing/AMQFrame.cpp
@@ -119,14 +119,18 @@ void AMQFrame::decodeBody(Buffer& buffer, uint32_t bufSize)
body->decode(buffer, bufSize);
}
-std::ostream& qpid::framing::operator<<(std::ostream& out, const AMQFrame& t)
+void AMQFrame::print(std::ostream& out) const
{
- out << "Frame[channel=" << t.channel << "; ";
- if (t.body.get() == 0)
+ out << "Frame[channel=" << channel << "; ";
+ if (body.get() == 0)
out << "empty";
else
- out << *t.body;
+ out << *body;
out << "]";
+}
+std::ostream& qpid::framing::operator<<(std::ostream& out, const AMQFrame& t)
+{
+ t.print(out);
return out;
}
diff --git a/cpp/lib/common/framing/AMQFrame.h b/cpp/lib/common/framing/AMQFrame.h
index d3c769087a..21642f112a 100644
--- a/cpp/lib/common/framing/AMQFrame.h
+++ b/cpp/lib/common/framing/AMQFrame.h
@@ -61,6 +61,8 @@ namespace qpid {
u_int32_t decodeHead(Buffer& buffer);
void decodeBody(Buffer& buffer, uint32_t size);
+ void print(std::ostream& out) const;
+
friend std::ostream& operator<<(std::ostream& out, const AMQFrame& body);
};
diff --git a/cpp/lib/common/framing/FrameList.cpp b/cpp/lib/common/framing/FrameList.cpp
new file mode 100644
index 0000000000..f188347101
--- /dev/null
+++ b/cpp/lib/common/framing/FrameList.cpp
@@ -0,0 +1,69 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "FrameList.h"
+#include "Exception.h"
+
+namespace qpid {
+namespace framing {
+
+FrameList::~FrameList()
+{
+ for (Frames::iterator i = frames.begin(); i != frames.end(); i++) {
+ delete (*i);
+ }
+}
+
+void FrameList::encode(Buffer& buffer)
+{
+ for (Frames::iterator i = frames.begin(); i != frames.end(); i++) {
+ (*i)->encode(buffer);
+ }
+}
+
+bool FrameList::decode(Buffer&)
+{
+ throw Exception("FrameList::decode() not valid!");
+}
+
+u_int32_t FrameList::size() const
+{
+ uint32_t s(0);
+ for (Frames::const_iterator i = frames.begin(); i != frames.end(); i++) {
+ s += (*i)->size();
+ }
+ return s;
+}
+
+void FrameList::print(std::ostream& out) const
+{
+ out << "Frames: ";
+ for (Frames::const_iterator i = frames.begin(); i != frames.end(); i++) {
+ (*i)->print(out);
+ out << "; ";
+ }
+}
+
+void FrameList::add(AMQFrame* f)
+{
+ frames.push_back(f);
+}
+
+}}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java b/cpp/lib/common/framing/FrameList.h
index b8099a12f7..59dc385e46 100644
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java
+++ b/cpp/lib/common/framing/FrameList.h
@@ -18,19 +18,33 @@
* under the License.
*
*/
-package org.apache.qpid.server.cluster;
+#include "Buffer.h"
+#include "AMQDataBlock.h"
+#include "AMQFrame.h"
-import org.apache.qpid.framing.AMQShortString;
+#include <list>
-public interface MemberHandle
-{
- public String getHost();
-
- public int getPort();
+#ifndef _FrameList_
+#define _FrameList_
- public boolean matches(MemberHandle m);
+namespace qpid {
+namespace framing {
- public boolean matches(String host, int port);
+class FrameList : public AMQDataBlock
+{
+ typedef std::list<AMQFrame*> Frames;
+ Frames frames;
+public:
+ virtual ~FrameList();
+ void encode(Buffer& buffer);
+ bool decode(Buffer& buffer);
+ u_int32_t size() const;
+ void add(AMQFrame* f);
+ void print(std::ostream& out) const;
+};
- public AMQShortString getDetails();
}
+}
+
+
+#endif
diff --git a/cpp/lib/common/framing/OutputHandler.h b/cpp/lib/common/framing/OutputHandler.h
index 2e01e34df2..16dc519738 100644
--- a/cpp/lib/common/framing/OutputHandler.h
+++ b/cpp/lib/common/framing/OutputHandler.h
@@ -22,6 +22,7 @@
*
*/
#include <boost/noncopyable.hpp>
+#include <AMQDataBlock.h>
#include <AMQFrame.h>
namespace qpid {
@@ -30,7 +31,7 @@ namespace framing {
class OutputHandler : private boost::noncopyable {
public:
virtual ~OutputHandler() {}
- virtual void send(AMQFrame* frame) = 0;
+ virtual void send(AMQDataBlock* frame) = 0;
};
}}
diff --git a/cpp/lib/common/framing/ProtocolInitiation.cpp b/cpp/lib/common/framing/ProtocolInitiation.cpp
index 471f736a7d..360178df5a 100644
--- a/cpp/lib/common/framing/ProtocolInitiation.cpp
+++ b/cpp/lib/common/framing/ProtocolInitiation.cpp
@@ -19,6 +19,7 @@
*
*/
#include <ProtocolInitiation.h>
+#include <iostream>
qpid::framing::ProtocolInitiation::ProtocolInitiation(){}
@@ -55,4 +56,9 @@ bool qpid::framing::ProtocolInitiation::decode(Buffer& buffer){
}
}
+void qpid::framing::ProtocolInitiation::print(std::ostream& out) const
+{
+ out << "AMQP(" << getMajor() << "-" << getMinor() << ")";
+}
+
//TODO: this should prbably be generated from the spec at some point to keep the version numbers up to date
diff --git a/cpp/lib/common/framing/ProtocolInitiation.h b/cpp/lib/common/framing/ProtocolInitiation.h
index 003c3bba81..03e53c75cb 100644
--- a/cpp/lib/common/framing/ProtocolInitiation.h
+++ b/cpp/lib/common/framing/ProtocolInitiation.h
@@ -45,6 +45,7 @@ public:
inline u_int8_t getMajor() const { return version.getMajor(); }
inline u_int8_t getMinor() const { return version.getMinor(); }
inline const ProtocolVersion& getVersion() const { return version; }
+ void print(std::ostream& out) const;
};
}
diff --git a/cpp/lib/common/sys/apr/LFSessionContext.cpp b/cpp/lib/common/sys/apr/LFSessionContext.cpp
index 8a7ce18136..dfe27050c4 100644
--- a/cpp/lib/common/sys/apr/LFSessionContext.cpp
+++ b/cpp/lib/common/sys/apr/LFSessionContext.cpp
@@ -96,7 +96,7 @@ void LFSessionContext::write(){
if(!framesToWrite.empty()){
out.clear();
bool encoded(false);
- AMQFrame* frame = framesToWrite.front();
+ AMQDataBlock* frame = framesToWrite.front();
while(frame && out.available() >= frame->size()){
encoded = true;
frame->encode(out);
@@ -120,7 +120,7 @@ void LFSessionContext::write(){
}
}
-void LFSessionContext::send(AMQFrame* frame){
+void LFSessionContext::send(AMQDataBlock* frame){
Mutex::ScopedLock l(writeLock);
if(!closing){
framesToWrite.push(frame);
@@ -173,9 +173,9 @@ void LFSessionContext::init(SessionHandler* _handler){
processor->add(&fd);
}
-void LFSessionContext::log(const std::string& desc, AMQFrame* const frame){
+void LFSessionContext::log(const std::string& desc, AMQDataBlock* const block){
Mutex::ScopedLock l(logLock);
- std::cout << desc << " [" << &socket << "]: " << *frame << std::endl;
+ std::cout << desc << " [" << &socket << "]: " << *block << std::endl;
}
Mutex LFSessionContext::logLock;
diff --git a/cpp/lib/common/sys/apr/LFSessionContext.h b/cpp/lib/common/sys/apr/LFSessionContext.h
index eeb8279d9a..7862055735 100644
--- a/cpp/lib/common/sys/apr/LFSessionContext.h
+++ b/cpp/lib/common/sys/apr/LFSessionContext.h
@@ -54,7 +54,7 @@ class LFSessionContext : public virtual qpid::sys::SessionContext
apr_pollfd_t fd;
- std::queue<qpid::framing::AMQFrame*> framesToWrite;
+ std::queue<qpid::framing::AMQDataBlock*> framesToWrite;
qpid::sys::Mutex writeLock;
bool processing;
@@ -62,7 +62,7 @@ class LFSessionContext : public virtual qpid::sys::SessionContext
static qpid::sys::Mutex logLock;
void log(const std::string& desc,
- qpid::framing::AMQFrame* const frame);
+ qpid::framing::AMQDataBlock* const block);
public:
@@ -70,7 +70,7 @@ class LFSessionContext : public virtual qpid::sys::SessionContext
LFProcessor* const processor,
bool debug = false);
virtual ~LFSessionContext();
- virtual void send(qpid::framing::AMQFrame* frame);
+ virtual void send(qpid::framing::AMQDataBlock* frame);
virtual void close();
void read();
void write();
diff --git a/cpp/tests/ChannelTest.cpp b/cpp/tests/ChannelTest.cpp
index cc0a90bad9..637e971077 100644
--- a/cpp/tests/ChannelTest.cpp
+++ b/cpp/tests/ChannelTest.cpp
@@ -39,8 +39,8 @@ using std::queue;
struct DummyHandler : OutputHandler{
std::vector<AMQFrame*> frames;
- virtual void send(AMQFrame* frame){
- frames.push_back(frame);
+ virtual void send(AMQDataBlock* block){
+ frames.push_back(dynamic_cast<AMQFrame*>(block));
}
};
diff --git a/cpp/tests/InMemoryContentTest.cpp b/cpp/tests/InMemoryContentTest.cpp
index bd638dae66..ca27e80515 100644
--- a/cpp/tests/InMemoryContentTest.cpp
+++ b/cpp/tests/InMemoryContentTest.cpp
@@ -33,8 +33,9 @@ using namespace qpid::framing;
struct DummyHandler : OutputHandler{
std::vector<AMQFrame*> frames;
- virtual void send(AMQFrame* frame){
- frames.push_back(frame);
+
+ virtual void send(AMQDataBlock* block){
+ frames.push_back(dynamic_cast<AMQFrame*>(block));
}
};
diff --git a/cpp/tests/LazyLoadedContentTest.cpp b/cpp/tests/LazyLoadedContentTest.cpp
index 2075a6dd3a..8d0ad65a6a 100644
--- a/cpp/tests/LazyLoadedContentTest.cpp
+++ b/cpp/tests/LazyLoadedContentTest.cpp
@@ -35,8 +35,8 @@ using namespace qpid::framing;
struct DummyHandler : OutputHandler{
std::vector<AMQFrame*> frames;
- virtual void send(AMQFrame* frame){
- frames.push_back(frame);
+ virtual void send(AMQDataBlock* block){
+ frames.push_back(dynamic_cast<AMQFrame*>(block));
}
};
diff --git a/cpp/tests/MessageTest.cpp b/cpp/tests/MessageTest.cpp
index bcf3ad8064..ee864a3883 100644
--- a/cpp/tests/MessageTest.cpp
+++ b/cpp/tests/MessageTest.cpp
@@ -30,8 +30,8 @@ using namespace qpid::framing;
struct DummyHandler : OutputHandler{
std::vector<AMQFrame*> frames;
- virtual void send(AMQFrame* frame){
- frames.push_back(frame);
+ virtual void send(AMQDataBlock* block){
+ frames.push_back(dynamic_cast<AMQFrame*>(block));
}
};
diff --git a/cpp/tests/client_test.cpp b/cpp/tests/client_test.cpp
index a5cc64d1e4..55bf6234d1 100644
--- a/cpp/tests/client_test.cpp
+++ b/cpp/tests/client_test.cpp
@@ -35,6 +35,7 @@
#include <MessageListener.h>
#include <sys/Monitor.h>
#include <FieldTable.h>
+#include <cstdlib>
using namespace qpid::client;
using namespace qpid::sys;
@@ -52,12 +53,28 @@ public:
inline SimpleListener(Monitor* _monitor) : monitor(_monitor){}
inline virtual void received(Message& msg){
- std::cout << "Received message " << msg.getData() << std::endl;
+ std::cout << "Received message " << msg.getData().substr(0, 5) << "..." << std::endl;
monitor->notify();
}
};
-int main(int argc, char**)
+const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+std::string generateData(uint size)
+{
+ if (size < chars.length()) {
+ return chars.substr(0, size);
+ }
+ std::string data;
+ for (uint i = 0; i < (size / chars.length()); i++) {
+ data += chars;
+ }
+ data += chars.substr(0, size % chars.length());
+ return data;
+}
+
+
+int main(int argc, char** argv)
{
try{
//Use a custom exchange
@@ -109,10 +126,17 @@ int main(int argc, char**)
//Now we create and publish a message to our exchange with a
//routing key that will cause it to be routed to our queue
Message msg;
- string data("MyMessage");
- msg.setData(data);
+ uint size = 0;
+ if (argc > 1) {
+ size = atoi(argv[1]);
+ }
+ if (size) {
+ msg.setData(generateData(size));
+ } else {
+ msg.setData("MyMessage");
+ }
channel.publish(msg, exchange, "MyTopic");
- std::cout << "Published message: " << data << std::endl;
+ std::cout << "Published message: " << msg.getData().substr(0, 5) << "..." << std::endl;
{
Monitor::ScopedLock l(monitor);
diff --git a/cpp/tests/run-python-tests b/cpp/tests/run-python-tests
index 5148f644eb..e2e45f9ea9 100755
--- a/cpp/tests/run-python-tests
+++ b/cpp/tests/run-python-tests
@@ -41,7 +41,7 @@ sleep 4
# Run the tests.
( cd $abs_srcdir/../../python \
- && python ./run-tests -v -I cpp_failing.txt ) || fail=1
+ && python ./run-tests -v -I cpp_failing.txt -s ../specs/amqp.0-9.no-wip.xml ) || fail=1
kill $pid || { echo FAIL: process already died; cat log; fail=1; }
diff --git a/dotnet/Qpid.Client/Client/AMQConnection.cs b/dotnet/Qpid.Client/Client/AMQConnection.cs
index d74cf6b5e4..d0bebf1170 100644
--- a/dotnet/Qpid.Client/Client/AMQConnection.cs
+++ b/dotnet/Qpid.Client/Client/AMQConnection.cs
@@ -816,10 +816,7 @@ namespace Apache.Qpid.Client
if (ProtocolInitiation.CURRENT_PROTOCOL_VERSION_MAJOR != 7)
{
// Basic.Qos frame appears to not be supported by OpenAMQ 1.0d.
- _protocolWriter.SyncWrite(
- BasicQosBody.CreateAMQFrame(
- channelId, (uint)prefetchHigh, 0, false),
- typeof (BasicQosOkBody));
+ _protocolWriter.SyncWrite(BasicQosBody.CreateAMQFrame(channelId, 0, (ushort)prefetchHigh, false), typeof (BasicQosOkBody));
}
if (transacted)
diff --git a/dotnet/Qpid.Client/Client/AmqChannel.cs b/dotnet/Qpid.Client/Client/AmqChannel.cs
index b5f6b6eec9..ce8e2ca2fe 100644
--- a/dotnet/Qpid.Client/Client/AmqChannel.cs
+++ b/dotnet/Qpid.Client/Client/AmqChannel.cs
@@ -457,7 +457,7 @@ namespace Apache.Qpid.Client
foreach (BasicMessageConsumer consumer in _consumers.Values)
{
// Sends acknowledgement to server.
- consumer.AcknowledgeLastDelivered();
+ consumer.AcknowledgeDelivered();
}
// Commits outstanding messages sent and outstanding acknowledgements.
@@ -485,13 +485,16 @@ namespace Apache.Qpid.Client
{
Suspend(true);
}
-
- // todo: rollback dispatcher when TX support is added
- //if ( _dispatcher != null )
- // _dispatcher.Rollback();
- _connection.ConvenientProtocolWriter.SyncWrite(
- TxRollbackBody.CreateAMQFrame(_channelId), typeof(TxRollbackOkBody));
+ // Reject up to message last delivered (if any) for each consumer.
+ // Need to send reject for messages delivered to consumers so far.
+ foreach (BasicMessageConsumer consumer in _consumers.Values)
+ {
+ // Sends acknowledgement to server.
+ consumer.RejectUnacked();
+ }
+
+ _connection.ConvenientProtocolWriter.SyncWrite(TxRollbackBody.CreateAMQFrame(_channelId), typeof(TxRollbackOkBody));
if ( !suspended )
{
@@ -1012,6 +1015,15 @@ namespace Apache.Qpid.Client
_connection.ProtocolWriter.Write(ackFrame);
}
+ public void RejectMessage(ulong deliveryTag, bool requeue)
+ {
+ if ((_acknowledgeMode == AcknowledgeMode.ClientAcknowledge) || (_acknowledgeMode == AcknowledgeMode.SessionTransacted))
+ {
+ AMQFrame rejectFrame = BasicRejectBody.CreateAMQFrame(_channelId, deliveryTag, requeue);
+ _connection.ProtocolWriter.Write(rejectFrame);
+ }
+ }
+
/// <summary>
/// Handle a message that bounced from the server, creating
/// the corresponding exception and notifying the connection about it
@@ -1104,8 +1116,8 @@ namespace Apache.Qpid.Client
/// Placing stop check after consume may also be wrong as it may cause a message to be thrown away. Seems more correct to use interupt on
/// the block thread to cause it to prematurely return from its wait, whereupon it can be made to re-check the stop flag.</remarks>
///
- /// <remarks>Exception swalled, if there is an exception whilst notifying the connection on bounced messages. Unhandled excetpion should
- /// fall through and termiante the loop, as it is a bug if it occurrs.</remarks>
+ /// <remarks>Exception swallowed, if there is an exception whilst notifying the connection on bounced messages. Unhandled excetpion should
+ /// fall through and terminate the loop, as it is a bug if it occurrs.</remarks>
private class Dispatcher
{
/// <summary> Flag used to indicate when this dispatcher is to be stopped (0=go, 1=stop). </summary>
diff --git a/dotnet/Qpid.Client/Client/BasicMessageConsumer.cs b/dotnet/Qpid.Client/Client/BasicMessageConsumer.cs
index 6a5ba82264..e88cf8f04c 100644
--- a/dotnet/Qpid.Client/Client/BasicMessageConsumer.cs
+++ b/dotnet/Qpid.Client/Client/BasicMessageConsumer.cs
@@ -20,6 +20,8 @@
*/
using System;
using System.Threading;
+using System.Collections;
+using System.Collections.Generic;
using log4net;
using Apache.Qpid.Client.Message;
using Apache.Qpid.Collections;
@@ -106,10 +108,15 @@ namespace Apache.Qpid.Client
private AmqChannel _channel;
+ // <summary>
+ // Tag of last message delievered, whoch should be acknowledged on commit in transaction mode.
+ // </summary>
+ //private long _lastDeliveryTag;
+
/// <summary>
- /// Tag of last message delievered, whoch should be acknowledged on commit in transaction mode.
+ /// Explicit list of all received but un-acked messages in a transaction. Used to ensure acking is completed when transaction is committed.
/// </summary>
- private long _lastDeliveryTag;
+ private LinkedList<long> _receivedDeliveryTags;
/// <summary>
/// Number of messages unacknowledged in DUPS_OK_ACKNOWLEDGE mode
@@ -135,6 +142,11 @@ namespace Apache.Qpid.Client
_prefetchHigh = prefetchHigh;
_prefetchLow = prefetchLow;
_exclusive = exclusive;
+
+ if (_acknowledgeMode == AcknowledgeMode.SessionTransacted)
+ {
+ _receivedDeliveryTags = new LinkedList<long>();
+ }
}
#region IMessageConsumer Members
@@ -391,13 +403,24 @@ namespace Apache.Qpid.Client
/// <summary>
/// Acknowledge up to last message delivered (if any). Used when commiting.
/// </summary>
- internal void AcknowledgeLastDelivered()
+ internal void AcknowledgeDelivered()
{
- if (_lastDeliveryTag > 0)
+ foreach (long tag in _receivedDeliveryTags)
{
- _channel.AcknowledgeMessage((ulong)_lastDeliveryTag, true); // XXX evil cast
- _lastDeliveryTag = -1;
+ _channel.AcknowledgeMessage((ulong)tag, false);
}
+
+ _receivedDeliveryTags.Clear();
+ }
+
+ internal void RejectUnacked()
+ {
+ foreach (long tag in _receivedDeliveryTags)
+ {
+ _channel.RejectMessage((ulong)tag, true);
+ }
+
+ _receivedDeliveryTags.Clear();
}
private void PreDeliver(AbstractQmsMessage msg)
@@ -442,7 +465,7 @@ namespace Apache.Qpid.Client
break;
case AcknowledgeMode.SessionTransacted:
- _lastDeliveryTag = msg.DeliveryTag;
+ _receivedDeliveryTags.AddLast(msg.DeliveryTag);
break;
}
}
diff --git a/dotnet/Qpid.Integration.Tests/interactive/SendReceiveTest.cs b/dotnet/Qpid.Integration.Tests/interactive/SendReceiveTest.cs
new file mode 100644
index 0000000000..ebc9307c2e
--- /dev/null
+++ b/dotnet/Qpid.Integration.Tests/interactive/SendReceiveTest.cs
@@ -0,0 +1,181 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+using System;
+using System.Threading;
+using log4net;
+using NUnit.Framework;
+using Apache.Qpid.Messaging;
+using Apache.Qpid.Client.Qms;
+using Apache.Qpid.Client;
+using Apache.Qpid.Integration.Tests.testcases;
+
+namespace Apache.Qpid.Integration.Tests.interactive
+{
+ /// <summary>
+ /// SendReceiveTest provides a quick interactive send-receive test, where the user is prompted to trigger each send or receive.
+ ///
+ /// <p><table id="crc"><caption>CRC Card</caption>
+ /// <tr><th> Responsibilities <th> Collaborations
+ /// <tr><td> Run an interactive send-receive loop prompting user to trigger each event.
+ /// </table>
+ /// </summary>
+ [TestFixture, Category("Interactive")]
+ public class SendReceiveTest : BaseMessagingTestFixture
+ {
+ /// <summary>Used for debugging purposes.</summary>
+ private static ILog log = LogManager.GetLogger(typeof(SendReceiveTest));
+
+ /// <summary>Defines the name of the test topic to use with the tests.</summary>
+ public const string TEST_ROUTING_KEY = "quicktestkey";
+
+ /// <summary>The number of consumers to test.</summary>
+ private const int CONSUMER_COUNT = 5;
+
+ /// <summary>The number of test messages to send.</summary>
+ private const int MESSAGE_COUNT = 10;
+
+ /// <summary>Monitor used to signal succesfull receipt of all test messages.</summary>
+ AutoResetEvent _finishedEvent;
+
+ /// <summary>Used to count test messages received so far.</summary>
+ private int _messageReceivedCount;
+
+ /// <summary>Used to hold the expected number of messages to receive.</summary>
+ private int expectedMessageCount;
+
+ /// <summary>Flag used to indicate that all messages really were received, and that the test did not just time out. </summary>
+ private bool allReceived;
+
+ /// <summary> Creates one producing end-point and many consuming end-points connected on a topic. </summary>
+ [SetUp]
+ public override void Init()
+ {
+ base.Init();
+
+ // Reset all test counts and flags.
+ _messageReceivedCount = 0;
+ allReceived = false;
+ _finishedEvent = new AutoResetEvent(false);
+ }
+
+ /// <summary> Cleans up all test end-points. </summary>
+ [TearDown]
+ public override void Shutdown()
+ {
+ try
+ {
+ // Close all end points for producer and consumers.
+ // Producer is on 0, and consumers on 1 .. n, so loop is from 0 to n inclusive.
+ for (int i = 0; i <= CONSUMER_COUNT; i++)
+ {
+ CloseEndPoint(i);
+ }
+ }
+ finally
+ {
+ base.Shutdown();
+ }
+ }
+
+ /// <summary> Check that all consumers on a topic each receive all message on it. </summary>
+ [Test]
+ public void AllConsumerReceiveAllMessagesOnTopic()
+ {
+ // Create end-points for all the consumers in the test.
+ for (int i = 1; i <= CONSUMER_COUNT; i++)
+ {
+ SetUpEndPoint(i, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, false, ExchangeNameDefaults.TOPIC,
+ true, false, null);
+ testConsumer[i].OnMessage += new MessageReceivedDelegate(OnMessage);
+ }
+
+ // Create an end-point to publish to the test topic.
+ SetUpEndPoint(0, true, false, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, false, ExchangeNameDefaults.TOPIC,
+ true, false, null);
+
+ expectedMessageCount = (MESSAGE_COUNT * CONSUMER_COUNT);
+
+ PromptAndWait("Press to send...");
+
+ for (int i = 0; i < MESSAGE_COUNT; i++)
+ {
+ testProducer[0].Send(testChannel[0].CreateTextMessage("A"));
+ }
+
+ _finishedEvent.WaitOne(new TimeSpan(0, 0, 0, 10), false);
+
+ PromptAndWait("Press to complete test...");
+
+ // Check that all messages really were received.
+ Assert.IsTrue(allReceived, "All messages were not received, only got " + _messageReceivedCount + " but wanted " + expectedMessageCount);
+ }
+
+ /// <summary> Check that consumers on the same queue receive each message once accross all consumers. </summary>
+ //[Test]
+ public void AllConsumerReceiveAllMessagesOnDirect()
+ {
+ // Create end-points for all the consumers in the test.
+ for (int i = 1; i <= CONSUMER_COUNT; i++)
+ {
+ SetUpEndPoint(i, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, false, ExchangeNameDefaults.DIRECT,
+ true, false, null);
+ testConsumer[i].OnMessage += new MessageReceivedDelegate(OnMessage);
+ }
+
+ // Create an end-point to publish to the test topic.
+ SetUpEndPoint(0, true, false, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, false, ExchangeNameDefaults.DIRECT,
+ true, false, null);
+
+ expectedMessageCount = MESSAGE_COUNT;
+
+ for (int i = 0; i < MESSAGE_COUNT; i++)
+ {
+ testProducer[0].Send(testChannel[0].CreateTextMessage("A"));
+ }
+
+ _finishedEvent.WaitOne(new TimeSpan(0, 0, 0, 10), false);
+
+ // Check that all messages really were received.
+ Assert.IsTrue(allReceived, "All messages were not received, only got: " + _messageReceivedCount + " but wanted " + expectedMessageCount);
+ }
+
+ /// <summary> Atomically increments the message count on every message, and signals once all messages in the test are received. </summary>
+ public void OnMessage(IMessage m)
+ {
+ int newCount = Interlocked.Increment(ref _messageReceivedCount);
+
+ if (newCount >= expectedMessageCount)
+ {
+ allReceived = true;
+ _finishedEvent.Set();
+ }
+ }
+
+ /// <summary>Prompts the user on stdout and waits for a reply on stdin, using the specified prompt message.</summary>
+ ///
+ /// <param name="message">The message to prompt the user with.</param>
+ private void PromptAndWait(string message)
+ {
+ Console.WriteLine("\n" + message);
+ Console.ReadLine();
+ }
+ }
+} \ No newline at end of file
diff --git a/dotnet/Qpid.Integration.Tests/testcases/BaseMessagingTestFixture.cs b/dotnet/Qpid.Integration.Tests/testcases/BaseMessagingTestFixture.cs
index 3608d070fc..f6d511034f 100644
--- a/dotnet/Qpid.Integration.Tests/testcases/BaseMessagingTestFixture.cs
+++ b/dotnet/Qpid.Integration.Tests/testcases/BaseMessagingTestFixture.cs
@@ -44,10 +44,10 @@ namespace Apache.Qpid.Integration.Tests.testcases
private const long RECEIVE_WAIT = 500;
/// <summary> The default AMQ connection URL to use for tests. </summary>
- const string connectionUri = "amqp://guest:guest@test/test?brokerlist='tcp://localhost:5672'";
+ public const string connectionUri = "amqp://guest:guest@test/test?brokerlist='tcp://localhost:5672'";
/// <summary> The default AMQ connection URL parsed as a connection info. </summary>
- protected IConnectionInfo connectionInfo = QpidConnectionInfo.FromUrl(connectionUri);
+ protected IConnectionInfo connectionInfo;
/// <summary> Holds an array of connections for building mutiple test end-points. </summary>
protected IConnection[] testConnection = new IConnection[10];
@@ -102,9 +102,17 @@ namespace Apache.Qpid.Integration.Tests.testcases
public void SetUpEndPoint(int n, bool producer, bool consumer, string routingKey, AcknowledgeMode ackMode, bool transacted,
string exchangeName, bool declareBind, bool durable, string subscriptionName)
{
+ // Allow client id to be fixed, or undefined.
+ {
+ // Use unique id for end point.
+ connectionInfo = QpidConnectionInfo.FromUrl(connectionUri);
+
+ connectionInfo.ClientName = "test" + n;
+ }
+
testConnection[n] = new AMQConnection(connectionInfo);
testConnection[n].Start();
- testChannel[n] = testConnection[n].CreateChannel(transacted, ackMode, 1);
+ testChannel[n] = testConnection[n].CreateChannel(transacted, ackMode);
if (producer)
{
diff --git a/dotnet/Qpid.Integration.Tests/testcases/DurableSubscriptionTest.cs b/dotnet/Qpid.Integration.Tests/testcases/DurableSubscriptionTest.cs
index 159b99006b..ceda19af3e 100644
--- a/dotnet/Qpid.Integration.Tests/testcases/DurableSubscriptionTest.cs
+++ b/dotnet/Qpid.Integration.Tests/testcases/DurableSubscriptionTest.cs
@@ -78,6 +78,8 @@ namespace Apache.Qpid.Integration.Tests.testcases
SetUpEndPoint(2, false, true, TEST_ROUTING_KEY + testId, ackMode, false, ExchangeNameDefaults.TOPIC, true,
true, "TestSubscription" + testId);
+ Thread.Sleep(500);
+
// Send messages and receive on both consumers.
testProducer[0].Send(testChannel[0].CreateTextMessage("A"));
@@ -93,15 +95,15 @@ namespace Apache.Qpid.Integration.Tests.testcases
ConsumeNMessagesOnly(1, "B", testConsumer[1]);
// Re-attach consumer, check that it gets the messages that it missed.
- SetUpEndPoint(3, false, true, TEST_ROUTING_KEY + testId, ackMode, false, ExchangeNameDefaults.TOPIC, true,
+ SetUpEndPoint(2, false, true, TEST_ROUTING_KEY + testId, ackMode, false, ExchangeNameDefaults.TOPIC, true,
true, "TestSubscription" + testId);
- ConsumeNMessagesOnly(1, "B", testConsumer[3]);
+ ConsumeNMessagesOnly(1, "B", testConsumer[2]);
// Clean up any open consumers at the end of the test.
- CloseEndPoint(0);
+ CloseEndPoint(2);
CloseEndPoint(1);
- CloseEndPoint(3);
+ CloseEndPoint(0);
}
/// <summary> Check that an uncommitted receive can be re-received, on re-consume from the same durable subscription. </summary>
@@ -122,13 +124,13 @@ namespace Apache.Qpid.Integration.Tests.testcases
// Close end-point 1 without committing the message, then re-open the subscription to consume again.
CloseEndPoint(1);
- SetUpEndPoint(2, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, true, ExchangeNameDefaults.DIRECT,
+ SetUpEndPoint(1, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, true, ExchangeNameDefaults.DIRECT,
true, false, null);
// Check that the message was released from the rolled back end-point an can be received on the alternative one instead.
- ConsumeNMessagesOnly(1, "A", testConsumer[2]);
+ ConsumeNMessagesOnly(1, "A", testConsumer[1]);
- CloseEndPoint(2);
+ CloseEndPoint(1);
CloseEndPoint(0);
}
@@ -151,13 +153,13 @@ namespace Apache.Qpid.Integration.Tests.testcases
// Close end-point 1 without committing the message, then re-open the subscription to consume again.
CloseEndPoint(1);
- SetUpEndPoint(2, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, true, ExchangeNameDefaults.DIRECT,
+ SetUpEndPoint(1, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, true, ExchangeNameDefaults.DIRECT,
true, false, null);
// Check that the message was released from the rolled back end-point an can be received on the alternative one instead.
- ConsumeNMessagesOnly(1, "A", testConsumer[2]);
+ ConsumeNMessagesOnly(1, "A", testConsumer[1]);
- CloseEndPoint(2);
+ CloseEndPoint(1);
CloseEndPoint(0);
}
}
diff --git a/dotnet/Qpid.Integration.Tests/testcases/HeadersExchangeTest.cs b/dotnet/Qpid.Integration.Tests/testcases/HeadersExchangeTest.cs
index 83a9267fc2..1ab8d79250 100644
--- a/dotnet/Qpid.Integration.Tests/testcases/HeadersExchangeTest.cs
+++ b/dotnet/Qpid.Integration.Tests/testcases/HeadersExchangeTest.cs
@@ -82,7 +82,8 @@ namespace Apache.Qpid.Integration.Tests.testcases
{
// Ensure that the base init method is called. It establishes a connection with the broker.
base.Init();
-
+
+ connectionInfo = QpidConnectionInfo.FromUrl(connectionUri);
_connection = new AMQConnection(connectionInfo);
_channel = _connection.CreateChannel(false, AcknowledgeMode.AutoAcknowledge, 500, 300);
diff --git a/dotnet/Qpid.Integration.Tests/testcases/MandatoryMessageTest.cs b/dotnet/Qpid.Integration.Tests/testcases/MandatoryMessageTest.cs
index a35ee7bb02..6cfdad1f94 100644
--- a/dotnet/Qpid.Integration.Tests/testcases/MandatoryMessageTest.cs
+++ b/dotnet/Qpid.Integration.Tests/testcases/MandatoryMessageTest.cs
@@ -49,6 +49,9 @@ namespace Apache.Qpid.Integration.Tests.testcases
/// <summary>Defines the maximum time in milliseconds, to wait for redelivery to occurr.</summary>
public const int TIMEOUT = 1000;
+ /// <summary>Defines the name of the routing key to use with the tests.</summary>
+ public const string TEST_ROUTING_KEY = "unboundkey";
+
/// <summary>Condition used to coordinate receipt of redelivery exception to the sending thread.</summary>
private ManualResetEvent errorEvent;
@@ -66,29 +69,14 @@ namespace Apache.Qpid.Integration.Tests.testcases
{
base.Init();
- _connection = new AMQConnection(connectionInfo);
- _channel = _connection.CreateChannel(false, AcknowledgeMode.AutoAcknowledge, 500, 300);
-
errorEvent = new ManualResetEvent(false);
lastErrorException = null;
- _connection.ExceptionListener = new ExceptionListenerDelegate(OnException);
-
- _connection.Start();
}
[TearDown]
public override void Shutdown()
{
- try
- {
- _connection.Stop();
- _connection.Close();
- _connection.Dispose();
- }
- finally
- {
- base.Shutdown();
- }
+ base.Shutdown();
}
/// <summary>
@@ -103,12 +91,6 @@ namespace Apache.Qpid.Integration.Tests.testcases
}
[Test]
- public void SendUndeliverableMessageOnDefaultExchange()
- {
- SendOne(null);
- }
-
- [Test]
public void SendUndeliverableMessageOnDirectExchange()
{
SendOne(ExchangeNameDefaults.DIRECT);
@@ -135,19 +117,21 @@ namespace Apache.Qpid.Integration.Tests.testcases
private void SendOne(string exchangeName)
{
log.Debug("private void SendOne(string exchangeName = " + exchangeName + "): called");
-
+
// Send a test message to a unbound key on the specified exchange.
- MessagePublisherBuilder builder = _channel.CreatePublisherBuilder()
- .WithRoutingKey("unboundkey")
- .WithMandatory(true);
+ SetUpEndPoint(0, false, false, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, false, exchangeName,
+ true, false, null);
+ testProducer[0] = testChannel[0].CreatePublisherBuilder()
+ .WithRoutingKey(TEST_ROUTING_KEY + testId)
+ .WithMandatory(true)
+ .WithExchangeName(exchangeName)
+ .Create();
- if ( exchangeName != null )
- {
- builder.WithExchangeName(exchangeName);
- }
+ // Set up the exception listener on the connection.
+ testConnection[0].ExceptionListener = new ExceptionListenerDelegate(OnException);
- IMessagePublisher publisher = builder.Create();
- publisher.Send(_channel.CreateTextMessage("Test Message"));
+ // Send message that should fail.
+ testProducer[0].Send(testChannel[0].CreateTextMessage("Test Message"));
// Wait for up to the timeout for a redelivery exception to be returned.
errorEvent.WaitOne(TIMEOUT, true);
@@ -158,6 +142,8 @@ namespace Apache.Qpid.Integration.Tests.testcases
Assert.IsNotNull(ex, "No exception was thrown by the test. Expected " + expectedException);
Assert.IsInstanceOfType(expectedException, ex.InnerException);
+
+ CloseEndPoint(0);
}
}
}
diff --git a/dotnet/Qpid.Integration.Tests/testcases/ProducerMultiConsumerTest.cs b/dotnet/Qpid.Integration.Tests/testcases/ProducerMultiConsumerTest.cs
index 43e60f94a0..7a4bf29cf6 100644
--- a/dotnet/Qpid.Integration.Tests/testcases/ProducerMultiConsumerTest.cs
+++ b/dotnet/Qpid.Integration.Tests/testcases/ProducerMultiConsumerTest.cs
@@ -81,6 +81,8 @@ namespace Apache.Qpid.Integration.Tests.testcases
{
try
{
+ // Close all end points for producer and consumers.
+ // Producer is on 0, and consumers on 1 .. n, so loop is from 0 to n inclusive.
for (int i = 0; i <= CONSUMER_COUNT; i++)
{
CloseEndPoint(i);
@@ -101,7 +103,7 @@ namespace Apache.Qpid.Integration.Tests.testcases
{
SetUpEndPoint(i, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, false, ExchangeNameDefaults.TOPIC,
true, false, null);
- testConsumer[i].OnMessage = new MessageReceivedDelegate(OnMessage);
+ testConsumer[i].OnMessage += new MessageReceivedDelegate(OnMessage);
}
// Create an end-point to publish to the test topic.
@@ -130,7 +132,7 @@ namespace Apache.Qpid.Integration.Tests.testcases
{
SetUpEndPoint(i, false, true, TEST_ROUTING_KEY + testId, AcknowledgeMode.AutoAcknowledge, false, ExchangeNameDefaults.DIRECT,
true, false, null);
- testConsumer[i].OnMessage = new MessageReceivedDelegate(OnMessage);
+ testConsumer[i].OnMessage += new MessageReceivedDelegate(OnMessage);
}
// Create an end-point to publish to the test topic.
@@ -155,7 +157,7 @@ namespace Apache.Qpid.Integration.Tests.testcases
{
int newCount = Interlocked.Increment(ref _messageReceivedCount);
- if (newCount > expectedMessageCount)
+ if (newCount >= expectedMessageCount)
{
allReceived = true;
_finishedEvent.Set();
diff --git a/dotnet/default.build b/dotnet/default.build
index 5dafa7fd9c..52a67ff638 100644
--- a/dotnet/default.build
+++ b/dotnet/default.build
@@ -72,7 +72,7 @@
<!-- Runs 'svnversion' to get the repository revision into the build property 'build.svnversion'. -->
<target name="svnversion" description="Runs svnversion to get the current repository version into a build script property.">
- <exec program="svnversion.exe" output="svnversion_tmp.txt">
+ <exec program="svnversion" output="svnversion_tmp.txt">
<arg value="-n"/>
</exec>
diff --git a/gentools/src/org/apache/qpid/gentools/Generator.java b/gentools/src/org/apache/qpid/gentools/Generator.java
index 1ce8fe9729..5d6e7be527 100644
--- a/gentools/src/org/apache/qpid/gentools/Generator.java
+++ b/gentools/src/org/apache/qpid/gentools/Generator.java
@@ -272,7 +272,7 @@ public abstract class Generator implements LanguageConverter
File[] versionTemplateFiles = new File[0];
- System.out.println("Looking for vesrion specific template files in directory: " + versionTemplateDirectory.getAbsoluteFile());
+ System.out.println("Looking for version specific template files in directory: " + versionTemplateDirectory.getAbsoluteFile());
if (versionTemplateDirectory.exists())
{
diff --git a/gentools/templ.cpp/MethodBodyClass.h.tmpl b/gentools/templ.cpp/method/MethodBodyClass.h.tmpl
index 5819a9cf9c..5819a9cf9c 100644
--- a/gentools/templ.cpp/MethodBodyClass.h.tmpl
+++ b/gentools/templ.cpp/method/MethodBodyClass.h.tmpl
diff --git a/gentools/templ.cpp/AMQP_ClientOperations.h.tmpl b/gentools/templ.cpp/model/AMQP_ClientOperations.h.tmpl
index a9fb0e0f69..a9fb0e0f69 100644
--- a/gentools/templ.cpp/AMQP_ClientOperations.h.tmpl
+++ b/gentools/templ.cpp/model/AMQP_ClientOperations.h.tmpl
diff --git a/gentools/templ.cpp/AMQP_ClientProxy.cpp.tmpl b/gentools/templ.cpp/model/AMQP_ClientProxy.cpp.tmpl
index 8cca6e5cec..8cca6e5cec 100644
--- a/gentools/templ.cpp/AMQP_ClientProxy.cpp.tmpl
+++ b/gentools/templ.cpp/model/AMQP_ClientProxy.cpp.tmpl
diff --git a/gentools/templ.cpp/AMQP_ClientProxy.h.tmpl b/gentools/templ.cpp/model/AMQP_ClientProxy.h.tmpl
index 0653ed7186..0653ed7186 100644
--- a/gentools/templ.cpp/AMQP_ClientProxy.h.tmpl
+++ b/gentools/templ.cpp/model/AMQP_ClientProxy.h.tmpl
diff --git a/gentools/templ.cpp/AMQP_Constants.h.tmpl b/gentools/templ.cpp/model/AMQP_Constants.h.tmpl
index 4631bc8de6..4631bc8de6 100644
--- a/gentools/templ.cpp/AMQP_Constants.h.tmpl
+++ b/gentools/templ.cpp/model/AMQP_Constants.h.tmpl
diff --git a/gentools/templ.cpp/AMQP_HighestVersion.h.tmpl b/gentools/templ.cpp/model/AMQP_HighestVersion.h.tmpl
index 9753b454ba..9753b454ba 100644
--- a/gentools/templ.cpp/AMQP_HighestVersion.h.tmpl
+++ b/gentools/templ.cpp/model/AMQP_HighestVersion.h.tmpl
diff --git a/gentools/templ.cpp/AMQP_MethodVersionMap.cpp.tmpl b/gentools/templ.cpp/model/AMQP_MethodVersionMap.cpp.tmpl
index dc2a890c88..dc2a890c88 100644
--- a/gentools/templ.cpp/AMQP_MethodVersionMap.cpp.tmpl
+++ b/gentools/templ.cpp/model/AMQP_MethodVersionMap.cpp.tmpl
diff --git a/gentools/templ.cpp/AMQP_MethodVersionMap.h.tmpl b/gentools/templ.cpp/model/AMQP_MethodVersionMap.h.tmpl
index c197871d4b..c197871d4b 100644
--- a/gentools/templ.cpp/AMQP_MethodVersionMap.h.tmpl
+++ b/gentools/templ.cpp/model/AMQP_MethodVersionMap.h.tmpl
diff --git a/gentools/templ.cpp/AMQP_ServerOperations.h.tmpl b/gentools/templ.cpp/model/AMQP_ServerOperations.h.tmpl
index e87723667b..e87723667b 100644
--- a/gentools/templ.cpp/AMQP_ServerOperations.h.tmpl
+++ b/gentools/templ.cpp/model/AMQP_ServerOperations.h.tmpl
diff --git a/gentools/templ.cpp/AMQP_ServerProxy.cpp.tmpl b/gentools/templ.cpp/model/AMQP_ServerProxy.cpp.tmpl
index cce369f98b..cce369f98b 100644
--- a/gentools/templ.cpp/AMQP_ServerProxy.cpp.tmpl
+++ b/gentools/templ.cpp/model/AMQP_ServerProxy.cpp.tmpl
diff --git a/gentools/templ.cpp/AMQP_ServerProxy.h.tmpl b/gentools/templ.cpp/model/AMQP_ServerProxy.h.tmpl
index fab29f2c60..fab29f2c60 100644
--- a/gentools/templ.cpp/AMQP_ServerProxy.h.tmpl
+++ b/gentools/templ.cpp/model/AMQP_ServerProxy.h.tmpl
diff --git a/java/broker/bin/qpid-server b/java/broker/bin/qpid-server
index f1f4d72e64..d0198f8dc9 100644
--- a/java/broker/bin/qpid-server
+++ b/java/broker/bin/qpid-server
@@ -25,7 +25,7 @@ QPID_LIBS=$QPID_HOME/lib/qpid-incubating.jar:$QPID_HOME/lib/bdbstore-launch.jar
export JAVA=java \
JAVA_VM=-server \
JAVA_MEM=-Xmx1024m \
- JAVA_GC="-XX:-UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \
+ JAVA_GC="-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \
QPID_CLASSPATH=$QPID_LIBS
. qpid-run org.apache.qpid.server.Main "$@"
diff --git a/java/broker/etc/acl.config.xml b/java/broker/etc/acl.config.xml
new file mode 100644
index 0000000000..0de01bf8bb
--- /dev/null
+++ b/java/broker/etc/acl.config.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ -->
+<broker>
+ <prefix>${QPID_HOME}</prefix>
+ <work>${QPID_WORK}</work>
+ <conf>${prefix}/etc</conf>
+ <connector>
+ <!-- Uncomment out this block and edit the keystorePath and keystorePassword
+ to enable SSL support
+ <ssl>
+ <enabled>true</enabled>
+ <sslOnly>true</sslOnly>
+ <keystorePath>/path/to/keystore.ks</keystorePath>
+ <keystorePassword>keystorepass</keystorePassword>
+ </ssl>-->
+ <qpidnio>false</qpidnio>
+ <!-- I've had the 0.0 and 0.1 Reader threads continually throwing IOException when client closes-->
+ <protectio>false</protectio>
+ <transport>nio</transport>
+ <port>5672</port>
+ <sslport>8672</sslport>
+ <socketReceiveBuffer>32768</socketReceiveBuffer>
+ <socketSendBuffer>32768</socketSendBuffer>
+ </connector>
+ <management>
+ <enabled>false</enabled>
+ <jmxport>8999</jmxport>
+ <security-enabled>false</security-enabled>
+ </management>
+ <advanced>
+ <filterchain enableExecutorPool="true"/>
+ <enablePooledAllocator>false</enablePooledAllocator>
+ <enableDirectBuffers>false</enableDirectBuffers>
+ <framesize>65535</framesize>
+ <compressBufferOnQueue>false</compressBufferOnQueue>
+ <enableJMSXUserID>false</enableJMSXUserID>
+ </advanced>
+
+ <security>
+ <principal-databases>
+ <!-- Example use of Base64 encoded MD5 hashes for authentication via CRAM-MD5-Hashed -->
+ <principal-database>
+ <name>passwordfile</name>
+ <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>
+ <attributes>
+ <attribute>
+ <name>passwordFile</name>
+ <value>${conf}/passwd</value>
+ </attribute>
+ </attributes>
+ </principal-database>
+ </principal-databases>
+
+ <access>
+ <class>org.apache.qpid.server.security.access.plugins.DenyAll</class>
+ </access>
+
+ <jmx>
+ <access>${conf}/jmxremote.access</access>
+ <principal-database>passwordfile</principal-database>
+ </jmx>
+ </security>
+
+ <virtualhosts>
+ <directory>${conf}/virtualhosts</directory>
+
+ <virtualhost>
+ <name>test</name>
+ <test>
+ <store>
+ <class>org.apache.qpid.server.store.MemoryMessageStore</class>
+ </store>
+
+ <queues>
+ <exchange>amq.direct</exchange>
+ <!-- 4Mb -->
+ <maximumQueueDepth>4235g264</maximumQueueDepth>
+ <!-- 2Mb -->
+ <maximumMessageSize>2117632</maximumMessageSize>
+ <!-- 10 mins -->
+ <maximumMessageAge>600000</maximumMessageAge>
+ </queues>
+
+
+ <security>
+ <access>
+ <class>org.apache.qpid.server.security.access.plugins.SimpleXML</class>
+ </access>
+
+ <access_control_list>
+ <!-- This section grants pubish rights to an exchange + routing key pair -->
+ <publish>
+ <exchanges>
+ <exchange>
+ <name>amq.direct</name>
+ <routing_keys>
+
+ <!-- Allow clients to publish requests -->
+ <routing_key>
+ <value>example.RequestQueue</value>
+ <users>
+ <user>client</user>
+ </users>
+ </routing_key>
+
+ <!-- Allow the processor to respond to a client on their Temporary Topic -->
+ <routing_key>
+ <value>tmp_*</value>
+ <users>
+ <user>server</user>
+ </users>
+ </routing_key>
+ </routing_keys>
+
+ </exchange>
+ </exchanges>
+ </publish>
+
+ <!-- This section grants users the ability to consume from the broker -->
+ <consume>
+ <queues>
+
+ <!-- Allow the clients to consume from their temporary queues-->
+ <queue>
+ <temporary/>
+ <users>
+ <user>client</user>
+ </users>
+ </queue>
+
+
+ <!-- Only allow the server to consume from the Request Queue-->
+ <queue>
+ <name>example.RequestQueue</name>
+ <users>
+ <user>server</user>
+ </users>
+ </queue>
+
+
+ </queues>
+ </consume>
+
+ <!-- This section grants clients the ability to create queues and exchanges -->
+ <create>
+ <queues>
+ <!-- Allow clients to create temporary queues-->
+ <queue>
+ <temporary/>
+ <exchanges>
+ <exchange>
+ <name>amq.direct</name>
+ <users>
+ <user>client</user>
+ </users>
+ </exchange>
+ </exchanges>
+ </queue>
+ <!-- Allow the server to create the Request Queue-->
+ <queue>
+ <name>example.RequestQueue</name>
+ <users>
+ <user>server</user>
+ </users>
+ </queue>
+
+ </queues>
+ </create>
+
+
+ </access_control_list>
+
+ </security>
+ </test>
+ </virtualhost>
+
+
+ <virtualhost>
+ <name>development</name>
+ <development>
+ <store>
+ <class>org.apache.qpid.server.store.MemoryMessageStore</class>
+ </store>
+ </development>
+ </virtualhost>
+
+ <virtualhost>
+ <name>localhost</name>
+ <localhost>
+ <store>
+ <class>org.apache.qpid.server.store.MemoryMessageStore</class>
+ </store>
+ </localhost>
+ </virtualhost>
+
+ </virtualhosts>
+
+ <heartbeat>
+ <delay>0</delay>
+ <timeoutFactor>2.0</timeoutFactor>
+ </heartbeat>
+
+ <virtualhosts>${conf}/virtualhosts.xml</virtualhosts>
+</broker>
+
+
diff --git a/java/broker/etc/config.xml b/java/broker/etc/config.xml
index b3b6a2877f..80ee039ee5 100644
--- a/java/broker/etc/config.xml
+++ b/java/broker/etc/config.xml
@@ -32,8 +32,10 @@
<keystorePath>/path/to/keystore.ks</keystorePath>
<keystorePassword>keystorepass</keystorePassword>
</ssl>-->
- <qpidnio>true</qpidnio>
- <protectio>true</protectio>
+ <qpidnio>false</qpidnio>
+ <protectio>
+ <enabled>false</enabled>
+ </protectio>
<transport>nio</transport>
<port>5672</port>
<sslport>8672</sslport>
@@ -51,37 +53,26 @@
<enableDirectBuffers>false</enableDirectBuffers>
<framesize>65535</framesize>
<compressBufferOnQueue>false</compressBufferOnQueue>
- <enableJMSXUserID>true</enableJMSXUserID>
+ <enableJMSXUserID>false</enableJMSXUserID>
</advanced>
<security>
<principal-databases>
+ <!-- Example use of Base64 encoded MD5 hashes for authentication via CRAM-MD5-Hashed -->
<principal-database>
<name>passwordfile</name>
- <class>org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase</class>
+ <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>
<attributes>
<attribute>
<name>passwordFile</name>
- <value>${conf}/passwdVhost</value>
+ <value>${conf}/passwd</value>
</attribute>
</attributes>
</principal-database>
-
- <!-- Example use of Base64 encoded MD5 hashes for authentication via CRAM-MD5-Hashed
- <principal-database>
- <name>passwordfile</name>
- <class>org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase</class>
- <attributes>
- <attribute>
- <name>passwordFile</name>
- <value>${conf}/qpid.passwd</value>
- </attribute>
- </attributes>
- </principal-database-->
</principal-databases>
<access>
- <class>org.apache.qpid.server.security.access.AllowAll</class>
+ <class>org.apache.qpid.server.security.access.plugins.AllowAll</class>
</access>
<jmx>
<access>${conf}/jmxremote.access</access>
@@ -90,37 +81,19 @@
</security>
<virtualhosts>
+ <directory>${conf}/virtualhosts</directory>
+
<virtualhost>
<name>localhost</name>
<localhost>
<store>
- <!-- <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
- <environment-path>${work}/localhost-store</environment-path> -->
-
<class>org.apache.qpid.server.store.MemoryMessageStore</class>
</store>
- <security>
- <!-- Need protocol changes to allow this-->
- <authentication>
- <name>passwordfile</name>
- <!-- Currently this can't be used as Vhost isn't specified at connection start only connection open -->
- <mechanism>PLAIN</mechanism>
- </authentication>
- <access>
- <class>org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager</class>
- <attributes>
- <attribute>
- <name>principalDatabase</name>
- <value>passwordfile</value>
- </attribute>
- <attribute>
- <name>defaultAccessManager</name>
- <value>DenyAll</value>
- </attribute>
- </attributes>
- </access>
- </security>
+ <housekeeping>
+ <expiredMessageCheckPeriod>20000</expiredMessageCheckPeriod>
+ </housekeeping>
+
</localhost>
</virtualhost>
@@ -130,11 +103,6 @@
<store>
<class>org.apache.qpid.server.store.MemoryMessageStore</class>
</store>
- <security>
- <name>passwordfile-notusedyet</name>
- <mechanism>PLAIN</mechanism>
- <mechanism>CRAM-MD5</mechanism>
- </security>
</development>
</virtualhost>
@@ -144,21 +112,6 @@
<store>
<class>org.apache.qpid.server.store.MemoryMessageStore</class>
</store>
- <security>
- <name>passwordfile-notusedyet</name>
- <mechanism>PLAIN</mechanism>
- <mechanism>CRAM-MD5</mechanism>
- </security>
- <access>
- <class>org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager</class>
- <attributes>
- <attribute>
- <name>principalDatabase</name>
- <value>rubbish-to-cause-default</value>
- </attribute>
- </attributes>
- </access>
-
</test>
</virtualhost>
@@ -175,4 +128,3 @@
</broker>
-
diff --git a/java/broker/etc/debug.log4j.xml b/java/broker/etc/debug.log4j.xml
index e8fd7e119d..71f9502b75 100644
--- a/java/broker/etc/debug.log4j.xml
+++ b/java/broker/etc/debug.log4j.xml
@@ -106,9 +106,9 @@
<!-- Log all info events to file -->
<root>
- <priority value="info"/>
+ <priority value="debug"/>
<appender-ref ref="STDOUT"/>
- <appender-ref ref="FileAppender"/>
+ <!--appender-ref ref="FileAppender"/-->
</root>
</log4j:configuration>
diff --git a/java/broker/etc/passwd b/java/broker/etc/passwd
index 966a16153d..7aca438551 100644
--- a/java/broker/etc/passwd
+++ b/java/broker/etc/passwd
@@ -17,3 +17,6 @@
# under the License.
#
guest:guest
+client:guest
+server:guest
+
diff --git a/java/broker/etc/persistent_config.xml b/java/broker/etc/persistent_config.xml
index 178a73515c..2143009711 100644
--- a/java/broker/etc/persistent_config.xml
+++ b/java/broker/etc/persistent_config.xml
@@ -28,7 +28,6 @@
<work>${QPID_WORK}</work>
<conf>${prefix}/etc</conf>
<connector>
- <qpidnio>true</qpidnio>
<transport>nio</transport>
<port>5672</port>
<sslport>8672</sslport>
@@ -51,18 +50,18 @@
<principal-databases>
<principal-database>
<name>passwordfile</name>
- <class>org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase</class>
+ <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>
<attributes>
<attribute>
<name>passwordFile</name>
- <value>${conf}/passwdVhost</value>
+ <value>${conf}/passwd</value>
</attribute>
</attributes>
</principal-database>
</principal-databases>
<access>
- <class>org.apache.qpid.server.security.access.AllowAll</class>
+ <class>org.apache.qpid.server.security.access.plugins.AllowAll</class>
</access>
<jmx>
<access>${conf}/jmxremote.access</access>
@@ -78,22 +77,6 @@
<class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
<environment-path>${work}/bdbstore/localhost-store</environment-path>
</store>
-
- <security>
- <access>
- <class>org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager</class>
- <attributes>
- <attribute>
- <name>principalDatabase</name>
- <value>passwordfile</value>
- </attribute>
- <attribute>
- <name>defaultAccessManager</name>
- <value>DenyAll</value>
- </attribute>
- </attributes>
- </access>
- </security>
</localhost>
</virtualhost>
diff --git a/java/broker/etc/qpid.passwd b/java/broker/etc/qpid.passwd
index 79b5e11777..dbfb9d1923 100644
--- a/java/broker/etc/qpid.passwd
+++ b/java/broker/etc/qpid.passwd
@@ -19,4 +19,5 @@
guest:CE4DQ6BIb/BVMN9scFyLtA==
admin:ISMvKXpXpadDiUoOSoAfww==
user:CE4DQ6BIb/BVMN9scFyLtA==
-
+server:CE4DQ6BIb/BVMN9scFyLtA==
+client:CE4DQ6BIb/BVMN9scFyLtA==
diff --git a/java/broker/etc/transient_config.xml b/java/broker/etc/transient_config.xml
index 164d66cd1b..5b13090f4c 100644
--- a/java/broker/etc/transient_config.xml
+++ b/java/broker/etc/transient_config.xml
@@ -28,7 +28,6 @@
<work>${QPID_WORK}</work>
<conf>${prefix}/etc</conf>
<connector>
- <qpidnio>true</qpidnio>
<transport>nio</transport>
<port>5672</port>
<sslport>8672</sslport>
@@ -61,7 +60,7 @@
</principal-database>
</principal-databases>
<access>
- <class>org.apache.qpid.server.security.access.AllowAll</class>
+ <class>org.apache.qpid.server.security.access.plugins.AllowAll</class>
</access>
<jmx>
<access>${conf}/jmxremote.access</access>
@@ -79,7 +78,7 @@
<security>
<access>
- <class>org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager</class>
+ <class>org.apache.qpid.server.security.old.PrincipalDatabaseAccessManager</class>
<attributes>
<attribute>
<name>principalDatabase</name>
diff --git a/java/broker/pom.xml b/java/broker/pom.xml
index b27ab657b7..1dd613e5f9 100644
--- a/java/broker/pom.xml
+++ b/java/broker/pom.xml
@@ -248,7 +248,7 @@
</condition>
<property name="command"
- value="python run-tests -v -I java_failing.txt -b localhost:2110"/>
+ value="python run-tests -v -I java_failing.txt -b localhost:2110 -s ../specs/amqp.0-9.no-wip.xml"/>
<!--value="bash -c 'python run-tests -v -I java_failing.txt'"/>-->
<ant antfile="python-test.xml" inheritRefs="true">
diff --git a/java/broker/src/main/grammar/SelectorParser.jj b/java/broker/src/main/grammar/SelectorParser.jj
index adec1b348d..f6a843e080 100644
--- a/java/broker/src/main/grammar/SelectorParser.jj
+++ b/java/broker/src/main/grammar/SelectorParser.jj
@@ -172,6 +172,7 @@ TOKEN [IGNORE_CASE] :
TOKEN [IGNORE_CASE] :
{
< ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* >
+ | < QUOTED_ID : "\"" ( ("\"\"") | ~["\""] )* "\"" >
}
// ----------------------------------------------------------------------------
@@ -589,6 +590,7 @@ String stringLitteral() :
PropertyExpression variable() :
{
Token t;
+ StringBuffer rc = new StringBuffer();
PropertyExpression left=null;
}
{
@@ -597,6 +599,21 @@ 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/broker/src/main/java/log4j.properties b/java/broker/src/main/java/log4j.properties
index 87f04f4991..6788c65463 100644
--- a/java/broker/src/main/java/log4j.properties
+++ b/java/broker/src/main/java/log4j.properties
@@ -19,6 +19,6 @@
log4j.rootCategory=${amqj.logging.level}, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
-log4j.appender.console.Threshold=info
+log4j.appender.console.Threshold=all
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%t %d %p [%c{4}] %m%n
diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
index 4696ec4453..17b4fa5d65 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
@@ -33,8 +33,9 @@ import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.ack.UnacknowledgedMessage;
import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl;
-import org.apache.qpid.server.exchange.MessageRouter;
+import org.apache.qpid.server.configuration.Configurator;
import org.apache.qpid.server.exchange.NoRouteException;
+import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.*;
import org.apache.qpid.server.store.MessageStore;
@@ -42,17 +43,15 @@ import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.LocalTransactionalContext;
import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.server.txn.TransactionalContext;
-import org.apache.qpid.server.configuration.Configurator;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
public class AMQChannel
{
@@ -74,7 +73,7 @@ public class AMQChannel
* The delivery tag is unique per channel. This is pre-incremented before putting into the deliver frame so that
* value of this represents the <b>last</b> tag sent out
*/
- private AtomicLong _deliveryTag = new AtomicLong(0);
+ private long _deliveryTag = 0;
/** A channel has a default queue (the last declared) that is used when no queue name is explictily set */
private AMQQueue _defaultQueue;
@@ -90,7 +89,7 @@ public class AMQChannel
private AMQMessage _currentMessage;
/** Maps from consumer tag to queue instance. Allows us to unsubscribe from a queue. */
- private final Map<AMQShortString, AMQQueue> _consumerTag2QueueMap = new HashMap<AMQShortString, AMQQueue>();
+ private final Map<AMQShortString, AMQQueue> _consumerTag2QueueMap = new ConcurrentHashMap<AMQShortString, AMQQueue>();
private final MessageStore _messageStore;
@@ -98,8 +97,6 @@ public class AMQChannel
private final AtomicBoolean _suspended = new AtomicBoolean(false);
- private final MessageRouter _exchanges;
-
private TransactionalContext _txnContext, _nonTransactedContext;
/**
@@ -119,11 +116,11 @@ public class AMQChannel
private boolean _closing;
@Configured(path = "advanced.enableJMSXUserID",
- defaultValue = "true")
+ defaultValue = "false")
public boolean ENABLE_JMSXUserID;
- public AMQChannel(AMQProtocolSession session, int channelId, MessageStore messageStore, MessageRouter exchanges)
+ public AMQChannel(AMQProtocolSession session, int channelId, MessageStore messageStore)
throws AMQException
{
//Set values from configuration
@@ -135,7 +132,7 @@ public class AMQChannel
_prefetch_HighWaterMark = DEFAULT_PREFETCH;
_prefetch_LowWaterMark = _prefetch_HighWaterMark / 2;
_messageStore = messageStore;
- _exchanges = exchanges;
+
// by default the session is non-transactional
_txnContext = new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
}
@@ -199,11 +196,12 @@ public class AMQChannel
_prefetch_HighWaterMark = prefetchCount;
}
- public void setPublishFrame(MessagePublishInfo info, AMQProtocolSession publisher) throws AMQException
+ public void setPublishFrame(MessagePublishInfo info, AMQProtocolSession publisher, final Exchange e) throws AMQException
{
_currentMessage = new AMQMessage(_messageStore.getNewMessageId(), info, _txnContext);
_currentMessage.setPublisher(publisher);
+ _currentMessage.setExchange(e);
}
public void publishContentHeader(ContentHeaderBody contentHeaderBody, AMQProtocolSession protocolSession)
@@ -215,9 +213,9 @@ public class AMQChannel
}
else
{
- if (_log.isTraceEnabled())
+ if (_log.isDebugEnabled())
{
- _log.trace(debugIdentity() + "Content header received on channel " + _channelId);
+ _log.debug(debugIdentity() + "Content header received on channel " + _channelId);
}
if (ENABLE_JMSXUserID)
@@ -252,9 +250,9 @@ public class AMQChannel
throw new AMQException("Received content body without previously receiving a JmsPublishBody");
}
- if (_log.isTraceEnabled())
+ if (_log.isDebugEnabled())
{
- _log.trace(debugIdentity() + "Content body received on channel " + _channelId);
+ _log.debug(debugIdentity() + "Content body received on channel " + _channelId);
}
try
@@ -285,7 +283,7 @@ public class AMQChannel
{
try
{
- _exchanges.routeContent(_currentMessage);
+ _currentMessage.route();
}
catch (NoRouteException e)
{
@@ -295,7 +293,7 @@ public class AMQChannel
public long getNextDeliveryTag()
{
- return _deliveryTag.incrementAndGet();
+ return ++_deliveryTag;
}
public int getNextConsumerTag()
@@ -333,13 +331,32 @@ public class AMQChannel
throw new ConsumerTagNotUniqueException();
}
- queue.registerProtocolSession(session, _channelId, tag, acks, filters, noLocal, exclusive);
+ // We add before we register as the Async Delivery process may AutoClose the subscriber
+ // so calling _cT2QM.remove before we have done put which was after the register succeeded.
+ // So to keep things straight we put before the call and catch all exceptions from the register and tidy up.
_consumerTag2QueueMap.put(tag, queue);
+ try
+ {
+ queue.registerProtocolSession(session, _channelId, tag, acks, filters, noLocal, exclusive);
+ }
+ catch (AMQException e)
+ {
+ _consumerTag2QueueMap.remove(tag);
+ throw e;
+ }
+
return tag;
}
- public void unsubscribeConsumer(AMQProtocolSession session, AMQShortString consumerTag) throws AMQException
+ /**
+ * Unsubscribe a consumer from a queue.
+ * @param session
+ * @param consumerTag
+ * @return true if the consumerTag had a mapped queue that could be unregistered.
+ * @throws AMQException
+ */
+ public boolean unsubscribeConsumer(AMQProtocolSession session, AMQShortString consumerTag) throws AMQException
{
if (_log.isDebugEnabled())
{
@@ -364,7 +381,9 @@ public class AMQChannel
if (q != null)
{
q.unregisterProtocolSession(session, _channelId, consumerTag);
+ return true;
}
+ return false;
}
/**
@@ -822,7 +841,7 @@ public class AMQChannel
{
message.discard(_storeContext);
message.setQueueDeleted(true);
-
+
}
catch (AMQException e)
{
@@ -967,16 +986,19 @@ public class AMQChannel
public void processReturns(AMQProtocolSession session) throws AMQException
{
- for (RequiredDeliveryException bouncedMessage : _returnMessages)
+ if (!_returnMessages.isEmpty())
{
- AMQMessage message = bouncedMessage.getAMQMessage();
- session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(),
- new AMQShortString(bouncedMessage.getMessage()));
+ for (RequiredDeliveryException bouncedMessage : _returnMessages)
+ {
+ AMQMessage message = bouncedMessage.getAMQMessage();
+ session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(),
+ new AMQShortString(bouncedMessage.getMessage()));
- message.decrementReference(_storeContext);
- }
+ message.decrementReference(_storeContext);
+ }
- _returnMessages.clear();
+ _returnMessages.clear();
+ }
}
public boolean wouldSuspend(AMQMessage msg)
diff --git a/java/broker/src/main/java/org/apache/qpid/server/Main.java b/java/broker/src/main/java/org/apache/qpid/server/Main.java
index ab9f40b31d..d8a8cfb6d1 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/Main.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/Main.java
@@ -35,6 +35,7 @@ import org.apache.log4j.xml.DOMConfigurator;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.SimpleByteBufferAllocator;
+import org.apache.mina.common.FixedSizeByteBufferAllocator;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
import org.apache.mina.transport.socket.nio.SocketSessionConfig;
import org.apache.qpid.AMQException;
@@ -275,7 +276,7 @@ public class Main
// once more testing of the performance of the simple allocator has been done
if (!connectorConfig.enablePooledAllocator)
{
- ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+ ByteBuffer.setAllocator(new FixedSizeByteBufferAllocator());
}
int port = connectorConfig.port;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java b/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
index ac29998c2a..c62a7880a8 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
@@ -100,10 +100,9 @@ public class TxAck implements TxnOp
//make persistent changes, i.e. dequeue and decrementReference
for (UnacknowledgedMessage msg : _unacked)
{
- //msg.restoreTransientMessageData();
-
//Message has been ack so discard it. This will dequeue and decrement the reference.
msg.discard(storeContext);
+
}
}
@@ -115,7 +114,6 @@ public class TxAck implements TxnOp
//in memory (persistent changes will be rolled back by store)
for (UnacknowledgedMessage msg : _unacked)
{
- msg.clearTransientMessageData();
msg.getMessage().takeReference();
}
}
@@ -124,11 +122,6 @@ public class TxAck implements TxnOp
{
//remove the unacked messages from the channels map
_map.remove(_unacked);
- for (UnacknowledgedMessage msg : _unacked)
- {
- msg.clearTransientMessageData();
- }
-
}
public void rollback(StoreContext storeContext)
diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java
index 40f5970cac..df7cecc940 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java
@@ -68,16 +68,6 @@ public class UnacknowledgedMessage
entry.getMessage().decrementReference(storeContext);
}
- public void restoreTransientMessageData() throws AMQException
- {
- entry.getMessage().restoreTransientMessageData();
- }
-
- public void clearTransientMessageData()
- {
- entry.getMessage().clearTransientMessageData();
- }
-
public AMQMessage getMessage()
{
return entry.getMessage();
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java
index b6b6ee39ce..12347c0278 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java
@@ -212,10 +212,9 @@ public class DestNameExchange extends AbstractExchange
_logger.debug("Publishing message to queue " + queues);
}
- for (AMQQueue q : queues)
- {
- payload.enqueue(q);
- }
+ payload.enqueue(queues);
+
+
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java
index 75be86a387..6fa3686152 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java
@@ -26,6 +26,7 @@ import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.AMQShortStringTokenizer;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.management.MBeanConstructor;
import org.apache.qpid.server.management.MBeanDescription;
@@ -40,11 +41,7 @@ import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -84,12 +81,21 @@ public class DestWildExchange extends AbstractExchange
private static final Logger _logger = Logger.getLogger(DestWildExchange.class);
- private ConcurrentHashMap<AMQShortString, List<AMQQueue>> _routingKey2queues =
+ private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _bindingKey2queues =
+ new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
+ private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _simpleBindingKey2queues =
+ new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
+ private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _wildCardBindingKey2queues =
new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
- // private ConcurrentHashMap<AMQShortString, AMQQueue> _routingKey2queue = new ConcurrentHashMap<AMQShortString, AMQQueue>();
- private static final String TOPIC_SEPARATOR = ".";
- private static final String AMQP_STAR = "*";
- private static final String AMQP_HASH = "#";
+
+ private static final byte TOPIC_SEPARATOR = (byte)'.';
+ private static final AMQShortString TOPIC_SEPARATOR_AS_SHORTSTRING = new AMQShortString(".");
+ private static final AMQShortString AMQP_STAR_TOKEN = new AMQShortString("*");
+ private static final AMQShortString AMQP_HASH_TOKEN = new AMQShortString("#");
+ private ConcurrentHashMap<AMQShortString, AMQShortString[]> _bindingKey2Tokenized =
+ new ConcurrentHashMap<AMQShortString, AMQShortString[]>();
+ private static final byte HASH_BYTE = (byte)'#';
+ private static final byte STAR_BYTE = (byte)'*';
/** DestWildExchangeMBean class implements the management interface for the Topic exchanges. */
@MBeanDescription("Management Bean for Topic Exchange")
@@ -107,7 +113,7 @@ public class DestWildExchange extends AbstractExchange
public TabularData bindings() throws OpenDataException
{
_bindingList = new TabularDataSupport(_bindinglistDataType);
- for (Map.Entry<AMQShortString, List<AMQQueue>> entry : _routingKey2queues.entrySet())
+ for (Map.Entry<AMQShortString, List<AMQQueue>> entry : _bindingKey2queues.entrySet())
{
AMQShortString key = entry.getKey();
List<String> queueList = new ArrayList<String>();
@@ -156,27 +162,75 @@ public class DestWildExchange extends AbstractExchange
assert queue != null;
assert rKey != null;
- AMQShortString routingKey = normalize(rKey);
+ _logger.debug("Registering queue " + queue.getName() + " with routing key " + rKey);
- _logger.debug("Registering queue " + queue.getName() + " with routing key " + routingKey);
// we need to use putIfAbsent, which is an atomic operation, to avoid a race condition
- List<AMQQueue> queueList = _routingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList<AMQQueue>());
+ List<AMQQueue> queueList = _bindingKey2queues.putIfAbsent(rKey, new CopyOnWriteArrayList<AMQQueue>());
+
+
+
+
+
+
+
// if we got null back, no previous value was associated with the specified routing key hence
// we need to read back the new value just put into the map
if (queueList == null)
{
- queueList = _routingKey2queues.get(routingKey);
+ queueList = _bindingKey2queues.get(rKey);
}
+
+
if (!queueList.contains(queue))
{
queueList.add(queue);
+
+
+ if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE))
+ {
+ AMQShortString routingKey = normalize(rKey);
+ List<AMQQueue> queueList2 = _wildCardBindingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList<AMQQueue>());
+
+ if(queueList2 == null)
+ {
+ queueList2 = _wildCardBindingKey2queues.get(routingKey);
+ AMQShortStringTokenizer keyTok = routingKey.tokenize(TOPIC_SEPARATOR);
+
+ ArrayList<AMQShortString> keyTokList = new ArrayList<AMQShortString>(keyTok.countTokens());
+
+ while (keyTok.hasMoreTokens())
+ {
+ keyTokList.add(keyTok.nextToken());
+ }
+
+ _bindingKey2Tokenized.put(routingKey, keyTokList.toArray(new AMQShortString[keyTokList.size()]));
+ }
+ queueList2.add(queue);
+
+ }
+ else
+ {
+ List<AMQQueue> queueList2 = _simpleBindingKey2queues.putIfAbsent(rKey, new CopyOnWriteArrayList<AMQQueue>());
+ if(queueList2 == null)
+ {
+ queueList2 = _simpleBindingKey2queues.get(rKey);
+ }
+ queueList2.add(queue);
+
+ }
+
+
+
+
}
else if (_logger.isDebugEnabled())
{
- _logger.debug("Queue " + queue + " is already registered with routing key " + routingKey);
+ _logger.debug("Queue " + queue + " is already registered with routing key " + rKey);
}
+
+
}
private AMQShortString normalize(AMQShortString routingKey)
@@ -186,60 +240,55 @@ public class DestWildExchange extends AbstractExchange
routingKey = AMQShortString.EMPTY_STRING;
}
- StringTokenizer routingTokens = new StringTokenizer(routingKey.toString(), TOPIC_SEPARATOR);
- List<String> _subscription = new ArrayList<String>();
+ AMQShortStringTokenizer routingTokens = routingKey.tokenize(TOPIC_SEPARATOR);
+
+ List<AMQShortString> subscriptionList = new ArrayList<AMQShortString>();
while (routingTokens.hasMoreTokens())
{
- _subscription.add(routingTokens.nextToken());
+ subscriptionList.add(routingTokens.nextToken());
}
- int size = _subscription.size();
+ int size = subscriptionList.size();
for (int index = 0; index < size; index++)
{
// if there are more levels
if ((index + 1) < size)
{
- if (_subscription.get(index).equals(AMQP_HASH))
+ if (subscriptionList.get(index).equals(AMQP_HASH_TOKEN))
{
- if (_subscription.get(index + 1).equals(AMQP_HASH))
+ if (subscriptionList.get(index + 1).equals(AMQP_HASH_TOKEN))
{
// we don't need #.# delete this one
- _subscription.remove(index);
+ subscriptionList.remove(index);
size--;
// redo this normalisation
index--;
}
- if (_subscription.get(index + 1).equals(AMQP_STAR))
+ if (subscriptionList.get(index + 1).equals(AMQP_STAR_TOKEN))
{
// we don't want #.* swap to *.#
// remove it and put it in at index + 1
- _subscription.add(index + 1, _subscription.remove(index));
+ subscriptionList.add(index + 1, subscriptionList.remove(index));
}
}
} // if we have more levels
}
- StringBuilder sb = new StringBuilder();
- for (String s : _subscription)
- {
- sb.append(s);
- sb.append(TOPIC_SEPARATOR);
- }
- sb.deleteCharAt(sb.length() - 1);
+ AMQShortString normalizedString = AMQShortString.join(subscriptionList, TOPIC_SEPARATOR_AS_SHORTSTRING);
- return new AMQShortString(sb.toString());
+ return normalizedString;
}
public void route(AMQMessage payload) throws AMQException
{
MessagePublishInfo info = payload.getMessagePublishInfo();
- final AMQShortString routingKey = normalize(info.getRoutingKey());
+ final AMQShortString routingKey = info.getRoutingKey();
List<AMQQueue> queues = getMatchedQueues(routingKey);
// if we have no registered queues we have nothing to do
@@ -254,19 +303,14 @@ public class DestWildExchange extends AbstractExchange
else
{
_logger.warn("No queues found for routing key " + routingKey);
- _logger.warn("Routing map contains: " + _routingKey2queues);
+ _logger.warn("Routing map contains: " + _bindingKey2queues);
return;
}
}
- for (AMQQueue q : queues)
- {
- // TODO: modify code generator to add clone() method then clone the deliver body
- // without this addition we have a race condition - we will be modifying the body
- // before the encoder has encoded the body for delivery
- payload.enqueue(q);
- }
+ payload.enqueue(queues);
+
}
public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
@@ -276,21 +320,21 @@ public class DestWildExchange extends AbstractExchange
public boolean isBound(AMQShortString routingKey, AMQQueue queue)
{
- List<AMQQueue> queues = _routingKey2queues.get(normalize(routingKey));
+ List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey));
return (queues != null) && queues.contains(queue);
}
public boolean isBound(AMQShortString routingKey)
{
- List<AMQQueue> queues = _routingKey2queues.get(normalize(routingKey));
+ List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey));
return (queues != null) && !queues.isEmpty();
}
public boolean isBound(AMQQueue queue)
{
- for (List<AMQQueue> queues : _routingKey2queues.values())
+ for (List<AMQQueue> queues : _bindingKey2queues.values())
{
if (queues.contains(queue))
{
@@ -303,7 +347,7 @@ public class DestWildExchange extends AbstractExchange
public boolean hasBindings()
{
- return !_routingKey2queues.isEmpty();
+ return !_bindingKey2queues.isEmpty();
}
public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException
@@ -311,13 +355,11 @@ public class DestWildExchange extends AbstractExchange
assert queue != null;
assert rKey != null;
- AMQShortString routingKey = normalize(rKey);
-
- List<AMQQueue> queues = _routingKey2queues.get(routingKey);
+ List<AMQQueue> queues = _bindingKey2queues.get(rKey);
if (queues == null)
{
throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName()
- + " with routing key " + routingKey + ". No queue was registered with that _routing key");
+ + " with routing key " + rKey + ". No queue was registered with that _routing key");
}
@@ -325,12 +367,39 @@ public class DestWildExchange extends AbstractExchange
if (!removedQ)
{
throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName()
- + " with routing key " + routingKey);
+ + " with routing key " + rKey);
}
+
+ if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE))
+ {
+ AMQShortString bindingKey = normalize(rKey);
+ List<AMQQueue> queues2 = _wildCardBindingKey2queues.get(bindingKey);
+ queues2.remove(queue);
+ if(queues2.isEmpty())
+ {
+ _wildCardBindingKey2queues.remove(bindingKey);
+ _bindingKey2Tokenized.remove(bindingKey);
+ }
+
+ }
+ else
+ {
+ List<AMQQueue> queues2 = _simpleBindingKey2queues.get(rKey);
+ queues2.remove(queue);
+ if(queues2.isEmpty())
+ {
+ _simpleBindingKey2queues.remove(rKey);
+ }
+
+ }
+
+
+
+
if (queues.isEmpty())
{
- _routingKey2queues.remove(routingKey);
+ _bindingKey2queues.remove(rKey);
}
}
@@ -349,117 +418,162 @@ public class DestWildExchange extends AbstractExchange
public Map<AMQShortString, List<AMQQueue>> getBindings()
{
- return _routingKey2queues;
+ return _bindingKey2queues;
}
private List<AMQQueue> getMatchedQueues(AMQShortString routingKey)
{
- List<AMQQueue> list = new LinkedList<AMQQueue>();
- StringTokenizer routingTokens = new StringTokenizer(routingKey.toString(), TOPIC_SEPARATOR);
- ArrayList<String> routingkeyList = new ArrayList<String>();
+ List<AMQQueue> list = null;
- while (routingTokens.hasMoreTokens())
+ if(!_wildCardBindingKey2queues.isEmpty())
{
- String next = routingTokens.nextToken();
- if (next.equals(AMQP_HASH) && routingkeyList.get(routingkeyList.size() - 1).equals(AMQP_HASH))
- {
- continue;
- }
- routingkeyList.add(next);
- }
- for (AMQShortString queue : _routingKey2queues.keySet())
- {
- StringTokenizer queTok = new StringTokenizer(queue.toString(), TOPIC_SEPARATOR);
+ AMQShortStringTokenizer routingTokens = routingKey.tokenize(TOPIC_SEPARATOR);
+
+ final int routingTokensCount = routingTokens.countTokens();
+
- ArrayList<String> queueList = new ArrayList<String>();
+ AMQShortString[] routingkeyTokens = new AMQShortString[routingTokensCount];
- while (queTok.hasMoreTokens())
+ if(routingTokensCount == 1)
{
- queueList.add(queTok.nextToken());
+ routingkeyTokens[0] =routingKey;
}
+ else
+ {
- int depth = 0;
- boolean matching = true;
- boolean done = false;
- int routingskip = 0;
- int queueskip = 0;
- while (matching && !done)
- {
- if ((queueList.size() == (depth + queueskip)) || (routingkeyList.size() == (depth + routingskip)))
+ int token = 0;
+ while (routingTokens.hasMoreTokens())
{
- done = true;
- // if it was the routing key that ran out of digits
- if (routingkeyList.size() == (depth + routingskip))
- {
- if (queueList.size() > (depth + queueskip))
- { // a hash and it is the last entry
- matching =
- queueList.get(depth + queueskip).equals(AMQP_HASH)
- && (queueList.size() == (depth + queueskip + 1));
- }
- }
- else if (routingkeyList.size() > (depth + routingskip))
- {
- // There is still more routing key to check
- matching = false;
- }
+ AMQShortString next = routingTokens.nextToken();
- continue;
+ routingkeyTokens[token++] = next;
}
+ }
+ for (AMQShortString bindingKey : _wildCardBindingKey2queues.keySet())
+ {
+
+ AMQShortString[] bindingKeyTokens = _bindingKey2Tokenized.get(bindingKey);
+
+
+ boolean matching = true;
+ boolean done = false;
- // if the values on the two topics don't match
- if (!queueList.get(depth + queueskip).equals(routingkeyList.get(depth + routingskip)))
+ int depthPlusRoutingSkip = 0;
+ int depthPlusQueueSkip = 0;
+
+ final int bindingKeyTokensCount = bindingKeyTokens.length;
+
+ while (matching && !done)
{
- if (queueList.get(depth + queueskip).equals(AMQP_STAR))
+
+ if ((bindingKeyTokensCount == depthPlusQueueSkip) || (routingTokensCount == depthPlusRoutingSkip))
{
- depth++;
+ done = true;
+
+ // if it was the routing key that ran out of digits
+ if (routingTokensCount == depthPlusRoutingSkip)
+ {
+ if (bindingKeyTokensCount > depthPlusQueueSkip)
+ { // a hash and it is the last entry
+ matching =
+ bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_HASH_TOKEN)
+ && (bindingKeyTokensCount == (depthPlusQueueSkip + 1));
+ }
+ }
+ else if (routingTokensCount > depthPlusRoutingSkip)
+ {
+ // There is still more routing key to check
+ matching = false;
+ }
continue;
}
- else if (queueList.get(depth + queueskip).equals(AMQP_HASH))
+
+ // if the values on the two topics don't match
+ if (!bindingKeyTokens[depthPlusQueueSkip].equals(routingkeyTokens[depthPlusRoutingSkip]))
{
- // Is this a # at the end
- if (queueList.size() == (depth + queueskip + 1))
+ if (bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_STAR_TOKEN))
{
- done = true;
+ depthPlusQueueSkip++;
+ depthPlusRoutingSkip++;
continue;
}
-
- // otherwise # in the middle
- while (routingkeyList.size() > (depth + routingskip))
+ else if (bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_HASH_TOKEN))
{
- if (routingkeyList.get(depth + routingskip).equals(queueList.get(depth + queueskip + 1)))
+ // Is this a # at the end
+ if (bindingKeyTokensCount == (depthPlusQueueSkip + 1))
+ {
+ done = true;
+
+ continue;
+ }
+
+ // otherwise # in the middle
+ while (routingTokensCount > depthPlusRoutingSkip)
{
- queueskip++;
- depth++;
+ if (routingkeyTokens[depthPlusRoutingSkip].equals(bindingKeyTokens[depthPlusQueueSkip + 1]))
+ {
+ depthPlusQueueSkip += 2;
+ depthPlusRoutingSkip++;
+
+ break;
+ }
- break;
+ depthPlusRoutingSkip++;
}
- routingskip++;
+ continue;
}
- continue;
+ matching = false;
}
- matching = false;
+ depthPlusQueueSkip++;
+ depthPlusRoutingSkip++;
}
- depth++;
+ if (matching)
+ {
+ if(list == null)
+ {
+ list = new ArrayList<AMQQueue>(_wildCardBindingKey2queues.get(bindingKey));
+ }
+ else
+ {
+ list.addAll(_wildCardBindingKey2queues.get(bindingKey));
+ }
+ }
}
- if (matching)
+ }
+ if(!_simpleBindingKey2queues.isEmpty())
+ {
+ List<AMQQueue> queues = _simpleBindingKey2queues.get(routingKey);
+ if(list == null)
+ {
+ if(queues == null)
+ {
+ list = Collections.EMPTY_LIST;
+ }
+ else
+ {
+ list = new ArrayList<AMQQueue>(queues);
+ }
+ }
+ else if(queues != null)
{
- list.addAll(_routingKey2queues.get(queue));
+ list.addAll(queues);
}
+
}
return list;
+
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
index 57ae2bb6d4..e7c887f306 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
@@ -42,6 +42,7 @@ import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import java.util.List;
import java.util.Map;
+import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class FanoutExchange extends AbstractExchange
@@ -205,10 +206,8 @@ public class FanoutExchange extends AbstractExchange
_logger.debug("Publishing message to queue " + _queues);
}
- for (AMQQueue q : _queues)
- {
- payload.enqueue(q);
- }
+ payload.enqueue(new ArrayList(_queues));
+
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java b/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java
index 2061803d65..32f58ed666 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java
@@ -56,7 +56,7 @@ public class JMSSelectorFilter implements MessageFilter
catch (AMQException e)
{
//fixme this needs to be sorted.. it shouldn't happen
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ e.printStackTrace();
}
return false;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
index bb48539f50..7cd4afdb77 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
@@ -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
@@ -24,18 +24,18 @@ import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.ConsumerTagNotUniqueException;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
public class BasicConsumeMethodHandler implements StateAwareMethodListener<BasicConsumeBody>
{
- private static final Logger _log = Logger.getLogger(BasicConsumeMethodHandler.class);
+ private static final Logger _logger = Logger.getLogger(BasicConsumeMethodHandler.class);
private static final BasicConsumeMethodHandler _instance = new BasicConsumeMethodHandler();
@@ -65,21 +65,21 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic
}
else
{
- if (_log.isDebugEnabled())
+ if (_logger.isDebugEnabled())
{
- _log.debug("BasicConsume: from '" + body.getQueue() +
- "' for:" + body.getConsumerTag() +
- " nowait:" + body.getNowait() +
- " args:" + body.getArguments());
+ _logger.debug("BasicConsume: from '" + body.getQueue() +
+ "' for:" + body.getConsumerTag() +
+ " nowait:" + body.getNowait() +
+ " args:" + body.getArguments());
}
AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue().intern());
if (queue == null)
{
- if (_log.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _log.trace("No queue for '" + body.getQueue() + "'");
+ _logger.debug("No queue for '" + body.getQueue() + "'");
}
if (body.getQueue() != null)
{
@@ -97,6 +97,9 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic
final AMQShortString consumerTagName;
+ //Perform ACLs
+ vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue);
+
if (body.getConsumerTag() != null)
{
consumerTagName = body.getConsumerTag().intern();
@@ -123,7 +126,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic
}
catch (org.apache.qpid.AMQInvalidArgumentException ise)
{
- _log.debug("Closing connection due to invalid selector");
+ _logger.debug("Closing connection due to invalid selector");
MethodRegistry methodRegistry = session.getMethodRegistry();
AMQMethodBody responseBody = methodRegistry.createChannelCloseBody(AMQConstant.INVALID_ARGUMENT.getCode(),
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
index 8d69697350..f8f9127809 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
@@ -27,10 +27,10 @@ import org.apache.qpid.framing.BasicGetBody;
import org.apache.qpid.framing.BasicGetEmptyBody;
import org.apache.qpid.framing.MethodRegistry;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -82,7 +82,11 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB
}
else
{
- if(!queue.performGet(session, channel, !body.getNoAck()))
+
+ //Perform ACLs
+ vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue);
+
+ if (!queue.performGet(session, channel, !body.getNoAck()))
{
MethodRegistry methodRegistry = session.getMethodRegistry();
// TODO - set clusterId
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
index 66afc61751..0f99a21ee5 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
@@ -25,20 +25,19 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.BasicPublishBody;
import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.MethodRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
-public class BasicPublishMethodHandler implements StateAwareMethodListener<BasicPublishBody>
+public class BasicPublishMethodHandler implements StateAwareMethodListener<BasicPublishBody>
{
- private static final Logger _log = Logger.getLogger(BasicPublishMethodHandler.class);
+ private static final Logger _logger = Logger.getLogger(BasicPublishMethodHandler.class);
private static final BasicPublishMethodHandler _instance = new BasicPublishMethodHandler();
@@ -55,12 +54,9 @@ public class BasicPublishMethodHandler implements StateAwareMethodListener<Basi
public void methodReceived(AMQStateManager stateManager, BasicPublishBody body, int channelId) throws AMQException
{
AMQProtocolSession session = stateManager.getProtocolSession();
-
-
-
- if (_log.isDebugEnabled())
+ if (_logger.isDebugEnabled())
{
- _log.debug("Publish received on channel " + channelId);
+ _logger.debug("Publish received on channel " + channelId);
}
AMQShortString exchange = body.getExchange();
@@ -90,8 +86,12 @@ public class BasicPublishMethodHandler implements StateAwareMethodListener<Basi
throw body.getChannelNotFoundException(channelId);
}
+ //Access Control
+ vHost.getAccessManager().authorise(session, Permission.PUBLISH, body, e);
+
MessagePublishInfo info = session.getMethodRegistry().getProtocolVersionMethodConverter().convertToInfo(body);
- channel.setPublishFrame(info, session);
+ info.setExchange(exchange);
+ channel.setPublishFrame(info, session, e);
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
index c48dd902eb..069cc6ea2c 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
@@ -100,9 +100,9 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR
}
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Rejecting: DT:" + deliveryTag + "-" + message.getMessage().debugIdentity() +
+ _logger.debug("Rejecting: DT:" + deliveryTag + "-" + message.getMessage().debugIdentity() +
": Requeue:" + body.getRequeue() +
//": Resend:" + evt.getMethod().resend +
" on channel:" + channel.debugIdentity());
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java
index 3f604480b9..054674aed4 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java
@@ -29,7 +29,6 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.*;
import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9;
import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.protocol.AMQProtocolSession;
@@ -55,8 +54,8 @@ public class ChannelOpenHandler implements StateAwareMethodListener<ChannelOpenB
AMQProtocolSession session = stateManager.getProtocolSession();
VirtualHost virtualHost = session.getVirtualHost();
- final AMQChannel channel = new AMQChannel(session,channelId, virtualHost.getMessageStore(),
- virtualHost.getExchangeRegistry());
+ final AMQChannel channel = new AMQChannel(session,channelId, virtualHost.getMessageStore()
+ );
session.addChannel(channel);
ChannelOpenOkBody response;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java
index 4f62e0b930..f99e650979 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java
@@ -23,14 +23,12 @@ package org.apache.qpid.server.handler;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQState;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.security.access.AccessResult;
-import org.apache.qpid.server.security.access.AccessRights;
import org.apache.log4j.Logger;
public class ConnectionOpenMethodHandler implements StateAwareMethodListener<ConnectionOpenBody>
@@ -79,22 +77,8 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con
{
session.setVirtualHost(virtualHost);
- AccessResult result = virtualHost.getAccessManager().isAuthorized(virtualHost, session.getAuthorizedID(), AccessRights.Rights.ANY);
-
- switch (result.getStatus())
- {
- default:
- case REFUSED:
- String error = "Any access denied to vHost '" + virtualHostName + "' by "
- + result.getAuthorizer();
-
- _logger.warn(error);
-
- throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, error);
- case GRANTED:
- _logger.info("Granted any access to vHost '" + virtualHostName + "' for " + session.getAuthorizedID()
- + " by '" + result.getAuthorizer() + "'");
- }
+ //Perform ACL
+ virtualHost.getAccessManager().authorise(session, Permission.ACCESS ,body, virtualHost);
// See Spec (0.8.2). Section 3.1.2 Virtual Hosts
if (session.getContextKey() == null)
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 c4a57f0286..9a98bc9659 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
@@ -26,11 +26,11 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.AMQUnknownExchangeType;
import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.exchange.ExchangeFactory;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -58,7 +58,12 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange
VirtualHost virtualHost = session.getVirtualHost();
ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory();
-
+
+ if (!body.getPassive())
+ {
+ //Perform ACL if request is not passive
+ virtualHost.getAccessManager().authorise(session, Permission.CREATE, body);
+ }
if (_logger.isDebugEnabled())
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java
index 8f36e6c767..888ffcb2e5 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java
@@ -21,13 +21,12 @@
package org.apache.qpid.server.handler;
import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQFrame;
import org.apache.qpid.framing.ExchangeDeleteBody;
import org.apache.qpid.framing.ExchangeDeleteOkBody;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.exchange.ExchangeInUseException;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -51,6 +50,9 @@ public class ExchangeDeleteHandler implements StateAwareMethodListener<ExchangeD
VirtualHost virtualHost = session.getVirtualHost();
ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+ //Perform ACLs
+ virtualHost.getAccessManager().authorise(session, Permission.DELETE,body,
+ exchangeRegistry.getExchange(body.getExchange()));
try
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
index a365cd864a..0f6dc7a19d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
@@ -25,13 +25,13 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.AMQInvalidRoutingKeyException;
import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -106,6 +106,10 @@ public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody>
try
{
+
+ //Perform ACLs
+ virtualHost.getAccessManager().authorise(session, Permission.BIND, body, exch, queue, routingKey);
+
if (!exch.isBound(routingKey, body.getArguments(), queue))
{
queue.bind(routingKey, body.getArguments(), exch);
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
index 067c6ac285..7df864f189 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
@@ -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
@@ -20,17 +20,15 @@
*/
package org.apache.qpid.server.handler;
-import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.UUID;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.configuration.Configured;
-import org.apache.qpid.exchange.ExchangeDefaults;
+
import org.apache.qpid.framing.*;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.configuration.Configurator;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
import org.apache.qpid.server.exchange.Exchange;
@@ -38,6 +36,7 @@ import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.store.MessageStore;
@@ -47,7 +46,7 @@ import org.apache.commons.configuration.Configuration;
public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclareBody>
{
- private static final Logger _log = Logger.getLogger(QueueDeclareHandler.class);
+ private static final Logger _logger = Logger.getLogger(QueueDeclareHandler.class);
private static final QueueDeclareHandler _instance = new QueueDeclareHandler();
@@ -56,7 +55,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
return _instance;
}
- @Configured(path = "queue.auto_register", defaultValue = "false")
+ @Configured(path = "queue.auto_register", defaultValue = "true")
public boolean autoRegister;
private final AtomicInteger _counter = new AtomicInteger();
@@ -76,6 +75,11 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
MessageStore store = virtualHost.getMessageStore();
+ if (!body.getPassive())
+ {
+ //Perform ACL if request is not passive
+ virtualHost.getAccessManager().authorise(session, Permission.CREATE, body);
+ }
final AMQShortString queueName;
@@ -109,7 +113,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
}
else
{
- queue = createQueue(queueName,body, virtualHost, session);
+ queue = createQueue(queueName, body, virtualHost, session);
if (queue.isDurable() && !queue.isAutoDelete())
{
store.createQueue(queue);
@@ -120,7 +124,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
Exchange defaultExchange = exchangeRegistry.getDefaultExchange();
queue.bind(queueName, null, defaultExchange);
- _log.info("Queue " + queueName + " bound to default exchange(" + defaultExchange.getName() + ")");
+ _logger.info("Queue " + queueName + " bound to default exchange(" + defaultExchange.getName() + ")");
}
}
}
@@ -152,7 +156,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
queue.getConsumerCount());
session.writeFrame(responseBody.generateFrame(channelId));
- _log.info("Queue " + queueName + " declared successfully");
+ _logger.info("Queue " + queueName + " declared successfully");
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
index 09b7de3a5c..310a73ffeb 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
@@ -34,6 +34,7 @@ import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.security.access.Permission;
public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteBody>
{
@@ -103,6 +104,10 @@ public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteB
}
else
{
+
+ //Perform ACLs
+ virtualHost.getAccessManager().authorise(session, Permission.DELETE, body, queue);
+
int purged = queue.delete(body.getIfUnused(), body.getIfEmpty());
if (queue.isDurable())
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
index 5bdca93bc6..cce49f13c7 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
@@ -35,6 +35,7 @@ import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.security.access.Permission;
public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBody>
{
@@ -100,6 +101,10 @@ public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBod
}
else
{
+
+ //Perform ACLs
+ virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue);
+
long purged = queue.clearQueue(channel.getStoreContext());
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java
index b056fa6797..e758e315aa 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java
@@ -1,110 +1,113 @@
-package org.apache.qpid.server.handler;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.AMQInvalidRoutingKeyException;
-import org.apache.qpid.protocol.AMQConstant;
-
-public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindBody>
-{
- private static final Logger _log = Logger.getLogger(QueueUnbindHandler.class);
-
- private static final QueueUnbindHandler _instance = new QueueUnbindHandler();
-
- public static QueueUnbindHandler getInstance()
- {
- return _instance;
- }
-
- private QueueUnbindHandler()
- {
- }
-
- public void methodReceived(AMQStateManager stateManager, QueueUnbindBody body, int channelId) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
- VirtualHost virtualHost = session.getVirtualHost();
- ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
- QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
-
-
- final AMQQueue queue;
- final AMQShortString routingKey;
-
- if (body.getQueue() == null)
- {
- AMQChannel channel = session.getChannel(channelId);
-
- if (channel == null)
- {
- throw body.getChannelNotFoundException(channelId);
- }
-
- queue = channel.getDefaultQueue();
-
- if (queue == null)
- {
- throw body.getConnectionException(AMQConstant.NOT_FOUND, "No default queue defined on channel and queue was null");
- }
-
- routingKey = body.getRoutingKey() == null ? null : body.getRoutingKey().intern();
-
- }
- else
- {
- queue = queueRegistry.getQueue(body.getQueue());
- routingKey = body.getRoutingKey() == null ? null : body.getRoutingKey().intern();
- }
-
- if (queue == null)
- {
- throw body.getConnectionException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist.");
- }
- final Exchange exch = exchangeRegistry.getExchange(body.getExchange());
- if (exch == null)
- {
- throw body.getChannelException(AMQConstant.NOT_FOUND, "Exchange " + body.getExchange() + " does not exist.");
- }
-
-
- try
- {
- queue.unBind(routingKey, body.getArguments(), exch);
- }
- catch (AMQInvalidRoutingKeyException rke)
- {
- throw body.getChannelException(AMQConstant.INVALID_ROUTING_KEY, routingKey.toString());
- }
- catch (AMQException e)
- {
- if(e.getErrorCode() == AMQConstant.NOT_FOUND)
- {
- throw body.getConnectionException(AMQConstant.NOT_FOUND,e.getMessage(),e);
- }
- throw body.getChannelException(AMQConstant.CHANNEL_ERROR, e.toString());
- }
-
- if (_log.isInfoEnabled())
- {
- _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + routingKey);
- }
-
- MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry();
- AMQMethodBody responseBody = methodRegistry.createQueueUnbindOkBody();
- session.writeFrame(responseBody.generateFrame(channelId));
-
-
- }
-}
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.security.access.Permission;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInvalidRoutingKeyException;
+import org.apache.qpid.protocol.AMQConstant;
+
+public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindBody>
+{
+ private static final Logger _log = Logger.getLogger(QueueUnbindHandler.class);
+
+ private static final QueueUnbindHandler _instance = new QueueUnbindHandler();
+
+ public static QueueUnbindHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private QueueUnbindHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueUnbindBody body, int channelId) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+
+
+ final AMQQueue queue;
+ final AMQShortString routingKey;
+
+ if (body.getQueue() == null)
+ {
+ AMQChannel channel = session.getChannel(channelId);
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(channelId);
+ }
+
+ queue = channel.getDefaultQueue();
+
+ if (queue == null)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_FOUND, "No default queue defined on channel and queue was null");
+ }
+
+ routingKey = body.getRoutingKey() == null ? null : body.getRoutingKey().intern();
+
+ }
+ else
+ {
+ queue = queueRegistry.getQueue(body.getQueue());
+ routingKey = body.getRoutingKey() == null ? null : body.getRoutingKey().intern();
+ }
+
+ if (queue == null)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist.");
+ }
+ final Exchange exch = exchangeRegistry.getExchange(body.getExchange());
+ if (exch == null)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Exchange " + body.getExchange() + " does not exist.");
+ }
+
+ //Perform ACLs
+ virtualHost.getAccessManager().authorise(session, Permission.UNBIND, body, queue);
+
+ try
+ {
+ queue.unBind(routingKey, body.getArguments(), exch);
+ }
+ catch (AMQInvalidRoutingKeyException rke)
+ {
+ throw body.getChannelException(AMQConstant.INVALID_ROUTING_KEY, routingKey.toString());
+ }
+ catch (AMQException e)
+ {
+ if(e.getErrorCode() == AMQConstant.NOT_FOUND)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_FOUND,e.getMessage(),e);
+ }
+ throw body.getChannelException(AMQConstant.CHANNEL_ERROR, e.toString());
+ }
+
+ if (_log.isInfoEnabled())
+ {
+ _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + routingKey);
+ }
+
+ MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry();
+ AMQMethodBody responseBody = methodRegistry.createQueueUnbindOkBody();
+ session.writeFrame(responseBody.generateFrame(channelId));
+
+
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
index 4fb260472d..a0ecc2bd85 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
@@ -20,7 +20,7 @@
*/
package org.apache.qpid.server.management;
-import org.apache.qpid.server.security.access.UserManagement;
+import org.apache.qpid.server.security.access.management.UserManagement;
import org.apache.log4j.Logger;
import javax.management.remote.MBeanServerForwarder;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
index 1bfe1e3d35..d7a879180a 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
@@ -72,7 +72,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
throws AMQException
{
- ByteBuffer deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
+ AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
message.getContentHeaderBody());
@@ -100,8 +100,8 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
- AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
writeFrame(compositeBlock);
//
@@ -127,7 +127,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
final StoreContext storeContext = message.getStoreContext();
final long messageId = message.getMessageId();
- ByteBuffer deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
+ AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
@@ -151,8 +151,8 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
- AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
writeFrame(compositeBlock);
//
@@ -171,7 +171,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
}
- private ByteBuffer createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
throws AMQException
{
final MessagePublishInfo pb = message.getMessagePublishInfo();
@@ -187,10 +187,10 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
AMQFrame deliverFrame = deliverBody.generateFrame(channelId);
- return deliverFrame.toByteBuffer();
+ return deliverFrame;
}
- private ByteBuffer createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
+ private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
throws AMQException
{
final MessagePublishInfo pb = message.getMessagePublishInfo();
@@ -205,7 +205,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
queueSize);
AMQFrame getOkFrame = getOkBody.generateFrame(channelId);
- return getOkFrame.toByteBuffer();
+ return getOkFrame;
}
public byte getProtocolMinorVersion()
@@ -218,7 +218,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
return getProtocolSession().getProtocolMajorVersion();
}
- private ByteBuffer createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
{
MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
BasicReturnBody basicReturnBody =
@@ -228,13 +228,13 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
message.getMessagePublishInfo().getRoutingKey());
AMQFrame returnFrame = basicReturnBody.generateFrame(channelId);
- return returnFrame.toByteBuffer();
+ return returnFrame;
}
public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
throws AMQException
{
- ByteBuffer returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
+ AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
message.getContentHeaderBody());
@@ -247,14 +247,13 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
if (bodyFrameIterator.hasNext())
{
AMQDataBlock firstContentBody = bodyFrameIterator.next();
- AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, headerAndFirstContent);
+ AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
writeFrame(compositeBlock);
}
else
{
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame,
- new AMQDataBlock[]{contentHeader});
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader});
writeFrame(compositeBlock);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java
index 0bc2fcf6f7..646ef43826 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java
@@ -12,10 +12,14 @@ import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.framing.*;
import org.apache.qpid.framing.abstraction.ContentChunk;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
{
+ private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
+ private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter();
public static Factory getInstanceFactory()
@@ -46,9 +50,9 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
throws AMQException
{
- ByteBuffer deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
- message.getContentHeaderBody());
+ AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
+ final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody();
+
final AMQMessageHandle messageHandle = message.getMessageHandle();
final StoreContext storeContext = message.getStoreContext();
@@ -58,8 +62,8 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
if(bodyCount == 0)
{
- SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
- contentHeader);
+ SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody,
+ contentHeaderBody);
writeFrame(compositeBlock);
}
@@ -73,9 +77,9 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
//
ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
- AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
- AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb);
+
+ CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody);
writeFrame(compositeBlock);
//
@@ -84,7 +88,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
for(int i = 1; i < bodyCount; i++)
{
cb = messageHandle.getContentChunk(storeContext,messageId, i);
- writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
}
@@ -93,6 +97,14 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
}
+ private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody)
+ {
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ contentHeaderBody);
+ return contentHeader;
+ }
+
public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException
{
@@ -101,11 +113,10 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
final StoreContext storeContext = message.getStoreContext();
final long messageId = message.getMessageId();
- ByteBuffer deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
+ AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
- message.getContentHeaderBody());
+ AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody());
final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
if(bodyCount == 0)
@@ -124,9 +135,9 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
//
ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
- AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
- AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb));
+ AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
writeFrame(compositeBlock);
//
@@ -135,7 +146,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
for(int i = 1; i < bodyCount; i++)
{
cb = messageHandle.getContentChunk(storeContext, messageId, i);
- writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
}
@@ -145,41 +156,84 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
}
- private ByteBuffer createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag)
throws AMQException
{
final MessagePublishInfo pb = message.getMessagePublishInfo();
final AMQMessageHandle messageHandle = message.getMessageHandle();
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
- BasicDeliverBody deliverBody =
- methodRegistry.createBasicDeliverBody(consumerTag,
- deliveryTag,
- messageHandle.isRedelivered(),
- pb.getExchange(),
- pb.getRoutingKey());
- AMQFrame deliverFrame = deliverBody.generateFrame(channelId);
+
+ final boolean isRedelivered = messageHandle.isRedelivered();
+ final AMQShortString exchangeName = pb.getExchange();
+ final AMQShortString routingKey = pb.getRoutingKey();
+
+ final AMQBody returnBlock = new AMQBody()
+ {
+
+ public AMQBody _underlyingBody;
+
+ public AMQBody createAMQBody()
+ {
+ return METHOD_REGISTRY.createBasicDeliverBody(consumerTag,
+ deliveryTag,
+ isRedelivered,
+ exchangeName,
+ routingKey);
+
+
- return deliverFrame.toByteBuffer();
+
+ }
+
+ public byte getFrameType()
+ {
+ return AMQMethodBody.TYPE;
+ }
+
+ public int getSize()
+ {
+ if(_underlyingBody == null)
+ {
+ _underlyingBody = createAMQBody();
+ }
+ return _underlyingBody.getSize();
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ if(_underlyingBody == null)
+ {
+ _underlyingBody = createAMQBody();
+ }
+ _underlyingBody.writePayload(buffer);
+ }
+
+ public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession)
+ throws AMQException
+ {
+ throw new AMQException("This block should never be dispatched!");
+ }
+ };
+ return returnBlock;
}
- private ByteBuffer createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
+ private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
throws AMQException
{
final MessagePublishInfo pb = message.getMessagePublishInfo();
final AMQMessageHandle messageHandle = message.getMessageHandle();
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
+
BasicGetOkBody getOkBody =
- methodRegistry.createBasicGetOkBody(deliveryTag,
+ METHOD_REGISTRY.createBasicGetOkBody(deliveryTag,
messageHandle.isRedelivered(),
pb.getExchange(),
pb.getRoutingKey(),
queueSize);
AMQFrame getOkFrame = getOkBody.generateFrame(channelId);
- return getOkFrame.toByteBuffer();
+ return getOkFrame;
}
public byte getProtocolMinorVersion()
@@ -192,26 +246,25 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
return getProtocolSession().getProtocolMajorVersion();
}
- private ByteBuffer createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
{
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
+
BasicReturnBody basicReturnBody =
- methodRegistry.createBasicReturnBody(replyCode,
+ METHOD_REGISTRY.createBasicReturnBody(replyCode,
replyText,
message.getMessagePublishInfo().getExchange(),
message.getMessagePublishInfo().getRoutingKey());
AMQFrame returnFrame = basicReturnBody.generateFrame(channelId);
- return returnFrame.toByteBuffer();
+ return returnFrame;
}
public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
throws AMQException
{
- ByteBuffer returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
+ AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
- message.getContentHeaderBody());
+ AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody());
Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId);
//
@@ -221,14 +274,13 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
if (bodyFrameIterator.hasNext())
{
AMQDataBlock firstContentBody = bodyFrameIterator.next();
- AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, headerAndFirstContent);
+ AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
writeFrame(compositeBlock);
}
else
{
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame,
- new AMQDataBlock[]{contentHeader});
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader});
writeFrame(compositeBlock);
}
@@ -252,9 +304,69 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
{
- MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
- BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag);
+
+ BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag);
writeFrame(basicCancelOkBody.generateFrame(channelId));
}
+
+
+ public static final class CompositeAMQBodyBlock extends AMQDataBlock
+ {
+ public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead();
+
+ private final AMQBody _methodBody;
+ private final AMQBody _headerBody;
+ private final AMQBody _contentBody;
+ private final int _channel;
+
+
+ public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody)
+ {
+ _channel = channel;
+ _methodBody = methodBody;
+ _headerBody = headerBody;
+ _contentBody = contentBody;
+
+ }
+
+ public long getSize()
+ {
+ return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize();
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody);
+ }
+ }
+
+ public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock
+ {
+ public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead();
+
+ private final AMQBody _methodBody;
+ private final AMQBody _headerBody;
+ private final int _channel;
+
+
+ public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody)
+ {
+ _channel = channel;
+ _methodBody = methodBody;
+ _headerBody = headerBody;
+
+ }
+
+ public long getSize()
+ {
+ return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ;
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody);
+ }
+ }
+
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java b/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java
index be22a90d0b..9191ecf6ed 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java
@@ -21,7 +21,6 @@ package org.apache.qpid.server.plugins;
import java.io.File;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,7 +29,6 @@ import org.apache.felix.framework.Felix;
import org.apache.felix.framework.cache.BundleCache;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
-import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.exchange.ExchangeType;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleException;
@@ -72,7 +70,7 @@ public class PluginManager
"org.apache.qpid.server.queue; version=0.2.1," +
"javax.management.openmbean; version=1.0.0,"+
"javax.management; version=1.0.0,"+
- "uk.co.thebadgerset.junit.extensions.util; version=0.6.1,"
+ "org.apache.qpid.junit.extensions.util; version=0.6.1,"
);
if (plugindir == null)
diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
index 0fe6d3636e..4267642b14 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
@@ -109,7 +109,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
private FieldTable _clientProperties;
private final List<Task> _taskList = new CopyOnWriteArrayList<Task>();
- private List<Integer> _closingChannelsList = new ArrayList<Integer>();
+ private List<Integer> _closingChannelsList = new CopyOnWriteArrayList<Integer>();
private ProtocolOutputConverter _protocolOutputConverter;
private Principal _authorizedID;
private MethodDispatcher _dispatcher;
@@ -138,11 +138,9 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
catch (RuntimeException e)
{
e.printStackTrace();
- // throw e;
+ throw e;
}
-
- // this(session, queueRegistry, exchangeRegistry, codecFactory, new AMQStateManager());
}
public AMQMinaProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory,
@@ -209,26 +207,39 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
_logger.debug("Frame Received: " + frame);
}
- if (body instanceof AMQMethodBody)
- {
- methodFrameReceived(channelId, (AMQMethodBody) body);
- }
- else if (body instanceof ContentHeaderBody)
- {
- contentHeaderReceived(channelId, (ContentHeaderBody) body);
- }
- else if (body instanceof ContentBody)
+ // Check that this channel is not closing
+ if (channelAwaitingClosure(channelId))
{
- contentBodyReceived(channelId, (ContentBody) body);
+ if ((frame.getBodyFrame() instanceof ChannelCloseOkBody))
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Channel[" + channelId + "] awaiting closure - processing close-ok");
+ }
+ }
+ else
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Channel[" + channelId + "] awaiting closure ignoring");
+ }
+
+ return;
+ }
}
- else if (body instanceof HeartbeatBody)
+
+
+
+ try
{
- // NO OP
+ body.handle(channelId, this);
}
- else
+ catch (AMQException e)
{
- _logger.warn("Unrecognised frame " + frame.getClass().getName());
+ closeChannel(channelId);
+ throw e;
}
+
}
private void protocolInitiationReceived(ProtocolInitiation pi)
@@ -271,32 +282,11 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
}
}
- private void methodFrameReceived(int channelId, AMQMethodBody methodBody)
+ public void methodFrameReceived(int channelId, AMQMethodBody methodBody)
{
final AMQMethodEvent<AMQMethodBody> evt = new AMQMethodEvent<AMQMethodBody>(channelId, methodBody);
- // Check that this channel is not closing
- if (channelAwaitingClosure(channelId))
- {
- if ((evt.getMethod() instanceof ChannelCloseOkBody))
- {
- if (_logger.isInfoEnabled())
- {
- _logger.info("Channel[" + channelId + "] awaiting closure - processing close-ok");
- }
- }
- else
- {
- if (_logger.isInfoEnabled())
- {
- _logger.info("Channel[" + channelId + "] awaiting closure ignoring");
- }
-
- return;
- }
- }
-
try
{
try
@@ -358,6 +348,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
_logger.info("Closing connection due to: " + e.getMessage());
}
+ markChannelAwaitingCloseOk(channelId);
closeSession();
_stateManager.changeState(AMQState.CONNECTION_CLOSING);
writeFrame(e.getCloseFrame(channelId));
@@ -365,17 +356,19 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
}
catch (Exception e)
{
- _stateManager.error(e);
+
for (AMQMethodListener listener : _frameListeners)
{
listener.error(e);
}
+ _logger.error("Unexpected exception while processing frame. Closing connection.", e);
+
_minaProtocolSession.close();
}
}
- private void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException
+ public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException
{
AMQChannel channel = getAndAssertChannel(channelId);
@@ -384,13 +377,18 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
}
- private void contentBodyReceived(int channelId, ContentBody body) throws AMQException
+ public void contentBodyReceived(int channelId, ContentBody body) throws AMQException
{
AMQChannel channel = getAndAssertChannel(channelId);
channel.publishContentBody(body, this);
}
+ public void heartbeatBodyReceived(int channelId, HeartbeatBody body)
+ {
+ // NO - OP
+ }
+
/**
* Convenience method that writes a frame to the protocol session. Equivalent to calling
* getProtocolSession().write().
@@ -539,7 +537,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
try
{
channel.close(this);
- markChannelawaitingCloseOk(channelId);
+ markChannelAwaitingCloseOk(channelId);
}
finally
{
@@ -550,11 +548,19 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
public void closeChannelOk(int channelId)
{
- removeChannel(channelId);
+ // todo QPID-847 - This is called from two lcoations ChannelCloseHandler and ChannelCloseOkHandler.
+ // When it is the CC_OK_Handler then it makes sence to remove the channel else we will leak memory.
+ // We do it from the Close Handler as we are sending the OK back to the client.
+ // While this is AMQP spec compliant. The Java client in the event of an IllegalArgumentException
+ // will send a close-ok.. Where we should call removeChannel.
+ // However, due to the poor exception handling on the client. The client-user will be notified of the
+ // InvalidArgument and if they then decide to close the session/connection then the there will be time
+ // for that to occur i.e. a new close method be sent before the exeption handling can mark the session closed.
+ //removeChannel(channelId);
_closingChannelsList.remove(new Integer(channelId));
}
- private void markChannelawaitingCloseOk(int channelId)
+ private void markChannelAwaitingCloseOk(int channelId)
{
_closingChannelsList.add(channelId);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
index 543e043bed..d8dbf97e49 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
@@ -29,9 +29,8 @@ 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.ProtocolCodecFilter;
+import org.apache.mina.filter.codec.QpidProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
-import org.apache.mina.transport.socket.nio.SocketSessionConfig;
import org.apache.mina.util.SessionUtil;
import org.apache.qpid.AMQException;
import org.apache.qpid.codec.AMQCodecFactory;
@@ -57,6 +56,11 @@ public class AMQPFastProtocolHandler extends IoHandlerAdapter
private final IApplicationRegistry _applicationRegistry;
+ private static String DEFAULT_BUFFER_READ_LIMIT_SIZE = "262144";
+ private static String DEFAULT_BUFFER_WRITE_LIMIT_SIZE = "262144";
+
+ private final int BUFFER_READ_LIMIT_SIZE;
+ private final int BUFFER_WRITE_LIMIT_SIZE;
public AMQPFastProtocolHandler(Integer applicationRegistryInstance)
{
@@ -66,6 +70,11 @@ public class AMQPFastProtocolHandler extends IoHandlerAdapter
public AMQPFastProtocolHandler(IApplicationRegistry applicationRegistry)
{
_applicationRegistry = applicationRegistry;
+
+ // Read the configuration from the application registry
+ BUFFER_READ_LIMIT_SIZE = Integer.parseInt(_applicationRegistry.getConfiguration().getString("broker.connector.protectio.readBufferLimitSize", DEFAULT_BUFFER_READ_LIMIT_SIZE));
+ BUFFER_WRITE_LIMIT_SIZE = Integer.parseInt(_applicationRegistry.getConfiguration().getString("broker.connector.protectio.writeBufferLimitSize", DEFAULT_BUFFER_WRITE_LIMIT_SIZE));
+
_logger.debug("AMQPFastProtocolHandler created");
}
@@ -82,7 +91,7 @@ public class AMQPFastProtocolHandler extends IoHandlerAdapter
createSession(protocolSession, _applicationRegistry, codecFactory);
_logger.info("Protocol session created for:" + protocolSession.getRemoteAddress());
- final ProtocolCodecFilter pcf = new ProtocolCodecFilter(codecFactory);
+ final QpidProtocolCodecFilter pcf = new QpidProtocolCodecFilter(codecFactory);
ConnectorConfiguration connectorConfig = ApplicationRegistry.getInstance().
getConfiguredObject(ConnectorConfiguration.class);
@@ -114,27 +123,22 @@ public class AMQPFastProtocolHandler extends IoHandlerAdapter
}
- if (ApplicationRegistry.getInstance().getConfiguration().getBoolean("broker.connector.protectio", false))
+ if (ApplicationRegistry.getInstance().getConfiguration().getBoolean("broker.connector.protectio.enabled", false))
{
try
{
// //Add IO Protection Filters
IoFilterChain chain = protocolSession.getFilterChain();
- int buf_size = 32768;
- if (protocolSession.getConfig() instanceof SocketSessionConfig)
- {
- buf_size = ((SocketSessionConfig) protocolSession.getConfig()).getReceiveBufferSize();
- }
protocolSession.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter());
ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
- readfilter.setMaximumConnectionBufferSize(buf_size);
+ readfilter.setMaximumConnectionBufferSize(BUFFER_READ_LIMIT_SIZE);
readfilter.attach(chain);
WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
- writefilter.setMaximumConnectionBufferSize(buf_size * 2);
+ writefilter.setMaximumConnectionBufferSize(BUFFER_WRITE_LIMIT_SIZE);
writefilter.attach(chain);
protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder");
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
index d9a9d2273b..f501bc27d1 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
@@ -35,6 +35,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.TransactionalContext;
+import org.apache.qpid.server.exchange.Exchange;
import java.util.HashMap;
import java.util.HashSet;
@@ -82,12 +83,16 @@ public class AMQMessage
private long _expiration;
- private final int hashcode = System.identityHashCode(this);
+
+
+ private Exchange _exchange;
+ private static final boolean SYNCED_CLOCKS =
+ ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false);
public String debugIdentity()
{
- return "(HC:" + hashcode + " ID:" + _messageId + " Ref:" + _referenceCount.get() + ")";
+ return "(HC:" + System.identityHashCode(this) + " ID:" + _messageId + " Ref:" + _referenceCount.get() + ")";
}
public void setExpiration()
@@ -97,7 +102,7 @@ public class AMQMessage
long timestamp =
((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getTimestamp();
- if (ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false))
+ if (SYNCED_CLOCKS)
{
_expiration = expiration;
}
@@ -126,6 +131,21 @@ public class AMQMessage
return _referenceCount.get() > 0;
}
+ public void setExchange(final Exchange exchange)
+ {
+ _exchange = exchange;
+ }
+
+ public void route() throws AMQException
+ {
+ _exchange.route(this);
+ }
+
+ public void enqueue(final List<AMQQueue> queues)
+ {
+ _transientMessageData.setDestinationQueues(queues);
+ }
+
/**
* Used to iterate through all the body frames associated with this message. Will not keep all the data in memory
* therefore is memory-efficient.
@@ -637,8 +657,6 @@ public class AMQMessage
// now that it has all been received, before we attempt delivery
_txnContext.messageFullyReceived(isPersistent());
- _transientMessageData = null;
-
for (AMQQueue q : destinationQueues)
{
// Increment the references to this message for each queue delivery.
@@ -649,7 +667,7 @@ public class AMQMessage
}
finally
{
- destinationQueues.clear();
+
// Remove refence for routing process . Reference count should now == delivered queue count
decrementReference(storeContext);
}
@@ -687,10 +705,6 @@ public class AMQMessage
_transientMessageData = transientMessageData;
}
- public void clearTransientMessageData()
- {
- _transientMessageData = null;
- }
public String toString()
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
index 53c36d9718..7c6db0b4b3 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
@@ -26,6 +26,7 @@ import org.apache.qpid.configuration.Configured;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.management.Managable;
import org.apache.qpid.server.management.ManagedObject;
@@ -36,7 +37,7 @@ import org.apache.qpid.server.virtualhost.VirtualHost;
import javax.management.JMException;
import java.text.MessageFormat;
-import java.util.List;
+import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -154,10 +155,9 @@ public class AMQQueue implements Managable, Comparable
/** total messages received by the queue since startup. */
public AtomicLong _totalMessagesReceived = new AtomicLong();
- public int compareTo(Object o)
- {
- return _name.compareTo(((AMQQueue) o).getName());
- }
+
+ private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class);
+
public AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
throws AMQException
@@ -200,6 +200,13 @@ public class AMQQueue implements Managable, Comparable
_subscribers = subscribers;
_subscriptionFactory = subscriptionFactory;
_deliveryMgr = new ConcurrentSelectorDeliveryManager(_subscribers, this);
+
+ // This ensure that the notification checks for the configured alerts are created.
+ setMaximumMessageAge(_maximumMessageAge);
+ setMaximumMessageCount(_maximumMessageCount);
+ setMaximumMessageSize(_maximumMessageSize);
+ setMaximumQueueDepth(_maximumQueueDepth);
+
}
private AMQQueueMBean createMBean() throws AMQException
@@ -214,7 +221,7 @@ public class AMQQueue implements Managable, Comparable
}
}
- public AMQShortString getName()
+ public final AMQShortString getName()
{
return _name;
}
@@ -540,9 +547,17 @@ public class AMQQueue implements Managable, Comparable
return _maximumMessageSize;
}
- public void setMaximumMessageSize(long value)
+ public void setMaximumMessageSize(final long maximumMessageSize)
{
- _maximumMessageSize = value;
+ _maximumMessageSize = maximumMessageSize;
+ if(maximumMessageSize == 0L)
+ {
+ _notificationChecks.remove(NotificationCheck.MESSAGE_SIZE_ALERT);
+ }
+ else
+ {
+ _notificationChecks.add(NotificationCheck.MESSAGE_SIZE_ALERT);
+ }
}
public int getConsumerCount()
@@ -565,9 +580,20 @@ public class AMQQueue implements Managable, Comparable
return _maximumMessageCount;
}
- public void setMaximumMessageCount(long value)
+ public void setMaximumMessageCount(final long maximumMessageCount)
{
- _maximumMessageCount = value;
+ _maximumMessageCount = maximumMessageCount;
+ if(maximumMessageCount == 0L)
+ {
+ _notificationChecks.remove(NotificationCheck.MESSAGE_COUNT_ALERT);
+ }
+ else
+ {
+ _notificationChecks.add(NotificationCheck.MESSAGE_COUNT_ALERT);
+ }
+
+
+
}
public long getMaximumQueueDepth()
@@ -576,9 +602,18 @@ public class AMQQueue implements Managable, Comparable
}
// Sets the queue depth, the max queue size
- public void setMaximumQueueDepth(long value)
+ public void setMaximumQueueDepth(final long maximumQueueDepth)
{
- _maximumQueueDepth = value;
+ _maximumQueueDepth = maximumQueueDepth;
+ if(maximumQueueDepth == 0L)
+ {
+ _notificationChecks.remove(NotificationCheck.QUEUE_DEPTH_ALERT);
+ }
+ else
+ {
+ _notificationChecks.add(NotificationCheck.QUEUE_DEPTH_ALERT);
+ }
+
}
public long getOldestMessageArrivalTime()
@@ -661,6 +696,12 @@ public class AMQQueue implements Managable, Comparable
}
_subscribers.addSubscriber(subscription);
+ if(exclusive)
+ {
+ _subscribers.setExclusive(true);
+ }
+
+ subscription.start();
}
private boolean isExclusive()
@@ -692,6 +733,7 @@ public class AMQQueue implements Managable, Comparable
ps, channel, consumerTag, this));
}
+ _subscribers.setExclusive(false);
Subscription removedSubscription;
if ((removedSubscription = _subscribers.removeSubscriber(_subscriptionFactory.createSubscription(channel, ps,
consumerTag)))
@@ -805,7 +847,7 @@ public class AMQQueue implements Managable, Comparable
public void process(StoreContext storeContext, QueueEntry entry, boolean deliverFirst) throws AMQException
{
AMQMessage msg = entry.getMessage();
- _deliveryMgr.deliver(storeContext, getName(), entry, deliverFirst);
+ _deliveryMgr.deliver(storeContext, _name, entry, deliverFirst);
try
{
msg.checkDeliveredToConsumer();
@@ -938,6 +980,14 @@ public class AMQQueue implements Managable, Comparable
public void setMaximumMessageAge(long maximumMessageAge)
{
_maximumMessageAge = maximumMessageAge;
+ if(maximumMessageAge == 0L)
+ {
+ _notificationChecks.remove(NotificationCheck.MESSAGE_AGE_ALERT);
+ }
+ else
+ {
+ _notificationChecks.add(NotificationCheck.MESSAGE_AGE_ALERT);
+ }
}
public void subscriberHasPendingResend(boolean hasContent, SubscriptionImpl subscription, QueueEntry entry)
@@ -950,4 +1000,25 @@ public class AMQQueue implements Managable, Comparable
return new QueueEntry(this, amqMessage);
}
+ public int compareTo(Object o)
+ {
+ return _name.compareTo(((AMQQueue) o).getName());
+ }
+
+
+ public void removeExpiredIfNoSubscribers() throws AMQException
+ {
+ synchronized(_subscribers.getChangeLock())
+ {
+ if(_subscribers.isEmpty())
+ {
+ _deliveryMgr.removeExpired();
+ }
+ }
+ }
+
+ public final Set<NotificationCheck> getNotificationChecks()
+ {
+ return _notificationChecks;
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
index 9e32de3f76..348a136f9d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
@@ -54,10 +54,7 @@ import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
/**
* AMQQueueMBean is the management bean for an {@link AMQQueue}.
@@ -97,6 +94,9 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que
private final long[] _lastNotificationTimes = new long[NotificationCheck.values().length];
private Notification _lastNotification = null;
+
+
+
@MBeanConstructor("Creates an MBean exposing an AMQQueue")
public AMQQueueMBean(AMQQueue queue) throws JMException
{
@@ -249,16 +249,21 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que
public void checkForNotification(AMQMessage msg) throws AMQException, JMException
{
- final long currentTime = System.currentTimeMillis();
- final long thresholdTime = currentTime - _queue.getMinimumAlertRepeatGap();
+ final Set<NotificationCheck> notificationChecks = _queue.getNotificationChecks();
- for (NotificationCheck check : NotificationCheck.values())
+ if(!notificationChecks.isEmpty())
{
- if (check.isMessageSpecific() || (_lastNotificationTimes[check.ordinal()] < thresholdTime))
+ final long currentTime = System.currentTimeMillis();
+ final long thresholdTime = currentTime - _queue.getMinimumAlertRepeatGap();
+
+ for (NotificationCheck check : notificationChecks)
{
- if (check.notifyIfNecessary(msg, _queue, this))
+ if (check.isMessageSpecific() || (_lastNotificationTimes[check.ordinal()] < thresholdTime))
{
- _lastNotificationTimes[check.ordinal()] = currentTime;
+ if (check.notifyIfNecessary(msg, _queue, this))
+ {
+ _lastNotificationTimes[check.ordinal()] = currentTime;
+ }
}
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java
index 3cf2cb9b12..7dfcae95c3 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java
@@ -212,6 +212,30 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
}
}
+ /**
+ * NOTE : This method should only be called when there are no active subscribers
+ */
+ public void removeExpired() throws AMQException
+ {
+ _lock.lock();
+
+
+ for(Iterator<QueueEntry> iter = _messages.iterator(); iter.hasNext();)
+ {
+ QueueEntry entry = iter.next();
+ if(entry.expired())
+ {
+ // fixme: Currently we have to update the total byte size here for the data in the queue
+ _totalMessageSize.addAndGet(-entry.getSize());
+ _queue.dequeue(_reapingStoreContext,entry);
+ iter.remove();
+ }
+ }
+
+
+ _lock.unlock();
+ }
+
/** @return the state of the async processor. */
public boolean isProcessingAsync()
{
@@ -278,9 +302,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
public void populatePreDeliveryQueue(Subscription subscription)
{
- if (_log.isTraceEnabled())
+ if (_log.isDebugEnabled())
{
- _log.trace("Populating PreDeliveryQueue for Subscription(" + System.identityHashCode(subscription) + ")");
+ _log.debug("Populating PreDeliveryQueue for Subscription(" + System.identityHashCode(subscription) + ")");
}
Iterator<QueueEntry> currentQueue = _messages.iterator();
@@ -289,13 +313,11 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
{
QueueEntry entry = currentQueue.next();
- if (!entry.getDeliveredToConsumer())
+ if (subscription.hasInterest(entry))
{
- if (subscription.hasInterest(entry))
- {
- subscription.enqueueForPreDelivery(entry, false);
- }
+ subscription.enqueueForPreDelivery(entry, false);
}
+
}
}
@@ -339,8 +361,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
protocolSession.getProtocolOutputConverter().writeGetOk(entry.getMessage(), channel.getChannelId(),
deliveryTag, _queue.getMessageCount());
- _totalMessageSize.addAndGet(-entry.getSize());
+
}
+ _totalMessageSize.addAndGet(-entry.getSize());
if (!acks)
{
@@ -484,9 +507,6 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
while (purgeMessage(entry, sub, purgeOnly))
{
AMQMessage message = entry.getMessage();
- // if we are purging then ensure we mark this message taken for the current subscriber
- // the current subscriber may be null in the case of a get or a purge but this is ok.
-// boolean alreadyTaken = message.taken(_queue, sub);
//remove the already taken message or expired
QueueEntry removed = messages.poll();
@@ -494,7 +514,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
assert removed == entry;
// if the message expired then the _totalMessageSize needs adjusting
- if (message.expired(_queue) && !entry.getDeliveredToConsumer())
+ if (message.expired(_queue) && !entry.taken(sub))
{
_totalMessageSize.addAndGet(-entry.getSize());
@@ -512,9 +532,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
//else the clean up is not required as the message has already been taken for this queue therefore
// it was the responsibility of the code that took the message to ensure the _totalMessageSize was updated.
- if (_log.isTraceEnabled())
+ if (_log.isDebugEnabled())
{
- _log.trace("Removed taken message:" + message.debugIdentity());
+ _log.debug("Removed taken message:" + message.debugIdentity());
}
// try the next message
@@ -610,9 +630,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
Queue<QueueEntry> messageQueue = sub.getNextQueue(_messages);
- if (_log.isTraceEnabled())
+ if (_log.isDebugEnabled())
{
- _log.trace(debugIdentity() + "Async sendNextMessage for sub (" + System.identityHashCode(sub) +
+ _log.debug(debugIdentity() + "Async sendNextMessage for sub (" + System.identityHashCode(sub) +
") from queue (" + System.identityHashCode(messageQueue) +
") AMQQueue (" + System.identityHashCode(queue) + ")");
}
@@ -638,9 +658,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
// message will be null if we have no messages in the messageQueue.
if (entry == null)
{
- if (_log.isTraceEnabled())
+ if (_log.isDebugEnabled())
{
- _log.trace(debugIdentity() + "No messages for Subscriber(" + System.identityHashCode(sub) + ") from queue; (" + System.identityHashCode(messageQueue) + ")");
+ _log.debug(debugIdentity() + "No messages for Subscriber(" + System.identityHashCode(sub) + ") from queue; (" + System.identityHashCode(messageQueue) + ")");
}
return;
}
@@ -680,9 +700,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
if (messageQueue == sub.getResendQueue())
{
- if (_log.isTraceEnabled())
+ if (_log.isDebugEnabled())
{
- _log.trace(debugIdentity() + "All messages sent from resendQueue for " + sub);
+ _log.debug(debugIdentity() + "All messages sent from resendQueue for " + sub);
}
if (messageQueue.isEmpty())
{
@@ -842,17 +862,6 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
for (Subscription sub : _subscriptions.getSubscriptions())
{
- // stop if the message gets delivered whilst PreDelivering if we have a shared queue.
- if (_queue.isShared() && entry.getDeliveredToConsumer())
- {
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + "Stopping PreDelivery as message(" + System.identityHashCode(entry) +
- ") is already delivered.");
- }
- continue;
- }
-
// Only give the message to those that want them.
if (sub.hasInterest(entry))
{
@@ -894,9 +903,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
{
if (!s.isSuspended())
{
- if (_log.isTraceEnabled())
+ if (debugEnabled)
{
- _log.trace(debugIdentity() + "Delivering Message:" + entry.getMessage().debugIdentity() + " to(" +
+ _log.debug(debugIdentity() + "Delivering Message:" + entry.getMessage().debugIdentity() + " to(" +
System.identityHashCode(s) + ") :" + s);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java
index f7f35a9319..1568f58e2e 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java
@@ -97,4 +97,6 @@ interface DeliveryManager
long getOldestMessageArrival();
void subscriberHasPendingResend(boolean hasContent, Subscription subscription, QueueEntry msg);
+
+ void removeExpired() throws AMQException;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
index 60c1a8f574..e6377b33da 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
@@ -51,7 +51,7 @@ class ExchangeBindings
ExchangeBinding(AMQShortString routingKey, Exchange exchange, FieldTable arguments)
{
- _routingKey = routingKey;
+ _routingKey = routingKey == null ? AMQShortString.EMPTY_STRING : routingKey;
_exchange = exchange;
_arguments = arguments == null ? EMPTY_ARGUMENTS : arguments;
}
@@ -74,8 +74,7 @@ class ExchangeBindings
public int hashCode()
{
return (_exchange == null ? 0 : _exchange.hashCode())
- + (_routingKey == null ? 0 : _routingKey.hashCode())
- + (_arguments == null ? 0 : _arguments.hashCode());
+ + (_routingKey == null ? 0 : _routingKey.hashCode());
}
public boolean equals(Object o)
@@ -86,8 +85,7 @@ class ExchangeBindings
}
ExchangeBinding eb = (ExchangeBinding) o;
return _exchange.equals(eb._exchange)
- && _routingKey.equals(eb._routingKey)
- && _arguments.equals(eb._arguments);
+ && _routingKey.equals(eb._routingKey);
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java
index 6b3d65661f..6f9efd3200 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java
@@ -29,9 +29,9 @@ public enum NotificationCheck
{
boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
{
- int msgCount = queue.getMessageCount();
+ int msgCount;
final long maximumMessageCount = queue.getMaximumMessageCount();
- if (maximumMessageCount!= 0 && msgCount >= maximumMessageCount)
+ if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount)
{
listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached.");
return true;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java
index a706098b71..96ce6743ec 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java
@@ -45,8 +45,6 @@ public interface Subscription
void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst);
- boolean isAutoClose();
-
void close();
boolean isClosed();
@@ -60,4 +58,6 @@ public interface Subscription
Object getSendLock();
AMQChannel getChannel();
+
+ void start();
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java
index e631481cc8..bde3ad8ec9 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java
@@ -292,14 +292,17 @@ public class SubscriptionImpl implements Subscription
queue.dequeue(storeContext, entry);
}
+/*
+ if (_sendLock.get())
+ {
+ _logger.error("Sending " + entry + " when subscriber(" + this + ") is closed!");
+ }
+*/
+
synchronized (channel)
{
long deliveryTag = channel.getNextDeliveryTag();
- if (_sendLock.get())
- {
- _logger.error("Sending " + entry + " when subscriber(" + this + ") is closed!");
- }
if (_acks)
{
@@ -308,10 +311,11 @@ public class SubscriptionImpl implements Subscription
protocolSession.getProtocolOutputConverter().writeDeliver(entry.getMessage(), channel.getChannelId(), deliveryTag, consumerTag);
- if (!_acks)
- {
- entry.getMessage().decrementReference(storeContext);
- }
+
+ }
+ if (!_acks)
+ {
+ entry.getMessage().decrementReference(storeContext);
}
}
finally
@@ -367,59 +371,60 @@ public class SubscriptionImpl implements Subscription
// return false;
}
- final AMQProtocolSession publisher = entry.getMessage().getPublisher();
+
//todo - client id should be recoreded and this test removed but handled below
- if (_noLocal && publisher != null)
+ if (_noLocal)
{
- // We don't want local messages so check to see if message is one we sent
- Object localInstance;
- Object msgInstance;
- if ((protocolSession.getClientProperties() != null) &&
- (localInstance = protocolSession.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
+ final AMQProtocolSession publisher = entry.getMessage().getPublisher();
+ if(publisher != null)
+
{
+ // We don't want local messages so check to see if message is one we sent
+ Object localInstance;
+ Object msgInstance;
- if ((publisher.getClientProperties() != null) &&
- (msgInstance = publisher.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
+ if ((protocolSession.getClientProperties() != null) &&
+ (localInstance = protocolSession.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
{
- if (localInstance == msgInstance || localInstance.equals(msgInstance))
+
+ if ((publisher.getClientProperties() != null) &&
+ (msgInstance = publisher.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
{
-// if (_logger.isTraceEnabled())
-// {
-// _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
-// msg.debugIdentity() + ")");
-// }
- return false;
+ if (localInstance == msgInstance || localInstance.equals(msgInstance))
+ {
+ // if (_logger.isTraceEnabled())
+ // {
+ // _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
+ // msg.debugIdentity() + ")");
+ // }
+ return false;
+ }
}
}
- }
- else
- {
+ else
+ {
- localInstance = protocolSession.getClientIdentifier();
- //todo - client id should be recoreded and this test removed but handled here
+ localInstance = protocolSession.getClientIdentifier();
+ //todo - client id should be recoreded and this test removed but handled here
- msgInstance = publisher.getClientIdentifier();
- if (localInstance == msgInstance || ((localInstance != null) && localInstance.equals(msgInstance)))
- {
-// if (_logger.isTraceEnabled())
-// {
-// _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
-// msg.debugIdentity() + ")");
-// }
- return false;
+ msgInstance = publisher.getClientIdentifier();
+ if (localInstance == msgInstance || ((localInstance != null) && localInstance.equals(msgInstance)))
+ {
+ // if (_logger.isTraceEnabled())
+ // {
+ // _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
+ // msg.debugIdentity() + ")");
+ // }
+ return false;
+ }
}
- }
-
+ }
}
- if (_logger.isTraceEnabled())
- {
- _logger.trace("(" + debugIdentity() + ") checking filters for message (" + entry.debugIdentity());
- }
return checkFilters(entry);
}
@@ -433,23 +438,7 @@ public class SubscriptionImpl implements Subscription
private boolean checkFilters(QueueEntry msg)
{
- if (_filters != null)
- {
-// if (_logger.isTraceEnabled())
-// {
-// _logger.trace("(" + debugIdentity() + ") has filters.");
-// }
- return _filters.allAllow(msg.getMessage());
- }
- else
- {
-// if (_logger.isTraceEnabled())
-// {
-// _logger.trace("(" + debugIdentity() + ") has no filters");
-// }
-
- return true;
- }
+ return (_filters == null) || _filters.allAllow(msg.getMessage());
}
public Queue<QueueEntry> getPreDeliveryQueue()
@@ -472,7 +461,7 @@ public class SubscriptionImpl implements Subscription
}
}
- public boolean isAutoClose()
+ private boolean isAutoClose()
{
return _autoClose;
}
@@ -534,19 +523,24 @@ public class SubscriptionImpl implements Subscription
{
_logger.info("Closing autoclose subscription (" + debugIdentity() + "):" + this);
- ProtocolOutputConverter converter = protocolSession.getProtocolOutputConverter();
- converter.confirmConsumerAutoClose(channel.getChannelId(), consumerTag);
- _sentClose = true;
-
- //fixme JIRA do this better
+ boolean unregisteredOK = false;
try
{
- channel.unsubscribeConsumer(protocolSession, consumerTag);
+ unregisteredOK = channel.unsubscribeConsumer(protocolSession, consumerTag);
}
catch (AMQException e)
{
// Occurs if we cannot find the subscriber in the channel with protocolSession and consumerTag.
+ _logger.info("Unable to UnsubscribeConsumer :" + consumerTag +" so not going to send CancelOK.");
+ }
+
+ if (unregisteredOK)
+ {
+ ProtocolOutputConverter converter = protocolSession.getProtocolOutputConverter();
+ converter.confirmConsumerAutoClose(channel.getChannelId(), consumerTag);
+ _sentClose = true;
}
+
}
}
@@ -563,9 +557,9 @@ public class SubscriptionImpl implements Subscription
{
QueueEntry resent = _resendQueue.poll();
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Removed for resending:" + resent.debugIdentity());
+ _logger.debug("Removed for resending:" + resent.debugIdentity());
}
resent.release();
@@ -613,7 +607,7 @@ public class SubscriptionImpl implements Subscription
public boolean wouldSuspend(QueueEntry msg)
{
- return channel.wouldSuspend(msg.getMessage());
+ return _acks && channel.wouldSuspend(msg.getMessage());
}
public Queue<QueueEntry> getResendQueue()
@@ -677,4 +671,19 @@ public class SubscriptionImpl implements Subscription
return channel;
}
+ public void start()
+ {
+ //Check to see if we need to autoclose
+ if (filtersMessages())
+ {
+ if (isAutoClose())
+ {
+ if (_messages.isEmpty())
+ {
+ autoclose();
+ }
+ }
+ }
+ }
+
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java
index b73b8d7e07..882efd380d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java
@@ -37,7 +37,9 @@ class SubscriptionSet implements WeightedSubscriptionManager
/** Used to control the round robin delivery of content */
private int _currentSubscriber;
- private final Object _subscriptionsChange = new Object();
+
+ private final Object _changeLock = new Object();
+ private volatile boolean _exclusive;
/** Accessor for unit tests. */
@@ -48,7 +50,7 @@ class SubscriptionSet implements WeightedSubscriptionManager
public void addSubscriber(Subscription subscription)
{
- synchronized (_subscriptionsChange)
+ synchronized (_changeLock)
{
_subscriptions.add(subscription);
}
@@ -66,7 +68,7 @@ class SubscriptionSet implements WeightedSubscriptionManager
// TODO: possibly need O(1) operation here.
Subscription sub = null;
- synchronized (_subscriptionsChange)
+ synchronized (_changeLock)
{
int subIndex = _subscriptions.indexOf(subscription);
@@ -115,10 +117,7 @@ class SubscriptionSet implements WeightedSubscriptionManager
*/
public Subscription nextSubscriber(QueueEntry msg)
{
- if (_subscriptions.isEmpty())
- {
- return null;
- }
+
try
{
@@ -142,30 +141,64 @@ class SubscriptionSet implements WeightedSubscriptionManager
private Subscription nextSubscriberImpl(QueueEntry msg)
{
- final ListIterator<Subscription> iterator = _subscriptions.listIterator(_currentSubscriber);
- while (iterator.hasNext())
+ if(_exclusive)
{
- Subscription subscription = iterator.next();
- ++_currentSubscriber;
- subscriberScanned();
-
- if (!(subscription.isSuspended() || subscription.wouldSuspend(msg)))
+ try
{
- if (subscription.hasInterest(msg))
+ Subscription subscription = _subscriptions.get(0);
+ subscriberScanned();
+
+ if (!(subscription.isSuspended() || subscription.wouldSuspend(msg)))
{
- // if the queue is not empty then this client is ready to receive a message.
- //FIXME the queue could be full of sent messages.
- // Either need to clean all PDQs after sending a message
- // OR have a clean up thread that runs the PDQs expunging the messages.
- if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty())
+ if (subscription.hasInterest(msg))
{
- return subscription;
+ // if the queue is not empty then this client is ready to receive a message.
+ //FIXME the queue could be full of sent messages.
+ // Either need to clean all PDQs after sending a message
+ // OR have a clean up thread that runs the PDQs expunging the messages.
+ if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty())
+ {
+ return subscription;
+ }
}
}
}
+ catch(IndexOutOfBoundsException e)
+ {
+ }
+ return null;
}
+ else
+ {
+ if (_subscriptions.isEmpty())
+ {
+ return null;
+ }
+ final ListIterator<Subscription> iterator = _subscriptions.listIterator(_currentSubscriber);
+ while (iterator.hasNext())
+ {
+ Subscription subscription = iterator.next();
+ ++_currentSubscriber;
+ subscriberScanned();
- return null;
+ if (!(subscription.isSuspended() || subscription.wouldSuspend(msg)))
+ {
+ if (subscription.hasInterest(msg))
+ {
+ // if the queue is not empty then this client is ready to receive a message.
+ //FIXME the queue could be full of sent messages.
+ // Either need to clean all PDQs after sending a message
+ // OR have a clean up thread that runs the PDQs expunging the messages.
+ if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty())
+ {
+ return subscription;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
}
/** Overridden in test classes. */
@@ -226,4 +259,16 @@ class SubscriptionSet implements WeightedSubscriptionManager
{
return _subscriptions.size();
}
+
+
+ public Object getChangeLock()
+ {
+ return _changeLock;
+ }
+
+ public void setExclusive(final boolean exclusive)
+ {
+ _exclusive = exclusive;
+ }
+
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java b/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java
index 79ee6b93a3..9b91c71a1d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java
@@ -22,6 +22,8 @@ package org.apache.qpid.server.queue;
import java.util.LinkedList;
import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
@@ -60,7 +62,7 @@ public class TransientMessageData
* delivered. It is <b>cleared after delivery has been attempted</b>. Any persistent record of destinations is done
* by the message handle.
*/
- private List<AMQQueue> _destinationQueues = new LinkedList<AMQQueue>();
+ private List<AMQQueue> _destinationQueues;
public MessagePublishInfo getMessagePublishInfo()
{
@@ -74,7 +76,7 @@ public class TransientMessageData
public List<AMQQueue> getDestinationQueues()
{
- return _destinationQueues;
+ return _destinationQueues == null ? (List<AMQQueue>) Collections.EMPTY_LIST : _destinationQueues;
}
public void setDestinationQueues(List<AMQQueue> destinationQueues)
@@ -109,6 +111,10 @@ public class TransientMessageData
public void addDestinationQueue(AMQQueue queue)
{
+ if(_destinationQueues == null)
+ {
+ _destinationQueues = new ArrayList<AMQQueue>();
+ }
_destinationQueues.add(queue);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java
index 42c32dcf00..fef958000a 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java
@@ -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
@@ -39,8 +39,8 @@ import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
import org.apache.qpid.server.security.auth.database.ConfigurationFilePrincipalDatabaseManager;
import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager;
import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager;
-import org.apache.qpid.server.security.access.AccessManager;
-import org.apache.qpid.server.security.access.AccessManagerImpl;
+import org.apache.qpid.server.security.access.ACLPlugin;
+import org.apache.qpid.server.security.access.ACLManager;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
import org.apache.qpid.AMQException;
@@ -52,15 +52,12 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry
private AuthenticationManager _authenticationManager;
- private AccessManager _accessManager;
+ private ACLPlugin _accessManager;
private PrincipalDatabaseManager _databaseManager;
private VirtualHostRegistry _virtualHostRegistry;
-
- private final Map<String, VirtualHost> _virtualHosts = new ConcurrentHashMap<String, VirtualHost>();
-
private PluginManager _pluginManager;
@@ -110,9 +107,9 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry
_virtualHostRegistry = new VirtualHostRegistry();
- _accessManager = new AccessManagerImpl("default", _configuration);
+ _accessManager = ACLManager.loadACLManager("default", _configuration);
- _databaseManager = new ConfigurationFilePrincipalDatabaseManager();
+ _databaseManager = new ConfigurationFilePrincipalDatabaseManager(_configuration);
_authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null);
@@ -121,7 +118,7 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry
_managedObjectRegistry.start();
_pluginManager = new PluginManager(_configuration.getString("plugin-directory"));
-
+
initialiseVirtualHosts();
}
@@ -154,7 +151,7 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry
return _virtualHostRegistry;
}
- public AccessManager getAccessManager()
+ public ACLPlugin getAccessManager()
{
return _accessManager;
}
@@ -178,7 +175,7 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry
{
return getConfiguration().getList("virtualhosts.virtualhost.name");
}
-
+
public PluginManager getPluginManager()
{
return _pluginManager;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java
index 6aac21a161..ca10fbdba2 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java
@@ -27,7 +27,7 @@ import org.apache.qpid.server.management.ManagedObjectRegistry;
import org.apache.qpid.server.plugins.PluginManager;
import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager;
-import org.apache.qpid.server.security.access.AccessManager;
+import org.apache.qpid.server.security.access.ACLPlugin;
import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
public interface IApplicationRegistry
@@ -68,8 +68,8 @@ public interface IApplicationRegistry
VirtualHostRegistry getVirtualHostRegistry();
- AccessManager getAccessManager();
-
+ ACLPlugin getAccessManager();
+
PluginManager getPluginManager();
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java
index 35d036d20f..539f32a732 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java
@@ -23,33 +23,35 @@ package org.apache.qpid.server.security.access;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+import org.apache.qpid.server.security.access.plugins.DenyAll;
import org.apache.qpid.configuration.PropertyUtils;
import org.apache.log4j.Logger;
import java.util.List;
import java.lang.reflect.Method;
-import java.security.Principal;
-public class AccessManagerImpl implements AccessManager
+public class ACLManager
{
- private static final Logger _logger = Logger.getLogger(AccessManagerImpl.class);
+ private static final Logger _logger = Logger.getLogger(ACLManager.class);
- AccessManager _accessManager;
-
- public AccessManagerImpl(String name, Configuration hostConfig) throws ConfigurationException
+ public static ACLPlugin loadACLManager(String name, Configuration hostConfig) throws ConfigurationException
{
+ ACLPlugin aclPlugin = ApplicationRegistry.getInstance().getAccessManager();
+
if (hostConfig == null)
{
- _logger.warn("No Configuration specified. Using default access controls for VirtualHost:'" + name + "'");
- return;
+ _logger.warn("No Configuration specified. Using default ACLPlugin '" + aclPlugin.getPluginName()
+ + "' for VirtualHost:'" + name + "'");
+ return aclPlugin;
}
String accessClass = hostConfig.getString("security.access.class");
if (accessClass == null)
{
- _logger.warn("No access control specified. Using default access controls for VirtualHost:'" + name + "'");
- return;
+
+ _logger.warn("No ACL Plugin specified. Using default ACL Plugin '" + aclPlugin.getPluginName() +
+ "' for VirtualHost:'" + name + "'");
+ return aclPlugin;
}
Object o;
@@ -59,26 +61,35 @@ public class AccessManagerImpl implements AccessManager
}
catch (Exception e)
{
- throw new ConfigurationException("Error initialising access control: " + e, e);
+ throw new ConfigurationException("Error initialising ACL: " + e, e);
}
- if (!(o instanceof AccessManager))
+ if (!(o instanceof ACLPlugin))
{
- throw new ConfigurationException("Access control must implement the VirtualHostAccess interface");
+ throw new ConfigurationException("ACL Plugins must implement the ACLPlugin interface");
}
- initialiseAccessControl((AccessManager) o, hostConfig);
+ initialiseAccessControl((ACLPlugin) o, hostConfig);
- _accessManager = (AccessManager) o;
-
- _logger.info("Initialised access control for virtualhost '" + name + "' successfully");
+ aclPlugin = getManager((ACLPlugin) o);
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Initialised ACL Plugin '" + aclPlugin.getPluginName()
+ + "' for virtualhost '" + name + "' successfully");
+ }
+ return aclPlugin;
}
- private void initialiseAccessControl(AccessManager accessManager, Configuration config)
+ private static void initialiseAccessControl(ACLPlugin accessManager, Configuration config)
throws ConfigurationException
{
+ //First provide the ACLPlugin with the host configuration
+
+ accessManager.setConfiguaration(config);
+
+ //Provide additional attribute customisation.
String baseName = "security.access.attributes.attribute.";
List<String> argumentNames = config.getList(baseName + "name");
List<String> argumentValues = config.getList(baseName + "value");
@@ -123,33 +134,28 @@ public class AccessManagerImpl implements AccessManager
}
}
- public AccessResult isAuthorized(Accessable accessObject, String username)
- {
- return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ);
- }
- public AccessResult isAuthorized(Accessable accessObject, Principal user, AccessRights.Rights rights)
+ private static ACLPlugin getManager(ACLPlugin manager)
{
- if (_accessManager == null)
+ if (manager == null)
{
- if (ApplicationRegistry.getInstance().getAccessManager() == this)
+ if (ApplicationRegistry.getInstance().getAccessManager() == null)
{
- _logger.warn("No Default access manager specified DENYING ALL ACCESS");
- return new AccessResult(this, AccessResult.AccessStatus.REFUSED);
+ return new DenyAll();
}
else
{
- return ApplicationRegistry.getInstance().getAccessManager().isAuthorized(accessObject, user, rights);
+ return ApplicationRegistry.getInstance().getAccessManager();
}
}
else
{
- return _accessManager.isAuthorized(accessObject, user, rights);
+ return manager;
}
}
- public String getName()
+ public static Logger getLogger()
{
- return "AccessManagerImpl";
+ return _logger;
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java
new file mode 100644
index 0000000000..7855f147b4
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.access;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.AMQConnectionException;
+import org.apache.commons.configuration.Configuration;
+
+
+public interface ACLPlugin
+{
+ /**
+ * Pseudo-Code:
+ * Identify requested RighConnectiont
+ * Lookup users ability for that right.
+ * if rightsExists
+ * Validate right on object
+ * Return result
+ * e.g
+ * User, CONSUME , Queue
+ * User, CONSUME , Exchange + RoutingKey
+ * User, PUBLISH , Exchange + RoutingKey
+ * User, CREATE , Exchange || Queue
+ * User, BIND , Exchange + RoutingKey + Queue
+ *
+ * @param session - The session requesting access
+ * @param permission - The permission requested
+ * @param parameters - The above objects that are used to authorise the request.
+ * @return The AccessResult decision
+ */
+ //todo potential refactor this ConnectionException Out of here
+ AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) throws AMQConnectionException;
+
+ String getPluginName();
+
+ void setConfiguaration(Configuration config);
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java
index b8d8fc605a..89cead69b3 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java
@@ -30,15 +30,15 @@ public class AccessResult
StringBuilder _authorizer;
AccessStatus _status;
- public AccessResult(AccessManager authorizer, AccessStatus status)
+ public AccessResult(ACLPlugin authorizer, AccessStatus status)
{
_status = status;
- _authorizer = new StringBuilder(authorizer.getName());
+ _authorizer = new StringBuilder(authorizer.getPluginName());
}
- public void setAuthorizer(AccessManager authorizer)
+ public void setAuthorizer(ACLPlugin authorizer)
{
- _authorizer.append(authorizer.getName());
+ _authorizer.append(authorizer.getPluginName());
}
public String getAuthorizer()
@@ -56,10 +56,10 @@ public class AccessResult
return _status;
}
- public void addAuthorizer(AccessManager accessManager)
+ public void addAuthorizer(ACLPlugin accessManager)
{
_authorizer.insert(0, "->");
- _authorizer.insert(0, accessManager.getName());
+ _authorizer.insert(0, accessManager.getPluginName());
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java
deleted file mode 100644
index 1ddca3a64e..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-package org.apache.qpid.server.security.access;
-
-import java.security.Principal;
-
-public class AllowAll implements AccessManager
-{
-
- public AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights)
- {
- return new AccessResult(this, AccessResult.AccessStatus.GRANTED);
- }
-
- public AccessResult isAuthorized(Accessable accessObject, String username)
- {
- return new AccessResult(this, AccessResult.AccessStatus.GRANTED);
- }
-
- public String getName()
- {
- return "AllowAll";
- }
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java
deleted file mode 100644
index bf40eeba4e..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-package org.apache.qpid.server.security.access;
-
-import java.security.Principal;
-
-public class DenyAll implements AccessManager
-{
- public AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights)
- {
- return new AccessResult(this, AccessResult.AccessStatus.REFUSED);
- }
-
- public AccessResult isAuthorized(Accessable accessObject, String username)
- {
- return new AccessResult(this, AccessResult.AccessStatus.REFUSED);
- }
-
- public String getName()
- {
- return "DenyAll";
- }
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java
deleted file mode 100644
index 291bc714ed..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-package org.apache.qpid.server.security.access;
-
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
-import org.apache.log4j.Logger;
-
-import java.io.IOException;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-import java.io.File;
-import java.util.regex.Pattern;
-import java.security.Principal;
-
-/**
- * Represents a user database where the account information is stored in a simple flat file.
- *
- * The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn
- *
- * where a carriage return separates each username/password pair. Passwords are assumed to be in plain text.
- */
-public class FileAccessManager implements AccessManager
-{
- private static final Logger _logger = Logger.getLogger(FileAccessManager.class);
-
- protected File _accessFile;
-
- protected Pattern _regexp = Pattern.compile(":");
-
- private static final short USER_INDEX = 0;
- private static final short VIRTUALHOST_INDEX = 1;
-
- public void setAccessFile(String accessFile) throws FileNotFoundException
- {
- File f = new File(accessFile);
- _logger.info("FileAccessManager using file " + f.getAbsolutePath());
- _accessFile = f;
- if (!f.exists())
- {
- throw new FileNotFoundException("Cannot find access file " + f);
- }
- if (!f.canRead())
- {
- throw new FileNotFoundException("Cannot read access file " + f +
- ". Check permissions.");
- }
- }
-
- /**
- * Looks up the virtual hosts for a specified user in the access file.
- *
- * @param user The user to lookup
- *
- * @return a list of virtualhosts
- */
- private VirtualHostAccess[] lookupVirtualHost(String user)
- {
- String[] results = lookup(user, VIRTUALHOST_INDEX);
- VirtualHostAccess vhosts[] = new VirtualHostAccess[results.length];
-
- for (int index = 0; index < results.length; index++)
- {
- vhosts[index] = new VirtualHostAccess(results[index]);
- }
-
- return vhosts;
- }
-
-
- private String[] lookup(String user, int index)
- {
- try
- {
- BufferedReader reader = null;
- try
- {
- reader = new BufferedReader(new FileReader(_accessFile));
- String line;
-
- while ((line = reader.readLine()) != null)
- {
- String[] result = _regexp.split(line);
- if (result == null || result.length < (index + 1))
- {
- continue;
- }
-
- if (user.equals(result[USER_INDEX]))
- {
- return result[index].split(",");
- }
- }
- return null;
- }
- finally
- {
- if (reader != null)
- {
- reader.close();
- }
- }
- }
- catch (IOException ioe)
- {
- //ignore
- }
- return null;
- }
-
- public AccessResult isAuthorized(Accessable accessObject, String username)
- {
- return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ);
- }
-
- public AccessResult isAuthorized(Accessable accessObject, Principal user, AccessRights.Rights rights)
- {
- if (accessObject instanceof VirtualHost)
- {
- VirtualHostAccess[] hosts = lookupVirtualHost(user.getName());
-
- if (hosts != null)
- {
- for (VirtualHostAccess host : hosts)
- {
- if (accessObject.getAccessableName().equals(host.getVirtualHost()))
- {
- if (host.getAccessRights().allows(rights))
- {
- return new AccessResult(this, AccessResult.AccessStatus.GRANTED);
- }
- else
- {
- return new AccessResult(this, AccessResult.AccessStatus.REFUSED);
- }
- }
- }
- }
- }
-// else if (accessObject instanceof AMQQueue)
-// {
-// String[] queues = lookupQueue(username, ((AMQQueue) accessObject).getVirtualHost());
-//
-// if (queues != null)
-// {
-// for (String queue : queues)
-// {
-// if (accessObject.getAccessableName().equals(queue))
-// {
-// return new AccessResult(this, AccessResult.AccessStatus.GRANTED);
-// }
-// }
-// }
-// }
-
- return new AccessResult(this, AccessResult.AccessStatus.REFUSED);
- }
-
- public String getName()
- {
- return "FileAccessManager";
- }
-
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java
index d70a6dc8f4..5d439a99eb 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java
@@ -1,34 +1,37 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-package org.apache.qpid.server.security.access;
-
-import java.security.Principal;
-
-public interface AccessManager
-{
- AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights);
-
- @Deprecated
- AccessResult isAuthorized(Accessable accessObject, String username);
-
- String getName();
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.access;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+
+public enum Permission
+{
+ CONSUME,
+ PUBLISH,
+ CREATE,
+ ACCESS,
+ BIND,
+ UNBIND,
+ DELETE,
+ PURGE
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java
deleted file mode 100644
index 6ccadb2e7d..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-package org.apache.qpid.server.security.access;
-
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
-import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
-import org.apache.log4j.Logger;
-
-import java.security.Principal;
-
-public class PrincipalDatabaseAccessManager implements AccessManager
-{
- private static final Logger _logger = Logger.getLogger(PrincipalDatabaseAccessManager.class);
-
- PrincipalDatabase _database;
- AccessManager _default;
-
- public PrincipalDatabaseAccessManager()
- {
- _default = null;
- }
-
- public void setDefaultAccessManager(String defaultAM)
- {
- if (defaultAM.equals("AllowAll"))
- {
- _default = new AllowAll();
- }
-
- if (defaultAM.equals("DenyAll"))
- {
- _default = new DenyAll();
- }
- }
-
- public void setPrincipalDatabase(String database)
- {
- _database = ApplicationRegistry.getInstance().getDatabaseManager().getDatabases().get(database);
- if (!(_database instanceof AccessManager))
- {
- _logger.warn("Database '" + database + "' cannot perform access management");
- }
- }
-
-
- public AccessResult isAuthorized(Accessable accessObject, String username)
- {
- return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ);
- }
-
- public AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights)
- {
- AccessResult result;
-
- if (_database == null)
- {
- if (_default != null)
- {
- result = _default.isAuthorized(accessObject, username, rights);
- }
- else
- {
- throw new RuntimeException("Principal Database and default Access Manager are both null unable to perform Access Control");
- }
- }
- else
- {
- if (!(_database instanceof AccessManager))
- {
- _logger.warn("Specified PrincipalDatabase is not an AccessManager so using default AccessManager");
- result = _default.isAuthorized(accessObject, username, rights);
- }
- else
- {
- result = ((AccessManager) _database).isAuthorized(accessObject, username, rights);
- }
- }
-
- result.addAuthorizer(this);
-
- return result;
- }
-
- public String getName()
- {
- return "PrincipalDatabaseFileAccessManager";
- }
-
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java
new file mode 100755
index 0000000000..23073e0613
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java
@@ -0,0 +1,579 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.security.access;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.QueueBindBody;
+import org.apache.qpid.framing.QueueDeclareBody;
+import org.apache.qpid.framing.ExchangeDeclareBody;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.exchange.Exchange;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class PrincipalPermissions
+{
+
+ private static final Object CONSUME_QUEUES_KEY = new Object();
+ private static final Object CONSUME_TEMPORARY_KEY = new Object();
+ private static final Object CONSUME_OWN_QUEUES_ONLY_KEY = new Object();
+
+ private static final Object CREATE_QUEUES_KEY = new Object();
+ private static final Object CREATE_EXCHANGES_KEY = new Object();
+
+ private static final Object CREATE_QUEUE_TEMPORARY_KEY = new Object();
+ private static final Object CREATE_QUEUE_QUEUES_KEY = new Object();
+ private static final Object CREATE_QUEUE_EXCHANGES_KEY = new Object();
+
+ private static final Object CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY = new Object();
+ private static final Object CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY = new Object();
+
+ private static final int PUBLISH_EXCHANGES_KEY = 0;
+
+ private Map _permissions;
+
+ private String _user;
+
+
+ public PrincipalPermissions(String user)
+ {
+ _user = user;
+ _permissions = new ConcurrentHashMap();
+ }
+
+ public void grant(Permission permission, Object... parameters)
+ {
+ switch (permission)
+ {
+ case ACCESS:
+ break; // This is a no-op as the existence of this PrincipalPermission object is scoped per VHost for ACCESS
+ case BIND:
+ break; // All the details are currently included in the create setup.
+ case CONSUME: // Parameters : AMQShortString queueName, Boolean Temporary, Boolean ownQueueOnly
+ Map consumeRights = (Map) _permissions.get(permission);
+
+ if (consumeRights == null)
+ {
+ consumeRights = new ConcurrentHashMap();
+ _permissions.put(permission, consumeRights);
+ }
+
+ //if we have parametsre
+ if (parameters.length > 0)
+ {
+ AMQShortString queueName = (AMQShortString) parameters[0];
+ Boolean temporary = (Boolean) parameters[1];
+ Boolean ownQueueOnly = (Boolean) parameters[2];
+
+ if (temporary)
+ {
+ consumeRights.put(CONSUME_TEMPORARY_KEY, true);
+ }
+ else
+ {
+ consumeRights.put(CONSUME_TEMPORARY_KEY, false);
+ }
+
+ if (ownQueueOnly)
+ {
+ consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, true);
+ }
+ else
+ {
+ consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, false);
+ }
+
+
+ LinkedList queues = (LinkedList) consumeRights.get(CONSUME_QUEUES_KEY);
+ if (queues == null)
+ {
+ queues = new LinkedList();
+ consumeRights.put(CONSUME_QUEUES_KEY, queues);
+ }
+
+ if (queueName != null)
+ {
+ queues.add(queueName);
+ }
+ }
+
+
+ break;
+ case CREATE: // Parameters : Boolean temporary, AMQShortString queueName
+ // , AMQShortString exchangeName , AMQShortString routingKey
+ // || AMQShortString exchangeName , AMQShortString Class
+
+ Map createRights = (Map) _permissions.get(permission);
+
+ if (createRights == null)
+ {
+ createRights = new ConcurrentHashMap();
+ _permissions.put(permission, createRights);
+
+ }
+
+ //The existence of the empty map mean permission to all.
+ if (parameters.length == 0)
+ {
+ return;
+ }
+
+
+ if (parameters[0] instanceof Boolean) //Create Queue :
+ // Boolean temporary, [AMQShortString queueName, AMQShortString exchangeName , AMQShortString routingKey]
+ {
+ Boolean temporary = (Boolean) parameters[0];
+
+ AMQShortString queueName = parameters.length > 1 ? (AMQShortString) parameters[1] : null;
+ AMQShortString exchangeName = parameters.length > 2 ? (AMQShortString) parameters[2] : null;
+ //Set the routingkey to the specified value or the queueName if present
+ AMQShortString routingKey = parameters.length > 3 ? (AMQShortString) parameters[3] : queueName;
+
+ // Get the queues map
+ Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY);
+
+ if (create_queues == null)
+ {
+ create_queues = new ConcurrentHashMap();
+ createRights.put(CREATE_QUEUES_KEY, create_queues);
+ }
+
+ //Allow all temp queues to be created
+ create_queues.put(CREATE_QUEUE_TEMPORARY_KEY, temporary);
+
+ //Create empty list of queues
+ Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY);
+
+ if (create_queues_queues == null)
+ {
+ create_queues_queues = new ConcurrentHashMap();
+ create_queues.put(CREATE_QUEUE_QUEUES_KEY, create_queues_queues);
+ }
+
+ // We are granting CREATE rights to all temporary queues only
+ if (parameters.length == 1)
+ {
+ return;
+ }
+
+ // if we have a queueName then we need to store any associated exchange / rk bindings
+ if (queueName != null)
+ {
+ Map queue = (Map) create_queues_queues.get(queueName);
+ if (queue == null)
+ {
+ queue = new ConcurrentHashMap();
+ create_queues_queues.put(queueName, queue);
+ }
+
+ if (exchangeName != null)
+ {
+ queue.put(exchangeName, routingKey);
+ }
+
+ //If no exchange is specified then the presence of the queueName in the map says any exchange is ok
+ }
+
+ // Store the exchange that we are being granted rights to. This will be used as part of binding
+
+ //Lookup the list of exchanges
+ Map create_queues_exchanges = (Map) create_queues.get(CREATE_QUEUE_EXCHANGES_KEY);
+
+ if (create_queues_exchanges == null)
+ {
+ create_queues_exchanges = new ConcurrentHashMap();
+ create_queues.put(CREATE_QUEUE_EXCHANGES_KEY, create_queues_exchanges);
+ }
+
+ //if we have an exchange
+ if (exchangeName != null)
+ {
+ //Retrieve the list of permitted exchanges.
+ Map exchanges = (Map) create_queues_exchanges.get(exchangeName);
+
+ if (exchanges == null)
+ {
+ exchanges = new ConcurrentHashMap();
+ create_queues_exchanges.put(exchangeName, exchanges);
+ }
+
+ //Store the temporary setting CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY
+ exchanges.put(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY, temporary);
+
+ //Store the binding details of queue/rk for this exchange.
+ if (queueName != null)
+ {
+ //Retrieve the list of permitted routingKeys.
+ Map rKeys = (Map) exchanges.get(exchangeName);
+
+ if (rKeys == null)
+ {
+ rKeys = new ConcurrentHashMap();
+ exchanges.put(CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY, rKeys);
+ }
+
+ rKeys.put(queueName, routingKey);
+ }
+ }
+ }
+ else // Create Exchange : AMQShortString exchangeName , AMQShortString Class
+ {
+ Map create_exchanges = (Map) createRights.get(CREATE_EXCHANGES_KEY);
+
+ if (create_exchanges == null)
+ {
+ create_exchanges = new ConcurrentHashMap();
+ createRights.put(CREATE_EXCHANGES_KEY, create_exchanges);
+ }
+
+ //Should perhaps error if parameters[0] is null;
+ AMQShortString exchangeName = parameters.length > 0 ? (AMQShortString) parameters[0] : null;
+ AMQShortString className = parameters.length > 1 ? (AMQShortString) parameters[1] : null;
+
+ //Store the exchangeName / class mapping if the mapping is null
+ createRights.put(exchangeName, className);
+ }
+ break;
+ case DELETE:
+ break;
+
+ case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey
+ Map publishRights = (Map) _permissions.get(permission);
+
+ if (publishRights == null)
+ {
+ publishRights = new ConcurrentHashMap();
+ _permissions.put(permission, publishRights);
+ }
+
+ if (parameters == null || parameters.length == 0)
+ {
+ //If we have no parameters then allow publish to all destinations
+ // this is signified by having a null value for publish_exchanges
+ }
+ else
+ {
+ Map publish_exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY);
+
+ if (publish_exchanges == null)
+ {
+ publish_exchanges = new ConcurrentHashMap();
+ publishRights.put(PUBLISH_EXCHANGES_KEY, publish_exchanges);
+ }
+
+
+ HashSet routingKeys = (HashSet) publish_exchanges.get(parameters[0]);
+
+ // Check to see if we have a routing key
+ if (parameters.length == 2)
+ {
+ if (routingKeys == null)
+ {
+ routingKeys = new HashSet<AMQShortString>();
+ }
+ //Add routing key to permitted publish destinations
+ routingKeys.add(parameters[1]);
+ }
+
+ // Add the updated routingkey list or null if all values allowed
+ publish_exchanges.put(parameters[0], routingKeys);
+ }
+ break;
+ case PURGE:
+ break;
+ case UNBIND:
+ break;
+ }
+
+ }
+
+ public boolean authorise(Permission permission, Object... parameters)
+ {
+
+ switch (permission)
+ {
+ case ACCESS:
+ return true; // This is here for completeness but the SimpleXML ACLManager never calls it.
+ // The existence of this user specific PP can be validated in the map SimpleXML maintains.
+ case BIND: // Parameters : QueueBindMethod , Exchange , AMQQueue, AMQShortString routingKey
+
+ Exchange exchange = (Exchange) parameters[1];
+
+ AMQQueue bind_queueName = (AMQQueue) parameters[2];
+ AMQShortString routingKey = (AMQShortString) parameters[3];
+
+ //Get all Create Rights for this user
+ Map bindCreateRights = (Map) _permissions.get(Permission.CREATE);
+
+ //Look up the Queue Creation Rights
+ Map bind_create_queues = (Map) bindCreateRights.get(CREATE_QUEUES_KEY);
+
+ //Lookup the list of queues
+ Map bind_create_queues_queues = (Map) bindCreateRights.get(CREATE_QUEUE_QUEUES_KEY);
+
+ // Check and see if we have a queue white list to check
+ if (bind_create_queues_queues != null)
+ {
+ //There a white list for queues
+ Map exchangeDetails = (Map) bind_create_queues_queues.get(bind_queueName);
+
+ if (exchangeDetails == null) //Then all queue can be bound to all exchanges.
+ {
+ return true;
+ }
+
+ // Check to see if we have a white list of routingkeys to check
+ Map rkeys = (Map) exchangeDetails.get(exchange.getName());
+
+ // if keys is null then any rkey is allowed on this exchange
+ if (rkeys == null)
+ {
+ // There is no routingkey white list
+ return true;
+ }
+ else
+ {
+ // We have routingKeys so a match must be found to allowed binding
+ Iterator keys = rkeys.keySet().iterator();
+
+ boolean matched = false;
+ while (keys.hasNext() && !matched)
+ {
+ AMQShortString rkey = (AMQShortString) keys.next();
+ if (rkey.endsWith("*"))
+ {
+ matched = routingKey.startsWith(rkey.subSequence(0, rkey.length() - 1).toString());
+ }
+ else
+ {
+ matched = routingKey.equals(rkey);
+ }
+ }
+
+
+ return matched;
+ }
+
+
+ }
+ else
+ {
+ //There a is no white list for queues
+
+ // So can allow all queues to be bound
+ // but we should first check and see if we have a temp queue and validate that we are allowed
+ // to bind temp queues.
+
+ //Check to see if we have a temporary queue
+ if (bind_queueName.isAutoDelete())
+ {
+ // Check and see if we have an exchange white list.
+ Map bind_exchanges = (Map) bind_create_queues.get(CREATE_QUEUE_EXCHANGES_KEY);
+
+ // If the exchange exists then we must check to see if temporary queues are allowed here
+ if (bind_exchanges != null)
+ {
+ // Check to see if the requested exchange is allowed.
+ Map exchangeDetails = (Map) bind_exchanges.get(exchange.getName());
+
+ return (Boolean) exchangeDetails.get(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY);
+ }
+
+ //no white list so all allowed, drop through to return true below.
+ }
+
+ // not a temporary queue and no white list so all allowed.
+ return true;
+ }
+
+ case CREATE:// Paramters : QueueDeclareBody || ExchangeDeclareBody
+
+ Map createRights = (Map) _permissions.get(permission);
+
+ // If there are no create rights then deny request
+ if (createRights == null)
+ {
+ return false;
+ }
+
+ if (parameters.length == 1)
+ {
+ if (parameters[0] instanceof QueueDeclareBody)
+ {
+ QueueDeclareBody body = (QueueDeclareBody) parameters[0];
+
+ //Look up the Queue Creation Rights
+ Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY);
+
+ //Lookup the list of queues allowed to be created
+ Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY);
+
+
+ AMQShortString queueName = body.getQueue();
+
+
+ if (body.getAutoDelete())// we have a temporary queue
+ {
+ return (Boolean) create_queues.get(CREATE_QUEUE_TEMPORARY_KEY);
+ }
+ else
+ {
+ // If there is a white list then check
+ return create_queues_queues == null || create_queues_queues.containsKey(queueName);
+ }
+
+ }
+ else if (parameters[0] instanceof ExchangeDeclareBody)
+ {
+ ExchangeDeclareBody body = (ExchangeDeclareBody) parameters[0];
+
+ AMQShortString exchangeName = body.getExchange();
+
+ Map create_exchanges = (Map) createRights.get(CREATE_EXCHANGES_KEY);
+
+ // If the exchange list is doesn't exist then all is allowed else check the valid exchanges
+ return create_exchanges == null || create_exchanges.containsKey(exchangeName);
+ }
+ }
+ break;
+ case CONSUME: // Parameters : AMQQueue
+
+ if (parameters.length == 1 && parameters[0] instanceof AMQQueue)
+ {
+ AMQQueue queue = ((AMQQueue) parameters[0]);
+ Map queuePermissions = (Map) _permissions.get(permission);
+
+ List queues = (List) queuePermissions.get(CONSUME_QUEUES_KEY);
+
+ Boolean temporayQueues = (Boolean) queuePermissions.get(CONSUME_TEMPORARY_KEY);
+ Boolean ownQueuesOnly = (Boolean) queuePermissions.get(CONSUME_OWN_QUEUES_ONLY_KEY);
+
+ // If user is allowed to publish to temporary queues and this is a temp queue then allow it.
+ if (temporayQueues)
+ {
+ if (queue.isAutoDelete())
+ // This will allow consumption from any temporary queue including ones not owned by this user.
+ // Of course the exclusivity will not be broken.
+ {
+ // if not limited to ownQueuesOnly then ok else check queue Owner.
+ return !ownQueuesOnly || queue.getOwner().equals(_user);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // if queues are white listed then ensure it is ok
+ if (queues != null)
+ {
+ // if no queues are listed then ALL are ok othereise it must be specified.
+ if (ownQueuesOnly)
+ {
+ if (queue.getOwner().equals(_user))
+ {
+ return queues.size() == 0 || queues.contains(queue.getName());
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // If we are
+ return queues.size() == 0 || queues.contains(queue.getName());
+ }
+ }
+
+ // Can't authenticate without the right parameters
+ return false;
+ case DELETE:
+ break;
+
+ case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey
+ Map publishRights = (Map) _permissions.get(permission);
+
+ if (publishRights == null)
+ {
+ return false;
+ }
+
+ Map exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY);
+
+ // Having no exchanges listed gives full publish rights to all exchanges
+ if (exchanges == null)
+ {
+ return true;
+ }
+ // Otherwise exchange must be listed in the white list
+
+ // If the map doesn't have the exchange then it isn't allowed
+ if (!exchanges.containsKey(parameters[0]))
+ {
+ return false;
+ }
+ else
+ {
+
+ // Get valid routing keys
+ HashSet routingKeys = (HashSet) exchanges.get(parameters[0]);
+
+ // Having no routingKeys in the map then all are allowed.
+ if (routingKeys == null)
+ {
+ return true;
+ }
+ else
+ {
+ // We have routingKeys so a match must be found to allowed binding
+ Iterator keys = routingKeys.iterator();
+
+
+ AMQShortString publishRKey = (AMQShortString)parameters[1];
+
+ boolean matched = false;
+ while (keys.hasNext() && !matched)
+ {
+ AMQShortString rkey = (AMQShortString) keys.next();
+
+ if (rkey.endsWith("*"))
+ {
+ matched = publishRKey.startsWith(rkey.subSequence(0, rkey.length() - 1));
+ }
+ else
+ {
+ matched = publishRKey.equals(rkey);
+ }
+ }
+ return matched;
+ }
+ }
+ case PURGE:
+ break;
+ case UNBIND:
+ break;
+
+ }
+
+ return false;
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java
index 2dc7fcbc1e..a8ae03cc5d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java
@@ -18,7 +18,7 @@
*
*
*/
-package org.apache.qpid.server.security.access;
+package org.apache.qpid.server.security.access.management;
import org.apache.qpid.server.management.MBeanDescription;
import org.apache.qpid.server.management.AMQManagedObject;
@@ -26,6 +26,7 @@ import org.apache.qpid.server.management.MBeanOperation;
import org.apache.qpid.server.management.MBeanInvocationHandlerImpl;
import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+import org.apache.qpid.server.security.access.management.UserManagement;
import org.apache.log4j.Logger;
import org.apache.commons.configuration.ConfigurationException;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/management/UserManagement.java
index b8762aa43b..658d7ebbd3 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/management/UserManagement.java
@@ -18,7 +18,7 @@
*
*
*/
-package org.apache.qpid.server.security.access;
+package org.apache.qpid.server.security.access.management;
import org.apache.qpid.server.management.MBeanOperation;
import org.apache.qpid.server.management.MBeanOperationParameter;
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
new file mode 100644
index 0000000000..9b784069dd
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.access.plugins;
+
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.security.access.ACLPlugin;
+import org.apache.qpid.server.security.access.ACLManager;
+import org.apache.qpid.server.security.access.AccessResult;
+import org.apache.qpid.server.security.access.Accessable;
+import org.apache.qpid.server.security.access.Permission;
+import org.apache.commons.configuration.Configuration;
+
+public class AllowAll implements ACLPlugin
+{
+ public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters)
+ {
+ if (ACLManager.getLogger().isDebugEnabled())
+ {
+ ACLManager.getLogger().debug("Allowing user:" + session.getAuthorizedID() + " for :" + permission.toString()
+ + " on " + body.getClass().getSimpleName()
+ + (parameters == null || parameters.length == 0 ? "" : "-" + accessablesToString(parameters)));
+ }
+
+ return new AccessResult(this, AccessResult.AccessStatus.GRANTED);
+ }
+
+ public static String accessablesToString(Object[] accessObject)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (Object access : accessObject)
+ {
+ sb.append(access.getClass().getSimpleName() + ":" + access.toString() + ", ");
+ }
+
+ return sb.delete(sb.length() - 2, sb.length()).toString();
+ }
+
+ public String getPluginName()
+ {
+ return "AllowAll";
+ }
+
+ public void setConfiguaration(Configuration config)
+ {
+ //no-op
+ }
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java
new file mode 100644
index 0000000000..80c125e737
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.access.plugins;
+
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.security.access.ACLManager;
+import org.apache.qpid.server.security.access.ACLPlugin;
+import org.apache.qpid.server.security.access.AccessResult;
+import org.apache.qpid.server.security.access.Permission;
+import org.apache.qpid.AMQConnectionException;
+import org.apache.commons.configuration.Configuration;
+
+public class DenyAll implements ACLPlugin
+{
+ public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) throws AMQConnectionException
+ {
+
+ if (ACLManager.getLogger().isInfoEnabled())
+ {
+ }
+ ACLManager.getLogger().info("Denying user:" + session.getAuthorizedID() + " for :" + permission.toString()
+ + " on " + body.getClass().getSimpleName()
+ + (parameters == null || parameters.length == 0 ? "" : "-" + AllowAll.accessablesToString(parameters)));
+
+ throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "DenyAll Plugin");
+ }
+
+ public String getPluginName()
+ {
+ return "DenyAll";
+ }
+
+ public void setConfiguaration(Configuration config)
+ {
+ //no-op
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java
new file mode 100644
index 0000000000..251f4e6330
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java
@@ -0,0 +1,342 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.server.security.access.plugins;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQConnectionException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicConsumeBody;
+import org.apache.qpid.framing.BasicPublishBody;
+
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.security.access.ACLManager;
+import org.apache.qpid.server.security.access.ACLPlugin;
+import org.apache.qpid.server.security.access.AccessResult;
+import org.apache.qpid.server.security.access.Permission;
+import org.apache.qpid.server.security.access.PrincipalPermissions;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This uses the default
+ */
+public class SimpleXML implements ACLPlugin
+{
+ private Map<String, PrincipalPermissions> _users;
+ private final AccessResult GRANTED = new AccessResult(this, AccessResult.AccessStatus.GRANTED);
+
+ public SimpleXML()
+ {
+ _users = new ConcurrentHashMap<String, PrincipalPermissions>();
+ }
+
+ public void setConfiguaration(Configuration config)
+ {
+ processConfig(config);
+ }
+
+ private void processConfig(Configuration config)
+ {
+ processPublish(config);
+
+ processConsume(config);
+
+ processCreate(config);
+ }
+
+ /**
+ * Publish format takes
+ * Exchange + Routing Key Pairs
+ *
+ * @param config XML Configuration
+ */
+ private void processPublish(Configuration config)
+ {
+ Configuration publishConfig = config.subset("security.access_control_list.publish");
+
+ //Process users that have full publish permission
+ String[] users = publishConfig.getStringArray("users.user");
+
+ for (String user : users)
+ {
+ grant(Permission.PUBLISH, user);
+ }
+
+ // Process exchange limited users
+ int exchangeCount = 0;
+ Configuration exchangeConfig = publishConfig.subset("exchanges.exchange(" + exchangeCount + ")");
+
+ while (!exchangeConfig.isEmpty())
+ {
+ //Get Exchange Name
+ AMQShortString exchangeName = new AMQShortString(exchangeConfig.getString("name"));
+
+ //Get Routing Keys
+ int keyCount = 0;
+ Configuration routingkeyConfig = exchangeConfig.subset("routing_keys.routing_key(" + keyCount + ")");
+
+ while (!routingkeyConfig.isEmpty())
+ {
+ //Get RoutingKey Value
+ AMQShortString routingKeyValue = new AMQShortString(routingkeyConfig.getString("value"));
+
+ //Apply Exchange + RoutingKey permissions to Users
+ users = routingkeyConfig.getStringArray("users.user");
+ for (String user : users)
+ {
+ grant(Permission.PUBLISH, user, exchangeName, routingKeyValue);
+ }
+
+ //Apply permissions to Groups
+
+ // Check for more configs
+ keyCount++;
+ routingkeyConfig = exchangeConfig.subset("routing_keys.routing_key(" + keyCount + ")");
+ }
+
+ //Apply Exchange wide permissions to Users
+ users = exchangeConfig.getStringArray("exchange(" + exchangeCount + ").users.user");
+
+ for (String user : users)
+ {
+ grant(Permission.PUBLISH, user, exchangeName);
+ }
+
+ //Apply permissions to Groups
+ exchangeCount++;
+ exchangeConfig = publishConfig.subset("exchanges.exchange(" + exchangeCount + ")");
+ }
+ }
+
+ private void grant(Permission permission, String user, Object... parameters)
+ {
+ PrincipalPermissions permissions = _users.get(user);
+
+ if (permissions == null)
+ {
+ permissions = new PrincipalPermissions(user);
+ }
+
+ _users.put(user, permissions);
+ permissions.grant(permission, parameters);
+ }
+
+ private void processConsume(Configuration config)
+ {
+ Configuration consumeConfig = config.subset("security.access_control_list.consume");
+
+ // Process queue limited users
+ int queueCount = 0;
+ Configuration queueConfig = consumeConfig.subset("queues.queue(" + queueCount + ")");
+
+ while (!queueConfig.isEmpty())
+ {
+ //Get queue Name
+ AMQShortString queueName = new AMQShortString(queueConfig.getString("name"));
+ // if there is no name then there may be a temporary element
+ boolean temporary = queueConfig.containsKey("temporary");
+ boolean ownQueues = queueConfig.containsKey("own_queues");
+
+ //Process permissions for this queue
+ String[] users = queueConfig.getStringArray("users.user");
+ for (String user : users)
+ {
+ grant(Permission.CONSUME, user, queueName, temporary, ownQueues);
+ }
+
+ //See if we have another config
+ queueCount++;
+ queueConfig = consumeConfig.subset("queues.queue(" + queueCount + ")");
+ }
+
+ // Process users that have full consume permission
+ String[] users = consumeConfig.getStringArray("users.user");
+
+ for (String user : users)
+ {
+ grant(Permission.CONSUME, user);
+ }
+ }
+
+ private void processCreate(Configuration config)
+ {
+ Configuration createConfig = config.subset("security.access_control_list.create");
+
+ // Process create permissions for queue creation
+ int queueCount = 0;
+ Configuration queueConfig = createConfig.subset("queues.queue(" + queueCount + ")");
+
+ while (!queueConfig.isEmpty())
+ {
+ //Get queue Name
+ AMQShortString queueName = new AMQShortString(queueConfig.getString("name"));
+
+ // if there is no name then there may be a temporary element
+ boolean temporary = queueConfig.containsKey("temporary");
+
+ int exchangeCount = 0;
+ Configuration exchangeConfig = queueConfig.subset("exchanges.exchange(" + exchangeCount + ")");
+
+ while (!exchangeConfig.isEmpty())
+ {
+
+ AMQShortString exchange = new AMQShortString(exchangeConfig.getString("name"));
+ AMQShortString routingKey = new AMQShortString(exchangeConfig.getString("routing_key"));
+
+ //Process permissions for this queue
+ String[] users = exchangeConfig.getStringArray("users.user");
+ for (String user : users)
+ {
+ grant(Permission.CREATE, user, temporary,
+ (queueName.equals("") ? null : queueName),
+ (exchange.equals("") ? null : exchange),
+ (routingKey.equals("") ? null : routingKey));
+ }
+
+ //See if we have another config
+ exchangeCount++;
+ exchangeConfig = queueConfig.subset("exchanges.exchange(" + exchangeCount + ")");
+ }
+
+ // Process users that are not bound to an exchange
+ String[] users = queueConfig.getStringArray("users.user");
+
+ for (String user : users)
+ {
+ grant(Permission.CREATE, user, temporary, queueName);
+ }
+
+ //See if we have another config
+ queueCount++;
+ queueConfig = createConfig.subset("queues.queue(" + queueCount + ")");
+ }
+
+ // Process create permissions for exchange creation
+ int exchangeCount = 0;
+ Configuration exchangeConfig = createConfig.subset("exchanges.exchange(" + exchangeCount + ")");
+
+ while (!exchangeConfig.isEmpty())
+ {
+ AMQShortString exchange = new AMQShortString(exchangeConfig.getString("name"));
+ AMQShortString clazz = new AMQShortString(exchangeConfig.getString("class"));
+
+ //Process permissions for this queue
+ String[] users = exchangeConfig.getStringArray("users.user");
+ for (String user : users)
+ {
+ grant(Permission.CREATE, user, exchange, clazz);
+ }
+
+ //See if we have another config
+ exchangeCount++;
+ exchangeConfig = queueConfig.subset("exchanges.exchange(" + exchangeCount + ")");
+ }
+
+ // Process users that have full create permission
+ String[] users = createConfig.getStringArray("users.user");
+
+ for (String user : users)
+ {
+ grant(Permission.CREATE, user);
+ }
+
+
+ }
+
+ public String getPluginName()
+ {
+ return "Simple";
+ }
+
+ public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) throws AMQConnectionException
+ {
+ String error = "";
+
+ if (ACLManager.getLogger().isInfoEnabled())
+ {
+ ACLManager.getLogger().info("Simple Authorisation processing user:" + session.getAuthorizedID() + " for :" + permission.toString()
+ + " on " + body.getClass().getSimpleName()
+ + (parameters == null || parameters.length == 0 ? "" : "-" + AllowAll.accessablesToString(parameters)));
+ }
+
+ String username = session.getAuthorizedID().getName();
+
+ //Get the Users Permissions
+ PrincipalPermissions permissions = _users.get(username);
+
+ if (permissions != null)
+ {
+ switch (permission)
+ {
+ case ACCESS:
+ return GRANTED;
+ case BIND: // Body QueueDeclareBody - Parameters : Exchange, Queue, QueueName
+ // Body QueueBindBody - Paramters : Exchange, Queue, QueueName
+ if (parameters.length == 3)
+ {
+ // Parameters : Exchange, Queue, RoutingKey
+ if (permissions.authorise(Permission.BIND, body, parameters[0], parameters[1], parameters[2]))
+ {
+ return GRANTED;
+ }
+ }
+ break;
+ case CONSUME: // Parameters : none
+ if (parameters.length == 1 && permissions.authorise(Permission.CONSUME, parameters[0]))
+ {
+ return GRANTED;
+ }
+ break;
+ case CREATE: // Body : QueueDeclareBody | ExchangeDeclareBody - Parameters : none
+ if (permissions.authorise(Permission.CREATE, body))
+ {
+ return GRANTED;
+ }
+ break;
+ case PUBLISH: // Body : BasicPublishBody Parameters : exchange
+ if (parameters.length == 1 && parameters[0] instanceof Exchange)
+ {
+ if (permissions.authorise(Permission.PUBLISH, ((Exchange) parameters[0]).getName(),
+ ((BasicPublishBody) body).getRoutingKey()))
+ {
+ return GRANTED;
+ }
+ }
+ break;
+ case PURGE:
+ break;
+ case DELETE:
+ break;
+ case UNBIND:
+ break;
+ }
+ }
+
+ //todo potential refactor this ConnectionException Out of here
+ throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, error);
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
index 10adfdd9fc..348bccb4e9 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
@@ -24,7 +24,7 @@ import org.apache.log4j.Logger;
import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser;
-import org.apache.qpid.server.security.access.AMQUserManagementMBean;
+import org.apache.qpid.server.security.access.management.AMQUserManagementMBean;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.EncoderException;
@@ -45,7 +45,6 @@ import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
import java.security.Principal;
import java.security.NoSuchAlgorithmException;
-import java.security.MessageDigest;
/**
* Represents a user database where the account information is stored in a simple flat file.
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java
index 2d3f5e5131..15c62a62e4 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java
@@ -37,7 +37,7 @@ import org.apache.qpid.configuration.PropertyException;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager;
-import org.apache.qpid.server.security.access.AMQUserManagementMBean;
+import org.apache.qpid.server.security.access.management.AMQUserManagementMBean;
import org.apache.qpid.AMQException;
import javax.management.JMException;
@@ -50,17 +50,16 @@ public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatab
Map<String, PrincipalDatabase> _databases;
- public ConfigurationFilePrincipalDatabaseManager() throws Exception
+ public ConfigurationFilePrincipalDatabaseManager(Configuration configuration) throws Exception
{
_logger.info("Initialising PrincipleDatabase authentication manager");
- _databases = initialisePrincipalDatabases();
+ _databases = initialisePrincipalDatabases(configuration);
}
- private Map<String, PrincipalDatabase> initialisePrincipalDatabases() throws Exception
+ private Map<String, PrincipalDatabase> initialisePrincipalDatabases(Configuration configuration) throws Exception
{
- Configuration config = ApplicationRegistry.getInstance().getConfiguration();
- List<String> databaseNames = config.getList(_base + ".name");
- List<String> databaseClasses = config.getList(_base + ".class");
+ List<String> databaseNames = configuration.getList(_base + ".name");
+ List<String> databaseClasses = configuration.getList(_base + ".class");
Map<String, PrincipalDatabase> databases = new HashMap<String, PrincipalDatabase>();
if (databaseNames.size() == 0)
@@ -85,7 +84,7 @@ public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatab
throw new Exception("Principal databases must implement the PrincipalDatabase interface");
}
- initialisePrincipalDatabase((PrincipalDatabase) o, config, i);
+ initialisePrincipalDatabase((PrincipalDatabase) o, configuration, i);
String name = databaseNames.get(i);
if ((name == null) || (name.length() == 0))
@@ -200,7 +199,7 @@ public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatab
}
String jmxaccesssFile = null;
-
+
try
{
jmxaccesssFile = PropertyUtils.replaceProperties(jmxaccesslist.get(0));
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java
deleted file mode 100644
index 5c372f6c2c..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- *
- */
-package org.apache.qpid.server.security.auth.database;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.security.access.AccessManager;
-import org.apache.qpid.server.security.access.AccessResult;
-import org.apache.qpid.server.security.access.AccessRights;
-import org.apache.qpid.server.security.access.Accessable;
-import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.security.Principal;
-
-/**
- * Represents a user database where the account information is stored in a simple flat file.
- *
- * The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn
- *
- * where a carriage return separates each username/password pair. Passwords are assumed to be in plain text.
- */
-public class PlainPasswordVhostFilePrincipalDatabase extends PlainPasswordFilePrincipalDatabase implements AccessManager
-{
- private static final Logger _logger = Logger.getLogger(PlainPasswordVhostFilePrincipalDatabase.class);
-
- /**
- * Looks up the virtual hosts for a specified user in the password file.
- *
- * @param user The user to lookup
- *
- * @return a list of virtualhosts
- */
- private String[] lookupVirtualHost(String user)
- {
- try
- {
- BufferedReader reader = null;
- try
- {
- reader = new BufferedReader(new FileReader(_passwordFile));
- String line;
-
- while ((line = reader.readLine()) != null)
- {
- if (!line.startsWith("#"))
- {
- String[] result = _regexp.split(line);
- if (result == null || result.length < 3)
- {
- continue;
- }
-
- if (user.equals(result[0]))
- {
- return result[2].split(",");
- }
- }
- }
- return null;
- }
- finally
- {
- if (reader != null)
- {
- reader.close();
- }
- }
- }
- catch (IOException ioe)
- {
- //ignore
- }
- return null;
- }
-
-
- public AccessResult isAuthorized(Accessable accessObject, String username)
- {
- return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ);
- }
-
- public AccessResult isAuthorized(Accessable accessObject, Principal user, AccessRights.Rights rights)
- {
-
- if (accessObject instanceof VirtualHost)
- {
- String[] hosts = lookupVirtualHost(user.getName());
-
- if (hosts != null)
- {
- for (String host : hosts)
- {
- if (accessObject.getAccessableName().equals(host))
- {
- return new AccessResult(this, AccessResult.AccessStatus.GRANTED);
- }
- }
- }
- }
-
- return new AccessResult(this, AccessResult.AccessStatus.REFUSED);
- }
-
- public String getName()
- {
- return "PlainPasswordVhostFile";
- }
-
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java
index 73d58ca489..c8a4add0f1 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java
@@ -69,10 +69,14 @@ public class PropertiesPrincipalDatabase implements PrincipalDatabase
{
throw new IllegalArgumentException("principal must not be null");
}
- char[] pwd = _users.getProperty(principal.getName()).toCharArray();
+
+
+
+ final String pwd = _users.getProperty(principal.getName());
+
if (pwd != null)
{
- callback.setPassword(pwd);
+ callback.setPassword(pwd.toCharArray());
}
else
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
index ce5e0cd748..f589140e8e 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
@@ -82,14 +82,6 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
if (databaseName == null)
{
- if (hostConfig instanceof SubsetConfiguration)
- {
- _logger.warn("No authentication specified for '" + ((SubsetConfiguration) hostConfig).getPrefix() + "'. Using Default authentication manager");
- }
- else
- {
- _logger.warn("No authentication specified. Using Default authentication manager");
- }
_default = ApplicationRegistry.getInstance().getAuthenticationManager();
return;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java
index 1dcbb02d5c..23aaf56876 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java
@@ -94,7 +94,7 @@ public class ConnectorConfiguration
public String certType;
@Configured(path = "connector.qpidnio",
- defaultValue = "true")
+ defaultValue = "false")
public boolean _multiThreadNIO;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
index 047cef9064..1e4b69c935 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
@@ -49,11 +49,11 @@ public class NonTransactionalContext implements TransactionalContext
/** Where to put undeliverable messages */
private final List<RequiredDeliveryException> _returnMessages;
- private Set<Long> _browsedAcks;
+ private final Set<Long> _browsedAcks;
private final MessageStore _messageStore;
- private StoreContext _storeContext;
+ private final StoreContext _storeContext;
/** Whether we are in a transaction */
private boolean _inTran;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java
index b4c66aa24d..6016ecc1a5 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java
@@ -24,7 +24,6 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.store.StoreContext;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java
index e9fa642175..0acfa84f31 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java
@@ -35,8 +35,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.virtualhost.VirtualHost;
import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
@@ -48,7 +48,7 @@ public class NullApplicationRegistry extends ApplicationRegistry
private VirtualHostRegistry _virtualHostRegistry;
- private AccessManager _accessManager;
+ private ACLPlugin _accessManager;
private PrincipalDatabaseManager _databaseManager;
@@ -116,7 +116,7 @@ public class NullApplicationRegistry extends ApplicationRegistry
return _virtualHostRegistry;
}
- public AccessManager getAccessManager()
+ public ACLPlugin getAccessManager()
{
return _accessManager;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
index 8d6a26fdbc..3ff9b8c356 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
@@ -1,260 +1,308 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.virtualhost;
-
-import javax.management.NotCompliantMBeanException;
-
-import org.apache.commons.configuration.Configuration;
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.AMQBrokerManagerMBean;
-import org.apache.qpid.server.security.access.AccessManager;
-import org.apache.qpid.server.security.access.AccessManagerImpl;
-import org.apache.qpid.server.security.access.Accessable;
-import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager;
-import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
-import org.apache.qpid.server.configuration.Configurator;
-import org.apache.qpid.server.exchange.DefaultExchangeFactory;
-import org.apache.qpid.server.exchange.DefaultExchangeRegistry;
-import org.apache.qpid.server.exchange.ExchangeFactory;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.management.AMQManagedObject;
-import org.apache.qpid.server.management.ManagedObject;
-import org.apache.qpid.server.queue.DefaultQueueRegistry;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.store.MessageStore;
-
-public class VirtualHost implements Accessable
-{
- private static final Logger _logger = Logger.getLogger(VirtualHost.class);
-
-
- private final String _name;
-
- private QueueRegistry _queueRegistry;
-
- private ExchangeRegistry _exchangeRegistry;
-
- private ExchangeFactory _exchangeFactory;
-
- private MessageStore _messageStore;
-
- protected VirtualHostMBean _virtualHostMBean;
-
- private AMQBrokerManagerMBean _brokerMBean;
-
- private AuthenticationManager _authenticationManager;
-
- private AccessManager _accessManager;
-
-
- public void setAccessableName(String name)
- {
- _logger.warn("Setting Accessable Name for VirualHost is not allowed. ("
- + name + ") ignored remains :" + getAccessableName());
- }
-
- public String getAccessableName()
- {
- return _name;
- }
-
-
- /**
- * Abstract MBean class. This has some of the methods implemented from management intrerface for exchanges. Any
- * implementaion of an Exchange MBean should extend this class.
- */
- public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost
- {
- public VirtualHostMBean() throws NotCompliantMBeanException
- {
- super(ManagedVirtualHost.class, "VirtualHost");
- }
-
- public String getObjectInstanceName()
- {
- return _name.toString();
- }
-
- public String getName()
- {
- return _name.toString();
- }
-
- public VirtualHost getVirtualHost()
- {
- return VirtualHost.this;
- }
-
-
- } // End of MBean class
-
- /**
- * Used for testing only
- * @param name
- * @param store
- * @throws Exception
- */
- public VirtualHost(String name, MessageStore store) throws Exception
- {
- this(name, null, store);
- }
-
- /**
- * Normal Constructor
- * @param name
- * @param hostConfig
- * @throws Exception
- */
- public VirtualHost(String name, Configuration hostConfig) throws Exception
- {
- this(name, hostConfig, null);
- }
-
- private VirtualHost(String name, Configuration hostConfig, MessageStore store) throws Exception
- {
- _name = name;
-
- _virtualHostMBean = new VirtualHostMBean();
- // This isn't needed to be registered
- //_virtualHostMBean.register();
-
- _queueRegistry = new DefaultQueueRegistry(this);
- _exchangeFactory = new DefaultExchangeFactory(this);
- _exchangeFactory.initialise(hostConfig);
- _exchangeRegistry = new DefaultExchangeRegistry(this);
-
- if (store != null)
- {
- _messageStore = store;
- }
- else
- {
- if (hostConfig == null)
- {
- throw new IllegalAccessException("HostConfig and MessageStore cannot be null");
- }
- initialiseMessageStore(hostConfig);
- }
-
- _exchangeRegistry.initialise();
-
- _authenticationManager = new PrincipalDatabaseAuthenticationManager(name, hostConfig);
-
- _accessManager = new AccessManagerImpl(name, hostConfig);
-
- _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean);
- _brokerMBean.register();
- }
-
- private void initialiseMessageStore(Configuration config) throws Exception
- {
- String messageStoreClass = config.getString("store.class");
-
- Class clazz = Class.forName(messageStoreClass);
- Object o = clazz.newInstance();
-
- if (!(o instanceof MessageStore))
- {
- throw new ClassCastException("Message store class must implement " + MessageStore.class + ". Class " + clazz +
- " does not.");
- }
- _messageStore = (MessageStore) o;
- _messageStore.configure(this, "store", config);
- }
-
-
- public <T> T getConfiguredObject(Class<T> instanceType, Configuration config)
- {
- T instance;
- try
- {
- instance = instanceType.newInstance();
- }
- catch (Exception e)
- {
- _logger.error("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor");
- throw new IllegalArgumentException("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor", e);
- }
- Configurator.configure(instance);
-
- return instance;
- }
-
-
- public String getName()
- {
- return _name;
- }
-
- public QueueRegistry getQueueRegistry()
- {
- return _queueRegistry;
- }
-
- public ExchangeRegistry getExchangeRegistry()
- {
- return _exchangeRegistry;
- }
-
- public ExchangeFactory getExchangeFactory()
- {
- return _exchangeFactory;
- }
-
- public ApplicationRegistry getApplicationRegistry()
- {
- throw new UnsupportedOperationException();
- }
-
- public MessageStore getMessageStore()
- {
- return _messageStore;
- }
-
- public AuthenticationManager getAuthenticationManager()
- {
- return _authenticationManager;
- }
-
- public AccessManager getAccessManager()
- {
- return _accessManager;
- }
-
- public void close() throws Exception
- {
- if (_messageStore != null)
- {
- _messageStore.close();
- }
- }
-
- public ManagedObject getBrokerMBean()
- {
- return _brokerMBean;
- }
-
- public ManagedObject getManagedObject()
- {
- return _virtualHostMBean;
- }
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.virtualhost;
+
+import javax.management.NotCompliantMBeanException;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.AMQBrokerManagerMBean;
+import org.apache.qpid.server.security.access.ACLPlugin;
+import org.apache.qpid.server.security.access.ACLManager;
+import org.apache.qpid.server.security.access.Accessable;
+import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager;
+import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
+import org.apache.qpid.server.configuration.Configurator;
+import org.apache.qpid.server.exchange.DefaultExchangeFactory;
+import org.apache.qpid.server.exchange.DefaultExchangeRegistry;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.management.AMQManagedObject;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.queue.DefaultQueueRegistry;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.AMQException;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class VirtualHost implements Accessable
+{
+ private static final Logger _logger = Logger.getLogger(VirtualHost.class);
+
+
+ private final String _name;
+
+ private QueueRegistry _queueRegistry;
+
+ private ExchangeRegistry _exchangeRegistry;
+
+ private ExchangeFactory _exchangeFactory;
+
+ private MessageStore _messageStore;
+
+ protected VirtualHostMBean _virtualHostMBean;
+
+ private AMQBrokerManagerMBean _brokerMBean;
+
+ private AuthenticationManager _authenticationManager;
+
+ private ACLPlugin _accessManager;
+
+ private final Timer _houseKeepingTimer = new Timer("Queue-housekeeping", true);
+
+ private static final long DEFAULT_HOUSEKEEPING_PERIOD = 30000L;
+
+ public void setAccessableName(String name)
+ {
+ _logger.warn("Setting Accessable Name for VirualHost is not allowed. ("
+ + name + ") ignored remains :" + getAccessableName());
+ }
+
+ public String getAccessableName()
+ {
+ return _name;
+ }
+
+
+ /**
+ * Abstract MBean class. This has some of the methods implemented from management intrerface for exchanges. Any
+ * implementaion of an Exchange MBean should extend this class.
+ */
+ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost
+ {
+ public VirtualHostMBean() throws NotCompliantMBeanException
+ {
+ super(ManagedVirtualHost.class, "VirtualHost");
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _name.toString();
+ }
+
+ public String getName()
+ {
+ return _name.toString();
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return VirtualHost.this;
+ }
+
+
+ } // End of MBean class
+
+ /**
+ * Used for testing only
+ * @param name
+ * @param store
+ * @throws Exception
+ */
+ public VirtualHost(String name, MessageStore store) throws Exception
+ {
+ this(name, new PropertiesConfiguration(), store);
+ }
+
+ /**
+ * Normal Constructor
+ * @param name
+ * @param hostConfig
+ * @throws Exception
+ */
+ public VirtualHost(String name, Configuration hostConfig) throws Exception
+ {
+ this(name, hostConfig, null);
+ }
+
+ public VirtualHost(String name, Configuration hostConfig, MessageStore store) throws Exception
+ {
+ _name = name;
+
+ _virtualHostMBean = new VirtualHostMBean();
+ // This isn't needed to be registered
+ //_virtualHostMBean.register();
+
+ _queueRegistry = new DefaultQueueRegistry(this);
+ _exchangeFactory = new DefaultExchangeFactory(this);
+ _exchangeFactory.initialise(hostConfig);
+ _exchangeRegistry = new DefaultExchangeRegistry(this);
+
+ if (store != null)
+ {
+ _messageStore = store;
+ }
+ else
+ {
+ if (hostConfig == null)
+ {
+ throw new IllegalAccessException("HostConfig and MessageStore cannot be null");
+ }
+ initialiseMessageStore(hostConfig);
+ }
+
+ _exchangeRegistry.initialise();
+
+ _authenticationManager = new PrincipalDatabaseAuthenticationManager(name, hostConfig);
+
+ _accessManager = ACLManager.loadACLManager(name, hostConfig);
+
+ _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean);
+ _brokerMBean.register();
+ initialiseHouseKeeping(hostConfig);
+ }
+
+ private void initialiseHouseKeeping(final Configuration hostConfig)
+ {
+
+ long period = hostConfig.getLong("housekeeping.expiredMessageCheckPeriod", DEFAULT_HOUSEKEEPING_PERIOD);
+
+ /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */
+ if(period != 0L)
+ {
+ class RemoveExpiredMessagesTask extends TimerTask
+ {
+ public void run()
+ {
+ for(AMQQueue q : _queueRegistry.getQueues())
+ {
+
+ try
+ {
+ q.removeExpiredIfNoSubscribers();
+ }
+ catch (AMQException e)
+ {
+ _logger.error("Exception in housekeeping for queue: " + q.getName().toString(),e);
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(),
+ period/2,
+ period);
+ }
+ }
+
+ private void initialiseMessageStore(Configuration config) throws Exception
+ {
+ String messageStoreClass = config.getString("store.class");
+
+ Class clazz = Class.forName(messageStoreClass);
+ Object o = clazz.newInstance();
+
+ if (!(o instanceof MessageStore))
+ {
+ throw new ClassCastException("Message store class must implement " + MessageStore.class + ". Class " + clazz +
+ " does not.");
+ }
+ _messageStore = (MessageStore) o;
+ _messageStore.configure(this, "store", config);
+ }
+
+
+ public <T> T getConfiguredObject(Class<T> instanceType, Configuration config)
+ {
+ T instance;
+ try
+ {
+ instance = instanceType.newInstance();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor");
+ throw new IllegalArgumentException("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor", e);
+ }
+ Configurator.configure(instance);
+
+ return instance;
+ }
+
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public QueueRegistry getQueueRegistry()
+ {
+ return _queueRegistry;
+ }
+
+ public ExchangeRegistry getExchangeRegistry()
+ {
+ return _exchangeRegistry;
+ }
+
+ public ExchangeFactory getExchangeFactory()
+ {
+ return _exchangeFactory;
+ }
+
+ public ApplicationRegistry getApplicationRegistry()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public MessageStore getMessageStore()
+ {
+ return _messageStore;
+ }
+
+ public AuthenticationManager getAuthenticationManager()
+ {
+ return _authenticationManager;
+ }
+
+ public ACLPlugin getAccessManager()
+ {
+ return _accessManager;
+ }
+
+ public void close() throws Exception
+ {
+ if (_houseKeepingTimer != null)
+ {
+ _houseKeepingTimer.cancel();
+ }
+ if (_messageStore != null)
+ {
+ _messageStore.close();
+ }
+ }
+
+ public ManagedObject getBrokerMBean()
+ {
+ return _brokerMBean;
+ }
+
+ public ManagedObject getManagedObject()
+ {
+ return _virtualHostMBean;
+ }
+}
diff --git a/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java b/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java
new file mode 100644
index 0000000000..a0304a7b01
--- /dev/null
+++ b/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java
@@ -0,0 +1,128 @@
+package org.apache.qpid.server;
+
+import junit.framework.TestCase;
+import org.apache.qpid.server.filter.JMSSelectorFilter;
+import org.apache.qpid.AMQException;/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+public class SelectorParserTest extends TestCase
+{
+ public void testSelectorWithHyphen()
+ {
+ testPass("Cost = 2 AND \"property-with-hyphen\" = 'wibble'");
+ }
+
+ public void testLike()
+ {
+ testFail("Cost LIKE 2");
+ testPass("Cost LIKE 'Hello'");
+ }
+
+ public void testStringQuoted()
+ {
+ testPass("string = 'Test'");
+ }
+
+ public void testProperty()
+ {
+ testPass("prop1 = prop2");
+ }
+
+ public void testPropertyNames()
+ {
+ testPass("$min= TRUE AND _max= FALSE AND Prop_2 = true AND prop$3 = false");
+ }
+
+ public void testProtected()
+ {
+ testFail("NULL = 0 ");
+ testFail("TRUE = 0 ");
+ testFail("FALSE = 0 ");
+ testFail("NOT = 0 ");
+ testFail("AND = 0 ");
+ testFail("OR = 0 ");
+ testFail("BETWEEN = 0 ");
+ testFail("LIKE = 0 ");
+ testFail("IN = 0 ");
+ testFail("IS = 0 ");
+ testFail("ESCAPE = 0 ");
+ }
+
+
+ public void testBoolean()
+ {
+ testPass("min= TRUE AND max= FALSE ");
+ testPass("min= true AND max= false");
+ }
+
+ public void testDouble()
+ {
+ testPass("positive=31E2 AND negative=-31.4E3");
+ testPass("min=" + Double.MIN_VALUE + " AND max=" + Double.MAX_VALUE);
+ }
+
+ public void testLong()
+ {
+ testPass("minLong=" + Long.MIN_VALUE + "L AND maxLong=" + Long.MAX_VALUE + "L");
+ }
+
+ public void testInt()
+ {
+ testPass("minInt=" + Integer.MIN_VALUE + " AND maxInt=" + Integer.MAX_VALUE);
+ }
+
+ public void testSigned()
+ {
+ testPass("negative=-42 AND positive=+42");
+ }
+
+ public void testOctal()
+ {
+ testPass("octal=042");
+ }
+
+
+ private void testPass(String selector)
+ {
+ try
+ {
+ new JMSSelectorFilter(selector);
+ }
+ catch (AMQException e)
+ {
+ fail("Selector '" + selector + "' was not parsed :" + e.getMessage());
+ }
+ }
+
+ private void testFail(String selector)
+ {
+ try
+ {
+ new JMSSelectorFilter(selector);
+ fail("Selector '" + selector + "' was parsed ");
+ }
+ catch (AMQException e)
+ {
+ //normal path
+ }
+ }
+
+}
diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java
index 8e5879a51e..7e2d56b460 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java
@@ -589,6 +589,11 @@ public class DestWildExchangeTest extends TestCase
return null;
}
+ public void setExchange(AMQShortString exchange)
+ {
+
+ }
+
public boolean isImmediate()
{
return false;
diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
index 81b0ae2213..ed79384d42 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
@@ -173,7 +173,7 @@ public class AMQQueueAlertTest extends TestCase
public void testQueueDepthAlertWithSubscribers() throws Exception
{
protocolSession = new TestMinaProtocolSession();
- AMQChannel channel = new AMQChannel(protocolSession, 2, _messageStore, null);
+ AMQChannel channel = new AMQChannel(protocolSession, 2, _messageStore);
protocolSession.addChannel(channel);
// Create queue
@@ -242,6 +242,11 @@ public class AMQQueueAlertTest 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;
diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
index d86c90bdae..c02b47e9fd 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
@@ -123,7 +123,7 @@ public class AMQQueueMBeanTest extends TestCase
TestMinaProtocolSession protocolSession = new TestMinaProtocolSession();
- AMQChannel channel = new AMQChannel(protocolSession, 1, _messageStore, null);
+ AMQChannel channel = new AMQChannel(protocolSession, 1, _messageStore);
protocolSession.addChannel(channel);
_queue.registerProtocolSession(protocolSession, 1, new AMQShortString("test"), false, null, false, false);
@@ -234,6 +234,11 @@ public class AMQQueueMBeanTest 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;
diff --git a/java/client-java14/pom.xml b/java/client-java14/pom.xml
index 1dd02d142b..ebbd7b2228 100644
--- a/java/client-java14/pom.xml
+++ b/java/client-java14/pom.xml
@@ -1,224 +1,224 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT 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">
-
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-client-java14</artifactId>
- <packaging>jar</packaging>
- <version>1.0-incubating-M2.1-SNAPSHOT</version>
- <name>Qpid Client for Java 1.4</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>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <properties>
- <topDirectoryLocation>..</topDirectoryLocation>
- <java.source.version>1.4</java.source.version>
- <qpid.version>${pom.version}</qpid.version>
- <qpid.targetDir>${project.build.directory}</qpid.targetDir>
- <!--<qpid.root>${basedir}/..</qpid.root>-->
- <sasl.properties>${basedir}/etc/sasl.properties</sasl.properties>
-
- <!-- This is a dummy value to ensure this property exists, override in your settings.xml to your real 1.4 jdk location to run tests. -->
- <jvm.1.4.bin>path/to/java1.4</jvm.1.4.bin>
- </properties>
-
- <dependencies>
-
- <!-- These dependencies have to be re-declared here, because exluding the normal (non 1.4) client and common from the distribution takes out
- these transitive dependencies too. -->
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.apache.mina</groupId>
- <artifactId>mina-core</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.apache.geronimo.specs</groupId>
- <artifactId>geronimo-jms_1.1_spec</artifactId>
- </dependency>
-
- <dependency>
- <groupId>commons-collections</groupId>
- <artifactId>commons-collections</artifactId>
- </dependency>
-
- <dependency>
- <groupId>commons-lang</groupId>
- <artifactId>commons-lang</artifactId>
- </dependency>
-
- <!-- Use the java 1.4 retrotranslated client. -->
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-client</artifactId>
- <type>jar</type>
- <version>${pom.version}</version>
- <classifier>java14</classifier>
- </dependency>
-
- <!-- Use the java 1.4 retrotranslated common library. -->
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-common</artifactId>
- <type>jar</type>
- <version>${pom.version}</version>
- <classifier>java14</classifier>
- </dependency>
-
- <!-- Use the java 1.4 retrotranslated integration tests. -->
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-integrationtests</artifactId>
- <type>jar</type>
- <version>${pom.version}</version>
- <classifier>java14</classifier>
- <!--<scope>test</scope>-->
- </dependency>
-
- <dependency>
- <groupId>net.sf.retrotranslator</groupId>
- <artifactId>retrotranslator-runtime</artifactId>
- <scope>package</scope>
- </dependency>
-
- <!-- Test dependencies. -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
-
- <!-- Sets up the compiler plugin to compile on 1.4 -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>${java.source.version}</source>
- <target>${java.source.version}</target>
- </configuration>
- </plugin>
-
- <!-- Sets up the assembly plugin to use the assembly directions to build a 1.4 compatable client. -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-assembly-plugin</artifactId>
- <version>${assembly.version}</version>
-
- <executions>
-
- <!-- Produces the distribution. -->
- <execution>
- <id>assembly-dist</id>
- <phase>package</phase>
- <goals>
- <goal>attached</goal>
- </goals>
- <configuration>
- <descriptors>
- <descriptor>src/main/assembly/client-java14-bin.xml</descriptor>
- </descriptors>
- <finalName>qpid-${pom.version}</finalName>
- <outputDirectory>${qpid.targetDir}</outputDirectory>
- <tarLongFileMode>gnu</tarLongFileMode>
- </configuration>
- </execution>
-
- <!-- Produces a jar with all test dependencies in it. For convenience in running tests from command line. -->
- <!-- Todo: Replace this with a manifest only jar, its much quicker to build that. -->
- <execution>
- <id>assembly-alltestdeps</id>
- <phase>package</phase>
- <goals>
- <goal>attached</goal>
- </goals>
- <configuration>
- <descriptors>
- <descriptor>src/main/assembly/jar-with-dependencies.xml</descriptor>
- </descriptors>
- <outputDirectory>target</outputDirectory>
- <workDirectory>target/assembly/work</workDirectory>
- </configuration>
- </execution>
-
- </executions>
- </plugin>
-
- <!-- Sets up surefire to run during the integration-test phase instead of the test phase. -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <skip>true</skip>
- </configuration>
- <executions>
- <execution>
- <id>surefire-it</id>
- <phase>integration-test</phase>
- <goals>
- <goal>test</goal>
- </goals>
- <configuration>
- <skip>true</skip>
- <forkMode>once</forkMode>
- <jvm>${jvm.1.4.bin}</jvm>
- <systemProperties>
- <property>
- <name>amqj.logging.level</name>
- <value>${amqj.logging.level}</value>
- </property>
- <property>
- <name>log4j.configuration</name>
- <value>${log4j.configuration}</value>
- </property>
- <property>
- <name>amq.dynamicsaslregistrar.properties</name>
- <value>${sasl.properties}</value>
- </property>
- </systemProperties>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- </plugins>
- </build>
-</project>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client-java14</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <name>Qpid Client for Java 1.4</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>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <java.source.version>1.4</java.source.version>
+ <qpid.version>${pom.version}</qpid.version>
+ <qpid.targetDir>${project.build.directory}</qpid.targetDir>
+ <!--<qpid.root>${basedir}/..</qpid.root>-->
+ <sasl.properties>${basedir}/etc/sasl.properties</sasl.properties>
+
+ <!-- This is a dummy value to ensure this property exists, override in your settings.xml to your real 1.4 jdk location to run tests. -->
+ <jvm.1.4.bin>path/to/java1.4</jvm.1.4.bin>
+ </properties>
+
+ <dependencies>
+
+ <!-- These dependencies have to be re-declared here, because exluding the normal (non 1.4) client and common from the distribution takes out
+ these transitive dependencies too. -->
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jms_1.1_spec</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+
+ <!-- Use the java 1.4 retrotranslated client. -->
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client</artifactId>
+ <type>jar</type>
+ <version>${pom.version}</version>
+ <classifier>java14</classifier>
+ </dependency>
+
+ <!-- Use the java 1.4 retrotranslated common library. -->
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-common</artifactId>
+ <type>jar</type>
+ <version>${pom.version}</version>
+ <classifier>java14</classifier>
+ </dependency>
+
+ <!-- Use the java 1.4 retrotranslated integration tests. -->
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-integrationtests</artifactId>
+ <type>jar</type>
+ <version>${pom.version}</version>
+ <classifier>java14</classifier>
+ <!--<scope>test</scope>-->
+ </dependency>
+
+ <dependency>
+ <groupId>net.sf.retrotranslator</groupId>
+ <artifactId>retrotranslator-runtime</artifactId>
+ <scope>package</scope>
+ </dependency>
+
+ <!-- Test dependencies. -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <!-- Sets up the compiler plugin to compile on 1.4 -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${java.source.version}</source>
+ <target>${java.source.version}</target>
+ </configuration>
+ </plugin>
+
+ <!-- Sets up the assembly plugin to use the assembly directions to build a 1.4 compatable client. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>${assembly.version}</version>
+
+ <executions>
+
+ <!-- Produces the distribution. -->
+ <execution>
+ <id>assembly-dist</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attached</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/client-java14-bin.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ <outputDirectory>${qpid.targetDir}</outputDirectory>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </execution>
+
+ <!-- Produces a jar with all test dependencies in it. For convenience in running tests from command line. -->
+ <!-- Todo: Replace this with a manifest only jar, its much quicker to build that. -->
+ <execution>
+ <id>assembly-alltestdeps</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attached</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/jar-with-dependencies.xml</descriptor>
+ </descriptors>
+ <outputDirectory>target</outputDirectory>
+ <workDirectory>target/assembly/work</workDirectory>
+ </configuration>
+ </execution>
+
+ </executions>
+ </plugin>
+
+ <!-- Sets up surefire to run during the integration-test phase instead of the test phase. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ <executions>
+ <execution>
+ <id>surefire-it</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <skip>true</skip>
+ <forkMode>once</forkMode>
+ <jvm>${jvm.1.4.bin}</jvm>
+ <systemProperties>
+ <property>
+ <name>amqj.logging.level</name>
+ <value>${amqj.logging.level}</value>
+ </property>
+ <property>
+ <name>log4j.configuration</name>
+ <value>${log4j.configuration}</value>
+ </property>
+ <property>
+ <name>amq.dynamicsaslregistrar.properties</name>
+ <value>${sasl.properties}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+</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 5fe9cbf528..82c7f67faf 100644
--- a/java/client/pom.xml
+++ b/java/client/pom.xml
@@ -79,7 +79,7 @@
</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/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..ab3bc28d83
--- /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$, $Date$
+ */
+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 b6fbb6c6bf..69ff7a2c19 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);
}
+ 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 c04380ba8c..f3e71d2035 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
@@ -57,8 +57,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"))
{
@@ -156,7 +157,10 @@ public class AMQBrokerDetails implements BrokerDetails
}
else
{
- setPort(port);
+ if (!_transport.equalsIgnoreCase(SOCKET))
+ {
+ setPort(port);
+ }
}
String queryString = connection.getQuery();
@@ -263,13 +267,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 39b3b80e74..4b8143cfb5 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
@@ -39,6 +39,7 @@ 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -72,6 +73,105 @@ import java.util.concurrent.atomic.AtomicInteger;
public class AMQConnection extends Closeable implements Connection, QueueConnection, TopicConnection, Referenceable
{
+ private 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);
private AtomicInteger _idFactory = new AtomicInteger(0);
@@ -101,7 +201,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
private AMQProtocolHandler _protocolHandler;
/** Maps from session id (Integer) to AMQSession instance */
- private final Map<Integer, AMQSession> _sessions = new LinkedHashMap<Integer, AMQSession>();
+ private final ChannelToSessionMap _sessions = new ChannelToSessionMap();
private String _clientName;
@@ -157,6 +257,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
private static final long DEFAULT_TIMEOUT = 1000 * 30;
private ProtocolVersion _protocolVersion;
+
/**
* @param broker brokerdetails
* @param username username
@@ -232,6 +333,26 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
public AMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException
{
+ 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,8 +409,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
try
{
makeBrokerConnection(_failoverPolicy.getNextBrokerDetails());
- lastException = null;
- _connected = true;
}
catch (Exception e)
{
@@ -316,8 +435,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)
{
@@ -395,20 +537,27 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
EnumSet.of(AMQState.CONNECTION_OPEN, AMQState.CONNECTION_CLOSED);
try
{
+
TransportConnection.getInstance(brokerDetail).connect(_protocolHandler, brokerDetail);
// this blocks until the connection has been set up or when an error
// has prevented the connection being set up
//_protocolHandler.attainState(AMQState.CONNECTION_OPEN);
AMQState state = _protocolHandler.attainState(openOrClosedStates);
- if(state == AMQState.CONNECTION_OPEN)
+ if (state == AMQState.CONNECTION_OPEN)
{
-
_failoverPolicy.attainedConnection();
// Again this should be changed to a suitable notify
_connected = true;
}
+ else if (state == AMQState.CONNECTION_CLOSED)
+ {
+ //We need to change protocol handler here as an error during the connect will not
+ // cause the StateManager to be replaced. So the state is out of sync on reconnect
+ // This occurs here when we need to re-negotiate protocol versions
+ _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_NOT_STARTED);
+ }
}
catch (AMQException e)
{
@@ -513,71 +662,71 @@ 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)
+ synchronized (_sessionCreationLock)
{
- checkNotClosed();
+ checkNotClosed();
- if (channelLimitReached())
- {
- throw new ChannelLimitReachedException(_maximumChannelCount);
- }
+ if (channelLimitReached())
+ {
+ throw new ChannelLimitReachedException(_maximumChannelCount);
+ }
- return new FailoverRetrySupport<org.apache.qpid.jms.Session, JMSException>(
- new FailoverProtectedOperation<org.apache.qpid.jms.Session, JMSException>()
- {
- public org.apache.qpid.jms.Session execute() throws JMSException, FailoverException
+ return new FailoverRetrySupport<org.apache.qpid.jms.Session, JMSException>(
+ new FailoverProtectedOperation<org.apache.qpid.jms.Session, JMSException>()
{
- int channelId = _idFactory.incrementAndGet();
-
- if (_logger.isDebugEnabled())
+ public org.apache.qpid.jms.Session execute() throws JMSException, FailoverException
{
- _logger.debug("Write channel open frame for channel id " + channelId);
- }
-
- // We must create the session and register it before actually sending the frame to the server to
- // open it, so that there is no window where we could receive data on the channel and not be set
- // up to handle it appropriately.
- AMQSession session =
- new AMQSession(AMQConnection.this, channelId, transacted, acknowledgeMode, prefetchHigh,
- prefetchLow);
- // _protocolHandler.addSessionByChannel(channelId, session);
- registerSession(channelId, session);
+ int channelId = _idFactory.incrementAndGet();
- boolean success = false;
- try
- {
- createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted);
- success = true;
- }
- catch (AMQException e)
- {
- JMSException jmse = new JMSException("Error creating session: " + e);
- jmse.setLinkedException(e);
- throw jmse;
- }
- finally
- {
- if (!success)
+ if (_logger.isDebugEnabled())
{
- deregisterSession(channelId);
+ _logger.debug("Write channel open frame for channel id " + channelId);
}
- }
- if (_started)
- {
+ // We must create the session and register it before actually sending the frame to the server to
+ // open it, so that there is no window where we could receive data on the channel and not be set
+ // up to handle it appropriately.
+ AMQSession session =
+ new AMQSession(AMQConnection.this, channelId, transacted, acknowledgeMode, prefetchHigh,
+ prefetchLow);
+ // _protocolHandler.addSessionByChannel(channelId, session);
+ registerSession(channelId, session);
+
+ boolean success = false;
try
{
- session.start();
+ createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted);
+ success = true;
}
catch (AMQException e)
{
- throw new JMSAMQException(e);
+ JMSException jmse = new JMSException("Error creating session: " + e);
+ jmse.setLinkedException(e);
+ throw jmse;
+ }
+ finally
+ {
+ if (!success)
+ {
+ deregisterSession(channelId);
+ }
}
- }
- return session;
- }
- }, this).execute();
+ if (_started)
+ {
+ try
+ {
+ session.start();
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException(e);
+ }
+ }
+
+ return session;
+ }
+ }, this).execute();
}
}
@@ -589,13 +738,13 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
// TODO: Be aware of possible changes to parameter order as versions change.
- _protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class);
+ _protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class);
- BasicQosBody basicQosBody = getProtocolHandler().getMethodRegistry().createBasicQosBody(0,prefetchHigh,false);
+ 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);
+ _protocolHandler.syncWrite(basicQosBody.generateFrame(channelId), BasicQosOkBody.class);
if (transacted)
{
@@ -720,10 +869,10 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
checkNotClosed();
if (!_started)
{
- 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();
@@ -783,17 +932,20 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
}
else
{
- synchronized (getFailoverMutex())
- {
if (!_closed.getAndSet(true))
{
- try
+
+ synchronized (getFailoverMutex())
{
- long startCloseTime = System.currentTimeMillis();
+ try
+ {
+ long startCloseTime = System.currentTimeMillis();
- _taskPool.shutdown();
closeAllSessions(null, timeout, startCloseTime);
+ //This MUST occur after we have successfully closed all Channels/Sessions
+ _taskPool.shutdown();
+
if (!_taskPool.isTerminated())
{
try
@@ -977,11 +1129,6 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
return _maximumFrameSize;
}
- public Map getSessions()
- {
- return _sessions;
- }
-
public String getUsername()
{
return _username;
@@ -1154,7 +1301,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
_logger.error("Throwable Received but no listener set: " + cause.getMessage());
}
- if (!(cause instanceof AMQUndeliveredException) && !(cause instanceof AMQAuthenticationException))
+ if (hardError(cause))
{
try
{
@@ -1178,6 +1325,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);
@@ -1202,6 +1359,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
// _protocolHandler.addSessionByChannel(s.getChannelId(), s);
reopenChannel(s.getChannelId(), s.getDefaultPrefetchHigh(), s.getDefaultPrefetchLow(), s.getTransacted());
s.resubscribe();
+ s.setFlowControl(true);
}
}
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 28e5992b26..a3cf39003d 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,39 +91,40 @@ 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();
+
+ public boolean hasMoreElements()
{
+ _logger.info("QB:hasMoreElements:" + (_nextMessage != null));
- Message _nextMessage = consumer.receive();
+ return (_nextMessage != null);
+ }
- public boolean hasMoreElements()
+ public Object nextElement()
+ {
+ Message msg = _nextMessage;
+ try
{
- _logger.info("QB:hasMoreElements:" + (_nextMessage != null));
+ _logger.info("QB:nextElement about to receive");
- return (_nextMessage != null);
+ _nextMessage = consumer.receive();
+ _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();
- _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 42f07f97f9..c3219e6564 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
@@ -39,10 +39,10 @@ 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.client.util.FlowControllingBlockingQueue;
+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.framing.amqp_8_0.MethodRegistry_8_0;
import org.apache.qpid.jms.Session;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.protocol.AMQMethodEvent;
@@ -78,10 +78,7 @@ import javax.jms.TopicSubscriber;
import javax.jms.TransactionRolledBackException;
import java.io.Serializable;
import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
@@ -107,6 +104,89 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public class AMQSession extends Closeable implements Session, QueueSession, TopicSession
{
+ private 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);
@@ -156,7 +236,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
private boolean _transacted;
/** Holds the sessions acknowledgement mode. */
- private int _acknowledgeMode;
+ private final int _acknowledgeMode;
/** Holds this session unique identifier, used to distinguish it from other sessions. */
private int _channelId;
@@ -217,8 +297,10 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
* Maps from identifying tags to message consumers, in order to pass dispatch incoming messages to the right
* consumer.
*/
- private Map<AMQShortString, BasicMessageConsumer> _consumers =
- new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>();
+ private 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
@@ -281,6 +363,27 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
/** 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.
*
@@ -327,24 +430,20 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
{
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();
- }
+
}
});
}
@@ -495,14 +594,14 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
+ Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
}
- 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)
+ 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);
@@ -516,7 +615,6 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
final AMQFrame frame = body.generateFrame(getChannelId());
getProtocolHandler().syncWrite(frame, ChannelCloseOkBody.class, timeout);
-
// When control resumes at this point, a reply will have been received that
// indicates the broker has closed the channel successfully.
@@ -550,33 +648,44 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
*/
public void closed(Throwable e) throws JMSException
{
- synchronized (_connection.getFailoverMutex())
+ // 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 (e instanceof AMQDisconnectedException)
+ if (_dispatcher != null)
{
- if (_dispatcher != null)
- {
- // Failover failed and ain't coming back. Knife the dispatcher.
- _dispatcher.interrupt();
- }
+ // Failover failed and ain't coming back. Knife the dispatcher.
+ _dispatcher.interrupt();
}
- synchronized (_messageDeliveryLock)
+ }
+
+ 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("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);
+ }
}
}
}
@@ -662,16 +771,10 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
{
// 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
@@ -687,7 +790,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
_dispatcher.rejectPending(consumer);
}
- else
+ else // Queue Browser
{
// Just close the consumer
// fixme the CancelOK is being processed before the arriving messages..
@@ -695,13 +798,28 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
// 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
+ {
+ _queue.add(new UnprocessedMessage.CloseConsumerMessage(consumer));
+ }
+ }
}
}
- else
- {
- _logger.warn("Unable to confirm cancellation of consumer (" + consumerTag + "). Not found in consumer map.");
- }
-
}
public QueueBrowser createBrowser(Queue queue) throws JMSException
@@ -744,6 +862,16 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
false, false);
}
+
+ public MessageConsumer createExclusiveConsumer(Destination destination) throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, true, null, null,
+ false, false);
+ }
+
+
public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException
{
checkValidDestination(destination);
@@ -761,6 +889,17 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
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
{
@@ -925,7 +1064,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
{
checkNotClosed();
- return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic), topic);
+ return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic,false,false), topic);
}
public Queue createQueue(String queueName) throws JMSException
@@ -1089,9 +1228,10 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
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
*
@@ -1109,7 +1249,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
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 TemporaryQueue createTemporaryQueue() throws JMSException
@@ -1276,15 +1416,15 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
+ "] received in session with channel id " + _channelId);
}
- if (message.getDeliverBody() == null)
+ if (message.isDeliverMessage())
{
- // Return of the bounced message.
- returnBouncedMessage(message);
+ _highestDeliveryTag.set(message.getDeliverBody().getDeliveryTag());
+ _queue.add(message);
}
else
{
- _highestDeliveryTag.set(message.getDeliverBody().getDeliveryTag());
- _queue.add(message);
+ // Return of the bounced message.
+ returnBouncedMessage(message);
}
}
@@ -1393,9 +1533,9 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
public void rejectMessage(UnprocessedMessage message, boolean requeue)
{
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Rejecting Unacked message:" + message.getDeliverBody().getDeliveryTag());
+ _logger.debug("Rejecting Unacked message:" + message.getDeliverBody().getDeliveryTag());
}
rejectMessage(message.getDeliverBody().getDeliveryTag(), requeue);
@@ -1403,9 +1543,9 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
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);
@@ -1638,11 +1778,6 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
{
JMSException ex = new JMSException("Error registering consumer: " + e);
- if (_logger.isDebugEnabled())
- {
- e.printStackTrace();
- }
-
ex.setLinkedException(e);
throw ex;
}
@@ -1666,7 +1801,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
*/
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)
@@ -2063,8 +2198,9 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
private void consumeFromQueue(BasicMessageConsumer consumer, AMQShortString queueName,
AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector) throws AMQException, FailoverException
{
+ 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(_nextTag++));
+ AMQShortString tag = new AMQShortString(Integer.toString(tagId));
FieldTable arguments = FieldTableFactory.newFieldTable();
if ((messageSelector != null) && !messageSelector.equals(""))
@@ -2084,7 +2220,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
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
{
@@ -2112,7 +2248,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
catch (AMQException e)
{
// clean-up the map in the event of an error
- _consumers.remove(tag);
+ _consumers.remove(tagId);
throw e;
}
}
@@ -2148,6 +2284,73 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
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
+ {
+
+ 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;
+ }
+
+ }
+
+ return new FailoverNoopSupport<Long, AMQException>(
+ new FailoverProtectedOperation<Long, AMQException>()
+ {
+ public Long execute() 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;
+ }
+ }, _connection).execute();
+
+ }
+
+
+
/**
* Declares the named exchange and type of exchange.
*
@@ -2595,6 +2798,25 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
_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. */
private class Dispatcher extends Thread
{
@@ -2732,7 +2954,8 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
_lock.wait(2000);
}
- if (message.getDeliverBody().getDeliveryTag() <= _rollbackMark.get())
+ if (!(message instanceof UnprocessedMessage.CloseConsumerMessage)
+ && (message.getDeliverBody().getDeliveryTag() <= _rollbackMark.get()))
{
rejectMessage(message, true);
}
@@ -2786,10 +3009,11 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
private void dispatchMessage(UnprocessedMessage message)
{
- if (message.getDeliverBody() != null)
+ final BasicDeliverBody deliverBody = message.getDeliverBody();
+ if (deliverBody != null)
{
final BasicMessageConsumer consumer =
- (BasicMessageConsumer) _consumers.get(message.getDeliverBody().getConsumerTag());
+ _consumers.get(deliverBody.getConsumerTag().toIntValue());
if ((consumer == null) || consumer.isClosed())
{
@@ -2798,13 +3022,13 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
if (consumer == null)
{
_dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ")" + "["
- + message.getDeliverBody().getDeliveryTag() + "] from queue "
- + message.getDeliverBody().getConsumerTag() + " )without a handler - rejecting(requeue)...");
+ + deliverBody.getDeliveryTag() + "] from queue "
+ + deliverBody.getConsumerTag() + " )without a handler - rejecting(requeue)...");
}
else
{
_dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "["
- + message.getDeliverBody().getDeliveryTag() + "] from queue " + " consumer("
+ + deliverBody.getDeliveryTag() + "] from queue " + " consumer("
+ consumer.debugIdentity() + ") is closed rejecting(requeue)...");
}
}
@@ -2816,7 +3040,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
}
else
{
- consumer.notifyMessage(message, _channelId);
+ consumer.notifyMessage(message);
}
}
}
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 610e0109b1..efbce6033b 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
@@ -26,11 +26,7 @@ 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.AMQFrame;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.BasicCancelBody;
-import org.apache.qpid.framing.BasicCancelOkBody;
-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.slf4j.Logger;
@@ -42,6 +38,7 @@ import javax.jms.MessageListener;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
@@ -53,13 +50,13 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
private static final Logger _logger = LoggerFactory.getLogger(BasicMessageConsumer.class);
/** The connection being used by this consumer */
- private AMQConnection _connection;
+ private final AMQConnection _connection;
- private String _messageSelector;
+ private 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 */
private final AtomicBoolean _receiving = new AtomicBoolean(false);
@@ -70,7 +67,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
private AMQShortString _consumerTag;
/** We need to know the channel id when constructing frames */
- private int _channelId;
+ private final int _channelId;
/**
* Used in the blocking receive methods to receive a message from the Session thread. <p/> Or to notify of errors
@@ -78,45 +75,36 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
*/
private final ArrayBlockingQueue _synchronousQueue;
- private MessageFactoryRegistry _messageFactory;
+ private final MessageFactoryRegistry _messageFactory;
private final AMQSession _session;
- private AMQProtocolHandler _protocolHandler;
+ private 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;
-
- /** Number of messages unacknowledged in DUPS_OK_ACKNOWLEDGE mode */
- private int _outstanding;
-
- /**
- * Switch to enable sending of acknowledgements when using DUPS_OK_ACKNOWLEDGE mode. Enabled when _outstannding
- * number of msgs >= _prefetchHigh and disabled at < _prefetchLow
- */
- private boolean _dups_ok_acknowledge_send;
+ private final int _acknowledgeMode;
private ConcurrentLinkedQueue<Long> _unacknowledgedDeliveryTags = new ConcurrentLinkedQueue<Long>();
@@ -133,10 +121,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
* 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,
@@ -156,7 +143,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
_prefetchHigh = prefetchHigh;
_prefetchLow = prefetchLow;
_exclusive = exclusive;
- _acknowledgeMode = acknowledgeMode;
+
_synchronousQueue = new ArrayBlockingQueue(prefetchHigh, true);
_autoClose = autoClose;
_noConsume = noConsume;
@@ -166,6 +153,10 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
{
_acknowledgeMode = Session.NO_ACKNOWLEDGE;
}
+ else
+ {
+ _acknowledgeMode = acknowledgeMode;
+ }
}
public AMQDestination getDestination()
@@ -253,10 +244,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
switch (_acknowledgeMode)
{
- case Session.DUPS_OK_ACKNOWLEDGE:
- _logger.info("Recording tag for acking on close:" + msg.getDeliveryTag());
- _receivedDeliveryTags.add(msg.getDeliveryTag());
- break;
case Session.CLIENT_ACKNOWLEDGE:
_unacknowledgedDeliveryTags.add(msg.getDeliveryTag());
@@ -269,7 +256,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
}
else
{
- _logger.info("Recording tag for commit:" + msg.getDeliveryTag());
_receivedDeliveryTags.add(msg.getDeliveryTag());
}
@@ -284,8 +270,8 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
*
* @return boolean if the acquisition was successful
*
- * @throws JMSException
- * @throws InterruptedException
+ * @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
{
@@ -372,7 +358,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
}
catch (InterruptedException e)
{
- _logger.warn("Interrupted: " + e);
+ _logger.warn("Interrupted acquire: " + e);
if (isClosed())
{
return null;
@@ -383,11 +369,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
try
{
- if (closeOnAutoClose())
- {
- return null;
- }
-
Object o = null;
if (l > 0)
{
@@ -400,7 +381,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
}
catch (InterruptedException e)
{
- _logger.warn("Interrupted: " + e);
+ _logger.warn("Interrupted poll: " + e);
if (isClosed())
{
return null;
@@ -418,7 +399,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
}
catch (InterruptedException e)
{
- _logger.warn("Interrupted: " + e);
+ _logger.warn("Interrupted take: " + e);
if (isClosed())
{
return null;
@@ -440,20 +421,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
}
}
- private boolean closeOnAutoClose() throws JMSException
- {
- if (isAutoClose() && _closeWhenNoMessages && _synchronousQueue.isEmpty())
- {
- close(false);
-
- return true;
- }
- else
- {
- return false;
- }
- }
-
public Message receiveNoWait() throws JMSException
{
checkPreConditions();
@@ -482,11 +449,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
try
{
- if (closeOnAutoClose())
- {
- return null;
- }
-
Object o = _synchronousQueue.poll();
final AbstractJMSMessage m = returnMessageOrThrow(o);
if (m != null)
@@ -507,7 +469,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
* 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
*
@@ -527,6 +489,12 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
throw e;
}
+ else if (o instanceof UnprocessedMessage.CloseConsumerMessage)
+ {
+ _closed.set(true);
+ deregisterConsumer();
+ return null;
+ }
else
{
return (AbstractJMSMessage) o;
@@ -540,31 +508,30 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
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())
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ if (_closedStack != null)
{
- StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
- if (_closedStack != null)
- {
- _logger.trace(_consumerTag + " previously:" + _closedStack.toString());
- }
- else
- {
- _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1);
- }
+ _logger.debug(_consumerTag + " previously:" + _closedStack.toString());
+ }
+ else
+ {
+ _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1);
}
+ }
- if (sendClose)
+ if (sendClose)
+ {
+ // The Synchronized block only needs to protect network traffic.
+ synchronized (_connection.getFailoverMutex())
{
BasicCancelBody body = getSession().getMethodRegistry().createBasicCancelBody(_consumerTag, false);
@@ -578,7 +545,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
{
_logger.debug("CancelOk'd for consumer:" + debugIdentity());
}
-
}
catch (AMQException e)
{
@@ -589,24 +555,26 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
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();
- }
+ }
+ else
+ {
+ // //fixme this probably is not right
+ // if (!isNoConsume())
+ { // done in BasicCancelOK Handler but not sending one so just deregister.
+ deregisterConsumer();
}
+ }
- if ((_messageListener != null) && _receiving.get())
+ // 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())
{
- if (_logger.isInfoEnabled())
- {
- _logger.info("Interrupting thread: " + _receivingThread);
- }
-
- _receivingThread.interrupt();
+ _logger.info("Interrupting thread: " + _receivingThread);
}
+
+ _receivingThread.interrupt();
}
}
}
@@ -621,14 +589,14 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
{
_closed.set(true);
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (_closedStack != null)
{
- _logger.trace(_consumerTag + " markClosed():"
+ _logger.debug(_consumerTag + " markClosed():"
+ Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
- _logger.trace(_consumerTag + " previously:" + _closedStack.toString());
+ _logger.debug(_consumerTag + " previously:" + _closedStack.toString());
}
else
{
@@ -645,10 +613,15 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
* 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)
{
+ if (messageFrame instanceof UnprocessedMessage.CloseConsumerMessage)
+ {
+ notifyCloseMessage((UnprocessedMessage.CloseConsumerMessage) messageFrame);
+ return;
+ }
+
final boolean debug = _logger.isDebugEnabled();
if (debug)
@@ -658,10 +631,15 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
try
{
+ final BasicDeliverBody deliverBody = messageFrame.getDeliverBody();
+
AbstractJMSMessage jmsMessage =
- _messageFactory.createMessage(messageFrame.getDeliverBody().getDeliveryTag(),
- messageFrame.getDeliverBody().getRedelivered(), messageFrame.getDeliverBody().getExchange(),
- messageFrame.getDeliverBody().getRoutingKey(), messageFrame.getContentHeader(), messageFrame.getBodies());
+ _messageFactory.createMessage(deliverBody.getDeliveryTag(),
+ deliverBody.getRedelivered(),
+ deliverBody.getExchange(),
+ deliverBody.getRoutingKey(),
+ messageFrame.getContentHeader(),
+ messageFrame.getBodies());
if (debug)
{
@@ -673,11 +651,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
// if (!_closed.get())
{
- jmsMessage.setConsumer(this);
-
preDeliver(jmsMessage);
- notifyMessage(jmsMessage, channelId);
+ notifyMessage(jmsMessage);
}
// else
// {
@@ -700,11 +676,33 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
}
}
- /**
- * @param jmsMessage this message has already been processed so can't redo preDeliver
- * @param channelId
- */
- public void notifyMessage(AbstractJMSMessage jmsMessage, int channelId)
+ /** @param closeMessage this message signals that we should close the browser */
+ public void notifyCloseMessage(UnprocessedMessage.CloseConsumerMessage closeMessage)
+ {
+ if (isMessageListenerSet())
+ {
+ // Currently only possible to get this msg type with a browser.
+ // If we get the message here then we should probably just close this consumer.
+ // Though an AutoClose consumer with message listener is quite odd...
+ // Just log out the fact so we know where we are
+ _logger.warn("Using an AutoCloseconsumer with message listener is not supported.");
+ }
+ else
+ {
+ try
+ {
+ _synchronousQueue.put(closeMessage);
+ }
+ catch (InterruptedException e)
+ {
+ _logger.info(" SynchronousQueue.put interupted. Usually result of connection closing," +
+ "but we shouldn't have close yet");
+ }
+ }
+ }
+
+ /** @param jmsMessage this message has already been processed so can't redo preDeliver */
+ public void notifyMessage(AbstractJMSMessage jmsMessage)
{
try
{
@@ -773,28 +771,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
break;
case Session.DUPS_OK_ACKNOWLEDGE:
- /*( if (++_outstanding >= _prefetchHigh)
- {
- _dups_ok_acknowledge_send = true;
- }
-
- //Can't use <= as _prefetchHigh may equal _prefetchLow so no acking would occur.
- if (_outstanding < _prefetchLow)
- {
- _dups_ok_acknowledge_send = false;
- }
-
- if (_dups_ok_acknowledge_send)
- {
- if (!_session.isInRecovery())
- {
- _session.acknowledgeMessage(msg.getDeliveryTag(), true);
- _outstanding = 0;
- }
- }
-
- break;
- */
case Session.AUTO_ACKNOWLEDGE:
// we do not auto ack a message if the application code called recover()
if (!_session.isInRecovery())
@@ -845,14 +821,14 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
// synchronized (_closed)
{
_closed.set(true);
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (_closedStack != null)
{
- _logger.trace(_consumerTag + " notifyError():"
+ _logger.debug(_consumerTag + " notifyError():"
+ Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
- _logger.trace(_consumerTag + " previously" + _closedStack.toString());
+ _logger.debug(_consumerTag + " previously" + _closedStack.toString());
}
else
{
@@ -948,18 +924,6 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
return _noConsume;
}
- public void closeWhenNoMessages(boolean b)
- {
- _closeWhenNoMessages = b;
-
- if (_closeWhenNoMessages && _synchronousQueue.isEmpty() && _receiving.get() && (_messageListener != null))
- {
- _closed.set(true);
- _receivingThread.interrupt();
- }
-
- }
-
public void rollback()
{
clearUnackedMessages();
@@ -982,9 +946,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
if (tag != null)
{
- if (_logger.isTraceEnabled())
+ if (_logger.isDebugEnabled())
{
- _logger.trace("Rejecting tag from _receivedDTs:" + tag);
+ _logger.debug("Rejecting tag from _receivedDTs:" + tag);
}
_session.rejectMessage(tag, true);
@@ -1025,9 +989,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
{
_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();
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 7e96fb537c..ae71846870 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
@@ -538,6 +538,18 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j
frames[0] = publishFrame;
frames[1] = contentHeaderFrame;
CompositeAMQDataBlock compositeFrame = new CompositeAMQDataBlock(frames);
+
+ try
+ {
+ _session.checkFlowControl();
+ }
+ catch (InterruptedException e)
+ {
+ JMSException jmsEx = new JMSException("Interrupted while waiting for flow control to be removed");
+ jmsEx.setLinkedException(e);
+ throw jmsEx;
+ }
+
_protocolHandler.writeFrame(compositeFrame, wait);
if (message != origMessage)
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 60f95bfe33..a944ff6bec 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
@@ -120,13 +120,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
@@ -138,6 +142,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(
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/BasicDeliverMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
index 49c8a83833..d05e99d210 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
@@ -26,7 +26,6 @@ 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;
@@ -46,8 +45,8 @@ public class BasicDeliverMethodHandler implements StateAwareMethodListener<Basic
throws AMQException
{
final AMQProtocolSession session = stateManager.getProtocolSession();
- final UnprocessedMessage msg = new UnprocessedMessage(channelId, body);
+ final UnprocessedMessage msg = new UnprocessedMessage.UnprocessedDeliverMessage(body);
_logger.debug("New JmsDeliver method received");
- session.unprocessedMessageReceived(msg);
+ session.unprocessedMessageReceived(channelId, 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 428d366f07..2ebc9288c3 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
@@ -26,7 +26,6 @@ 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;
@@ -47,9 +46,9 @@ public void methodReceived(AMQStateManager stateManager, BasicReturnBody body, i
{
_logger.debug("New JmsBounce method received");
final AMQProtocolSession session = stateManager.getProtocolSession();
- final UnprocessedMessage msg = new UnprocessedMessage(channelId, body);
+ final UnprocessedMessage msg = new UnprocessedMessage.UnprocessedBouncedMessage(body);
- session.unprocessedMessageReceived(msg);
+ session.unprocessedMessageReceived(channelId, 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 8c8814e9b7..a580a6466d 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
@@ -104,6 +104,11 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener<Chann
}
// fixme why is this only done when the close is expected...
// should the above forced closes not also cause a close?
+ // ----------
+ // 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/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/ConnectionCloseMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java
index 4d805cf123..fdcb493f38 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
@@ -73,10 +73,11 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener<Co
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());
+ // 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.
@@ -98,6 +99,8 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener<Co
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/message/AbstractJMSMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java
index b029770946..6d4c61fb29 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
@@ -55,8 +55,9 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
protected boolean _changedData;
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));
protected AbstractJMSMessage(ByteBuffer data)
{
@@ -72,8 +73,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,
@@ -121,7 +120,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();
@@ -301,7 +303,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");
}
@@ -311,7 +313,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");
}
@@ -321,7 +323,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");
}
@@ -331,7 +333,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");
}
@@ -341,7 +343,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");
}
@@ -351,7 +353,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");
}
@@ -361,7 +363,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");
}
@@ -371,7 +373,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");
}
@@ -381,7 +383,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");
}
@@ -392,19 +394,14 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
public String getStringProperty(String propertyName) throws JMSException
{
- if (propertyName.startsWith("JMSX"))
+ //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()))
{
- //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()))
- {
- return ((BasicContentHeaderProperties) _contentHeaderProperties).getUserIdAsString();
- }
-
- return null;
+ return ((BasicContentHeaderProperties) _contentHeaderProperties).getUserIdAsString();
}
else
{
- if (_strictAMQP)
+ if (STRICT_AMQP_COMPLIANCE)
{
throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
}
@@ -425,7 +422,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");
}
@@ -436,7 +433,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");
}
@@ -447,7 +444,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");
}
@@ -458,7 +455,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");
}
@@ -469,7 +466,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");
}
@@ -487,7 +484,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");
}
@@ -498,7 +495,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");
}
@@ -509,7 +506,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");
}
@@ -691,9 +688,4 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach
}
}
- public void setConsumer(BasicMessageConsumer basicMessageConsumer)
- {
- _consumer = basicMessageConsumer;
- }
-
}
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 a70acbabbe..d8fe964b85 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
@@ -55,7 +55,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, ContentHeaderBody contentHeader, AMQShortString exchange, AMQShortString routingKey,
@@ -76,7 +80,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/UnprocessedMessage.java b/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java
index 5b199f2478..18157adc34 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
@@ -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
@@ -24,10 +24,20 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+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;
/**
* This class contains everything needed to process a JMS message. It assembles the deliver body, the content header and
@@ -36,33 +46,15 @@ import org.apache.qpid.framing.ContentHeaderBody;
* Note that the actual work of creating a JMS message for the client code's use is done outside of the MINA dispatcher
* thread in order to minimise the amount of work done in the MINA dispatcher thread.
*/
-public class UnprocessedMessage
+public abstract class UnprocessedMessage
{
- private long _bytesReceived = 0;
+ private long _bytesReceived = 0L;
- private final BasicDeliverBody _deliverBody;
- private final BasicReturnBody _bounceBody; // TODO: check change (gustavo)
- private final int _channelId;
private ContentHeaderBody _contentHeader;
/** List of ContentBody instances. Due to fragmentation you don't know how big this will be in general */
private List<ContentBody> _bodies;
- public UnprocessedMessage(int channelId, BasicDeliverBody deliverBody)
- {
- _deliverBody = deliverBody;
- _channelId = channelId;
- _bounceBody = null;
- }
-
-
- public UnprocessedMessage(int channelId, BasicReturnBody bounceBody)
- {
- _deliverBody = null;
- _channelId = channelId;
- _bounceBody = bounceBody;
- }
-
public void receiveBody(ContentBody body) //throws UnexpectedBodyReceivedException
{
@@ -96,22 +88,11 @@ public class UnprocessedMessage
return _bytesReceived == getContentHeader().bodySize;
}
- public BasicDeliverBody getDeliverBody()
- {
- return _deliverBody;
- }
-
- public BasicReturnBody getBounceBody()
- {
- return _bounceBody;
- }
- public int getChannelId()
- {
- return _channelId;
- }
+ abstract public BasicDeliverBody getDeliverBody();
+ abstract public BasicReturnBody getBounceBody();
public ContentHeaderBody getContentHeader()
{
@@ -128,4 +109,188 @@ public class UnprocessedMessage
return _bodies;
}
+ abstract public boolean isDeliverMessage();
+
+ public static final class UnprocessedDeliverMessage extends UnprocessedMessage
+ {
+ private final BasicDeliverBody _body;
+
+ public UnprocessedDeliverMessage(final BasicDeliverBody body)
+ {
+ _body = body;
+ }
+
+
+ public BasicDeliverBody getDeliverBody()
+ {
+ return _body;
+ }
+
+ public BasicReturnBody getBounceBody()
+ {
+ return null;
+ }
+
+ public boolean isDeliverMessage()
+ {
+ return true;
+ }
+ }
+
+ public static final class UnprocessedBouncedMessage extends UnprocessedMessage
+ {
+ private final BasicReturnBody _body;
+
+ public UnprocessedBouncedMessage(final BasicReturnBody body)
+ {
+ _body = body;
+ }
+
+
+ public BasicDeliverBody getDeliverBody()
+ {
+ return null;
+ }
+
+ public BasicReturnBody getBounceBody()
+ {
+ return _body;
+ }
+
+ public boolean isDeliverMessage()
+ {
+ return false;
+ }
+ }
+
+ public static final class CloseConsumerMessage extends UnprocessedMessage
+ {
+ BasicMessageConsumer _consumer;
+
+ public CloseConsumerMessage(BasicMessageConsumer consumer)
+ {
+ _consumer = consumer;
+ }
+
+ public BasicDeliverBody getDeliverBody()
+ {
+ return new BasicDeliverBody()
+ {
+ // This is the only thing we need to preserve so the correct consumer can be found later.
+ public AMQShortString getConsumerTag()
+ {
+ return _consumer.getConsumerTag();
+ }
+
+ // The Rest of these methods are not used
+ public long getDeliveryTag()
+ {
+ return 0;
+ }
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean getRedelivered()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+
+ public byte getMajor()
+ {
+ return 0;
+ }
+
+ public byte getMinor()
+ {
+ return 0;
+ }
+
+ public int getClazz()
+ {
+ return 0;
+ }
+
+ public int getMethod()
+ {
+ return 0;
+ }
+
+ public void writeMethodPayload(ByteBuffer buffer)
+ {
+ }
+
+ public byte getFrameType()
+ {
+ return 0;
+ }
+
+ public int getSize()
+ {
+ return 0;
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ }
+
+ public void handle(int channelId, AMQVersionAwareProtocolSession amqMinaProtocolSession) throws AMQException
+ {
+ }
+
+ public AMQFrame generateFrame(int channelId)
+ {
+ return null;
+ }
+
+ public AMQChannelException getChannelNotFoundException(int channelId)
+ {
+ return null;
+ }
+
+ public AMQChannelException getChannelException(AMQConstant code, String message)
+ {
+ return null;
+ }
+
+ public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause)
+ {
+ return null;
+ }
+
+ public AMQConnectionException getConnectionException(AMQConstant code, String message)
+ {
+ return null;
+ }
+
+ public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause)
+ {
+ return null;
+ }
+
+ public boolean execute(MethodDispatcher methodDispatcher, int channelId) throws AMQException
+ {
+ return false;
+ }
+ };
+ }
+
+ public BasicReturnBody getBounceBody()
+ {
+ return null;
+ }
+
+ public boolean isDeliverMessage()
+ {
+ return false;
+ }
+ }
+
}
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 8a1e78d2e0..3932b098cd 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
@@ -30,7 +30,6 @@ 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.mina.transport.socket.nio.SocketSessionConfig;
import org.apache.qpid.AMQConnectionClosedException;
import org.apache.qpid.AMQDisconnectedException;
import org.apache.qpid.AMQException;
@@ -55,6 +54,7 @@ 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;
@@ -153,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.
*
@@ -209,29 +219,24 @@ public class AMQProtocolHandler extends IoHandlerAdapter
}
catch (RuntimeException e)
{
- e.printStackTrace();
+ _logger.error(e.getMessage(), e);
}
- if (!System.getProperties().containsKey("protectio") || Boolean.getBoolean("protectio"))
+ if (Boolean.getBoolean("protectio"))
{
try
{
//Add IO Protection Filters
IoFilterChain chain = session.getFilterChain();
- int buf_size = 32768;
- if (session.getConfig() instanceof SocketSessionConfig)
- {
- buf_size = ((SocketSessionConfig) session.getConfig()).getReceiveBufferSize();
- }
session.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter());
ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
- readfilter.setMaximumConnectionBufferSize(buf_size);
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER_LIMIT)));
readfilter.attach(chain);
WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
- writefilter.setMaximumConnectionBufferSize(buf_size * 2);
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER_LIMIT)));
writefilter.attach(chain);
session.getFilterChain().remove("tempExecutorFilterForFilterBuilder");
@@ -355,7 +360,7 @@ 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
@@ -372,8 +377,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter
AMQException amqe = new AMQException("Protocol handler error: " + cause, cause);
propagateExceptionToWaiters(amqe);
- _connection.exceptionReceived(cause);
}
+ _connection.exceptionReceived(cause);
}
@@ -406,7 +411,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter
*/
public void propagateExceptionToWaiters(Exception e)
{
- getStateManager().error(e);
+
if (!_frameListeners.isEmpty())
{
final Iterator it = _frameListeners.iterator();
@@ -418,6 +423,24 @@ public class AMQProtocolHandler extends IoHandlerAdapter
}
}
+ public void notifyFailoverStarting()
+ {
+ // 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)
+ {
+ _lastFailoverException = new FailoverException("Failing over about to start");
+ }
+
+ propagateExceptionToWaiters(_lastFailoverException);
+ }
+
+ public void failoverInProgress()
+ {
+ _lastFailoverException = null;
+ }
+
private static int _messageReceivedCount;
public void messageReceived(IoSession session, Object message) throws Exception
@@ -438,78 +461,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter
HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody);
- switch (bodyFrame.getFrameType())
- {
- case AMQMethodBody.TYPE:
-
- if (debug)
- {
- _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + frame);
- }
-
- final AMQMethodEvent<AMQMethodBody> evt =
- new AMQMethodEvent<AMQMethodBody>(frame.getChannel(), (AMQMethodBody) bodyFrame);
-
- try
- {
-
- 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("AMQMethodEvent " + evt + " was not processed by any listener. Listeners:"
- + _frameListeners);
- }
- }
- 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);
- }
-
- break;
-
- case ContentHeaderBody.TYPE:
-
- _protocolSession.messageContentHeaderReceived(frame.getChannel(), (ContentHeaderBody) bodyFrame);
- break;
-
- case ContentBody.TYPE:
-
- _protocolSession.messageContentBodyReceived(frame.getChannel(), (ContentBody) bodyFrame);
- break;
-
- case HeartbeatBody.TYPE:
-
- if (debug)
- {
- _logger.debug("Received heartbeat");
- }
-
- break;
-
- default:
+ bodyFrame.handle(frame.getChannel(),_protocolSession);
- }
_connection.bytesReceived(_protocolSession.getIoSession().getReadBytes());
}
@@ -527,6 +480,57 @@ public class AMQProtocolHandler extends IoHandlerAdapter
}
}
+ public void methodBodyReceived(final int channelId, final AMQBody bodyFrame, IoSession session)//, final IoSession session)
+ throws AMQException
+ {
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + bodyFrame);
+ }
+
+ final AMQMethodEvent<AMQMethodBody> evt =
+ new AMQMethodEvent<AMQMethodBody>(channelId, (AMQMethodBody) bodyFrame);
+
+ try
+ {
+
+ 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())
+ {
+ final AMQMethodListener listener = (AMQMethodListener) it.next();
+ wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested;
+ }
+ }
+
+ if (!wasAnyoneInterested)
+ {
+ throw new AMQException("AMQMethodEvent " + evt + " was not processed by any listener. Listeners:"
+ + _frameListeners);
+ }
+ }
+ 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);
+ }
+
+ }
+
private static int _messagesOut;
public void messageSent(IoSession session, Object message) throws Exception
@@ -612,7 +616,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);
@@ -621,10 +633,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
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 b48adbdb08..6a5cc62bfc 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
@@ -74,8 +74,6 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
*/
protected final AMQProtocolHandler _protocolHandler;
- /** Maps from the channel id to the AMQSession that it represents. */
- protected ConcurrentMap<Integer, AMQSession> _channelId2SessionMap = new ConcurrentHashMap<Integer, AMQSession>();
protected ConcurrentMap _closingChannels = new ConcurrentHashMap();
@@ -83,7 +81,8 @@ 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 _channelId2UnprocessedMsgMap = new ConcurrentHashMap();
+ 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;
@@ -101,7 +100,8 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
private MethodDispatcher _methodDispatcher;
- private final AMQConnection _connection;
+ private final AMQConnection _connection;
+ private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0;
public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection)
{
@@ -230,14 +230,24 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
*
* @throws AMQException if this was not expected
*/
- public void unprocessedMessageReceived(UnprocessedMessage message) throws AMQException
+ public void unprocessedMessageReceived(final int channelId, UnprocessedMessage message) throws AMQException
{
- _channelId2UnprocessedMsgMap.put(message.getChannelId(), message);
+ 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 msg = (UnprocessedMessage) _channelId2UnprocessedMsgMap.get(channelId);
+ final UnprocessedMessage msg = (channelId & FAST_CHANNEL_ACCESS_MASK) == 0 ? _channelId2UnprocessedMsgArray[channelId]
+ : _channelId2UnprocessedMsgMap.get(channelId);
+
+
if (msg == null)
{
throw new AMQException("Error: received content header without having received a BasicDeliver frame first");
@@ -256,9 +266,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 msg = (UnprocessedMessage) _channelId2UnprocessedMsgMap.get(channelId);
+ UnprocessedMessage msg;
+ final boolean fastAccess = (channelId & FAST_CHANNEL_ACCESS_MASK) == 0;
+ if(fastAccess)
+ {
+ msg = _channelId2UnprocessedMsgArray[channelId];
+ }
+ else
+ {
+ msg = _channelId2UnprocessedMsgMap.get(channelId);
+ }
+
if (msg == null)
{
throw new AMQException("Error: received content body without having received a JMSDeliver frame first");
@@ -266,7 +286,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("Error: received content body without having received a ContentHeader frame first");
}
@@ -286,6 +313,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
*
@@ -296,7 +328,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)
@@ -486,4 +525,15 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
{
_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/AMQStateManager.java b/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
index b6baefe1b0..2e6a4beb83 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
@@ -37,7 +37,7 @@ 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);
@@ -52,9 +52,9 @@ public class AMQStateManager implements AMQMethodListener
* AMQFrame.
*/
- 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()
{
@@ -91,19 +91,6 @@ 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
{
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 e8a220f5e9..7ae2ddf66c 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,7 @@ 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;
@@ -36,6 +37,9 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
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);
}
- if (transport == _currentInstance)
+ /* if (transport == _currentInstance)
{
if (transport == VM)
{
@@ -83,19 +100,29 @@ 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()
{
SocketConnector result;
// FIXME - this needs to be sorted to use the new Mina MultiThread SA.
- if (!System.getProperties().containsKey("qpidnio") || Boolean.getBoolean("qpidnio"))
+ if (Boolean.getBoolean("qpidnio"))
{
_logger.warn("Using Qpid MultiThreaded NIO - " + (System.getProperties().containsKey("qpidnio")
? "Qpid NIO is new default"
@@ -117,16 +144,23 @@ public class TransportConnection
break;
case VM:
{
- _instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker"));
+ instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker"));
break;
}
+ default:
+ throw new AMQNoTransportForProtocolException(details);
}
- return _instance;
+ return instance;
}
private static int getTransport(String transport)
{
+ if (transport.equals(BrokerDetails.SOCKET))
+ {
+ return SOCKET;
+ }
+
if (transport.equals(BrokerDetails.TCP))
{
return TCP;
@@ -283,11 +317,14 @@ public class TransportConnection
public static void killAllVMBrokers()
{
_logger.info("Killing all VM Brokers");
- _acceptor.unbindAll();
+ if (_acceptor != null)
+ {
+ _acceptor.unbindAll();
+ }
synchronized (_inVmPipeAddress)
{
_inVmPipeAddress.clear();
- }
+ }
_acceptor = null;
_currentInstance = -1;
_currentVMPort = -1;
@@ -302,6 +339,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/jms/BrokerDetails.java b/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
index 603b0834a3..8b353a7264 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
@@ -34,6 +34,7 @@ public interface BrokerDetails
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/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/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/test/unit/basic/PropertyValueTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
index d2a7ba301b..09886f4736 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
@@ -173,7 +173,7 @@ public class PropertyValueTest extends TestCase 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"));
@@ -292,8 +292,11 @@ public class PropertyValueTest extends TestCase implements MessageListener
((AMQMessage) m).getPropertyHeaders().containsKey("void"));
//JMSXUserID
- Assert.assertEquals("Check 'JMSXUserID' is supported ", USERNAME,
- m.getStringProperty("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 40c712c1c9..d05ed7ca73 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,18 +21,20 @@
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;
import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.BasicMessageProducer;
import org.apache.qpid.client.transport.TransportConnection;
-
+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;
@@ -46,12 +48,12 @@ public class SelectorTest extends TestCase 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();
TransportConnection.createVMBroker(1);
- init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
}
protected void tearDown() throws Exception
@@ -60,19 +62,19 @@ public class SelectorTest extends TestCase implements MessageListener
TransportConnection.killAllVMBrokers();
}
- 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);
@@ -80,13 +82,17 @@ public class SelectorTest extends TestCase implements MessageListener
_session.createConsumer(destination, selector).setMessageListener(this);
}
- public synchronized void test() throws JMSException, InterruptedException
+ public synchronized void test() throws JMSException, InterruptedException, URLSyntaxException, AMQException
{
try
{
+
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+
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);
@@ -106,10 +112,147 @@ public class SelectorTest extends TestCase 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
+ {
+ if (_session != null)
+ {
+ _session.close();
+ }
+ if (_connection != null)
+ {
+ _connection.close();
+ }
+ }
+ }
+
+
+ public void testInvalidSelectors()
+ {
+ Connection connection = null;
+
+ try
+ {
+ connection = new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test");
+ _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
{
- _session.close();
- _connection.close();
+ 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());
+ }
+ }
}
}
@@ -128,9 +271,29 @@ public class SelectorTest extends TestCase 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/client/connection/ConnectionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
index 56394fee27..4b4df7e5c8 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)
{
- fail("Correct exception not thrown. Excpected 'AMQAuthenticationException' got: " + amqe);
+ 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)
+ {
+ 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
@@ -178,13 +240,27 @@ public class ConnectionTest extends TestCase
{
// PASS
}
+ finally
+ {
+ if (connection != null)
+ {
+ connection.close();
+ }
+ }
}
public void testClientIdIsPopulatedAutomatically() throws Exception
{
Connection connection = new AMQConnection(_broker, "guest", "guest",
null, "test");
- assertNotNull(connection.getClientID());
+ try
+ {
+ assertNotNull(connection.getClientID());
+ }
+ finally
+ {
+ connection.close();
+ }
}
public static junit.framework.Test suite()
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 6c872a0e10..d90873a6a7 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
@@ -510,6 +510,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/close/CloseBeforeAckTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java
index 5a61480f6a..ee110e7932 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.client.transport.TransportConnection;
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/message/JMSPropertiesTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
index 3012909daa..f2655adc98 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
@@ -38,6 +38,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
@@ -85,6 +86,12 @@ public class JMSPropertiesTest extends TestCase
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);
@@ -101,6 +108,30 @@ public class JMSPropertiesTest extends TestCase
// 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/cluster/doc/design.doc b/java/cluster/doc/design.doc
deleted file mode 100644
index c5bbf0f8a4..0000000000
--- a/java/cluster/doc/design.doc
+++ /dev/null
Binary files differ
diff --git a/java/cluster/pom.xml b/java/cluster/pom.xml
deleted file mode 100644
index 73c62520e6..0000000000
--- a/java/cluster/pom.xml
+++ /dev/null
@@ -1,69 +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.
--->
-<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">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-cluster</artifactId>
- <packaging>jar</packaging>
- <version>1.0-incubating-M2.1-SNAPSHOT</version>
- <name>Qpid Cluster</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>
- </parent>
-
- <properties>
- <topDirectoryLocation>..</topDirectoryLocation>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-client</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-broker</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <skip>true</skip>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java
deleted file mode 100644
index 951bd22df0..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQBody;
-
-/**
- * AMQUnexpectedBodyTypeException represents a failure where a message body does not match its expected type. For example,
- * and AMQP method should have a method body.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represents a failure where a message body does not match its expected type.
- * </table>
- *
- * @todo Not an AMQP exception as no status code.
- *
- * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would
- * be better just to leave that as a ClassCastException. Check that the framing layer will pick up the error first.
- */
-public class AMQUnexpectedBodyTypeException extends AMQException
-{
- public AMQUnexpectedBodyTypeException(Class<? extends AMQBody> expectedClass, AMQBody body)
- {
- super("Unexpected body type. Expected: " + expectedClass.getName() + "; got: " + body.getClass().getName());
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java
deleted file mode 100644
index 39508df566..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.framing.AMQMethodBody;
-
-public class BlockingHandler implements ResponseHandler
-{
- private final Class _expected;
- private boolean _completed;
- private AMQMethodBody _response;
-
-
- public BlockingHandler()
- {
- this(AMQMethodBody.class);
- }
-
- public BlockingHandler(Class<? extends AMQMethodBody> expected)
- {
- _expected = expected;
- }
-
- public void responded(AMQMethodBody response)
- {
- if (_expected.isInstance(response))
- {
- _response = response;
- completed();
- }
- }
-
- public void removed()
- {
- completed();
- }
-
- private synchronized void completed()
- {
- _completed = true;
- notifyAll();
- }
-
- synchronized void waitForCompletion()
- {
- while (!_completed)
- {
- try
- {
- wait();
- }
- catch (InterruptedException ignore)
- {
-
- }
- }
- }
-
- AMQMethodBody getResponse()
- {
- return _response;
- }
-
- boolean failed()
- {
- return _response == null;
- }
-
- boolean isCompleted()
- {
- return _completed;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java
deleted file mode 100644
index 145aa58574..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-public interface BroadcastPolicy
-{
- public boolean isComplete(int responded, int members);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java
deleted file mode 100644
index 7e2cf6da83..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.log4j.Logger;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * An implementation of the Member interface (through which data is sent to other
- * peers in the cluster). This class provides a base from which subclasses can
- * inherit some common behaviour for broadcasting GroupRequests and sending methods
- * that may expect a response. It also extends the Member abstraction to support
- * a richer set of operations that are useful within the package but should not be
- * exposed outside of it.
- *
- */
-abstract class Broker extends SimpleMemberHandle implements Member
-{
- private static final Logger _logger = Logger.getLogger(Broker.class);
- private static final int DEFAULT_CHANNEL = 1;
- private static final int START_CHANNEL = 2;
- private static final int END_CHANNEL = 10000;
-
-
- private MemberFailureListener _listener;
- //a wrap-around counter to allocate _requests a unique channel:
- private int _nextChannel = START_CHANNEL;
- //outstanding _requests:
- private final Map<Integer, ResponseHandler> _requests = new HashMap<Integer, ResponseHandler>();
-
- Broker(String host, int port)
- {
- super(host, port);
- }
-
- /**
- * Allows a listener to be registered that will receive callbacks when communication
- * to the peer this broker instance represents fails.
- * @param listener the callback to be notified of failures
- */
- public void addFailureListener(MemberFailureListener listener)
- {
- _listener = listener;
- }
-
- /**
- * Allows subclasses to signal comunication failures
- */
- protected void failed()
- {
- if (_listener != null)
- {
- _listener.failed(this);
- }
- }
-
- /**
- * Subclasses should call this on receiving message responses from the remote
- * peer. They are matched to any outstanding request they might be response
- * to, with the completion and callback of that request being managed if
- * required.
- *
- * @param channel the channel on which the method was received
- * @param response the response received
- * @return true if the response matched an outstanding request
- */
- protected synchronized boolean handleResponse(int channel, AMQMethodBody response)
- {
- ResponseHandler request = _requests.get(channel);
- if (request == null)
- {
- if(!_requests.isEmpty())
- {
- _logger.warn(new LogMessage("[next channel={3, integer}]: Response {0} on channel {1, integer} failed to match outstanding requests: {2}", response, channel, _requests, _nextChannel));
- }
- return false;
- }
- else
- {
- request.responded(response);
- return true;
- }
- }
-
- /**
- * Called when this broker is excluded from the group. Any requests made on
- * it are informed this member has left the group.
- */
- synchronized void remove()
- {
- for (ResponseHandler r : _requests.values())
- {
- r.removed();
- }
- }
-
- /**
- * Engages this broker in the specified group request
- *
- * @param request the request being made to a group of brokers
- * @throws AMQException if there is any failure
- */
- synchronized void invoke(GroupRequest request) throws AMQException
- {
- int channel = nextChannel();
- _requests.put(channel, new GroupRequestAdapter(request, channel));
- request.send(channel, this);
- }
-
- /**
- * Sends a message to the remote peer and undertakes to notify the specified
- * handler of the response.
- *
- * @param msg the message to send
- * @param handler the callback to notify of responses (or the removal of this broker
- * from the group)
- * @throws AMQException
- */
- synchronized void send(Sendable msg, ResponseHandler handler) throws AMQException
- {
- int channel;
- if (handler != null)
- {
- channel = nextChannel();
- _requests.put(channel, new RemovingWrapper(handler, channel));
- }
- else
- {
- channel = DEFAULT_CHANNEL;
- }
-
- msg.send(channel, this);
- }
-
- private int nextChannel()
- {
- int channel = _nextChannel++;
- if(_nextChannel >= END_CHANNEL)
- {
- _nextChannel = START_CHANNEL;
- }
- return channel;
- }
-
- /**
- * extablish connection without handling redirect
- */
- abstract boolean connect() throws IOException, InterruptedException;
-
- /**
- * Start connection process, including replay
- */
- abstract void connectAsynch(Iterable<AMQMethodBody> msgs);
-
- /**
- * Replay messages to the remote peer this instance represents. These messages
- * must be sent before any others whose transmission is requested through send() etc.
- *
- * @param msgs
- */
- abstract void replay(Iterable<AMQMethodBody> msgs);
-
- /**
- * establish connection, handling redirect if required...
- */
- abstract Broker connectToCluster() throws IOException, InterruptedException;
-
- private class GroupRequestAdapter implements ResponseHandler
- {
- private final GroupRequest request;
- private final int channel;
-
- GroupRequestAdapter(GroupRequest request, int channel)
- {
- this.request = request;
- this.channel = channel;
- }
-
- public void responded(AMQMethodBody response)
- {
- request.responseReceived(Broker.this, response);
- _requests.remove(channel);
- }
-
- public void removed()
- {
- request.removed(Broker.this);
- }
-
- public String toString()
- {
- return "GroupRequestAdapter{" + channel + ", " + request + "}";
- }
- }
-
- private class RemovingWrapper implements ResponseHandler
- {
- private final ResponseHandler handler;
- private final int channel;
-
- RemovingWrapper(ResponseHandler handler, int channel)
- {
- this.handler = handler;
- this.channel = channel;
- }
-
- public void responded(AMQMethodBody response)
- {
- handler.responded(response);
- _requests.remove(channel);
- }
-
- public void removed()
- {
- handler.removed();
- }
-
- public String toString()
- {
- return "RemovingWrapper{" + channel + ", " + handler + "}";
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java
deleted file mode 100644
index 92c3c4e7bf..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-interface BrokerFactory
-{
- public Broker create(MemberHandle handle);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java
deleted file mode 100644
index 755a341607..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.cluster.replay.ReplayManager;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.cluster.util.InvokeMultiple;
-import org.apache.qpid.framing.AMQMethodBody;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Manages the membership list of a group and the set of brokers representing the
- * remote peers. The group should be initialised through a call to establish()
- * or connectToLeader().
- *
- */
-class BrokerGroup
-{
- private static final Logger _logger = Logger.getLogger(BrokerGroup.class);
-
- private final InvokeMultiple<MembershipChangeListener> _changeListeners = new InvokeMultiple<MembershipChangeListener>(MembershipChangeListener.class);
- private final ReplayManager _replayMgr;
- private final MemberHandle _local;
- private final BrokerFactory _factory;
- private final Object _lock = new Object();
- private final Set<MemberHandle> _synch = new HashSet<MemberHandle>();
- private List<MemberHandle> _members;
- private List<Broker> _peers = new ArrayList<Broker>();
- private JoinState _state = JoinState.UNINITIALISED;
-
- /**
- * Creates an unitialised group.
- *
- * @param local a handle that represents the local broker
- * @param replayMgr the replay manager to use when creating new brokers
- * @param factory the factory through which broker instances are created
- */
- BrokerGroup(MemberHandle local, ReplayManager replayMgr, BrokerFactory factory)
- {
- _replayMgr = replayMgr;
- _local = local;
- _factory = factory;
- }
-
- /**
- * Called to establish the local broker as the leader of a new group
- */
- void establish()
- {
- synchronized (_lock)
- {
- setState(JoinState.JOINED);
- _members = new ArrayList<MemberHandle>();
- _members.add(_local);
- }
- fireChange();
- }
-
- /**
- * Called by prospect to connect to group
- */
- Broker connectToLeader(MemberHandle handle) throws Exception
- {
- Broker leader = _factory.create(handle);
- leader = leader.connectToCluster();
- synchronized (_lock)
- {
- setState(JoinState.JOINING);
- _members = new ArrayList<MemberHandle>();
- _members.add(leader);
- _peers.add(leader);
- }
- fireChange();
- return leader;
- }
-
- /**
- * Called by leader when handling a join request
- */
- Broker connectToProspect(MemberHandle handle) throws IOException, InterruptedException
- {
- Broker prospect = _factory.create(handle);
- prospect.connect();
- synchronized (_lock)
- {
- _members.add(prospect);
- _peers.add(prospect);
- }
- fireChange();
- return prospect;
- }
-
- /**
- * Called in reponse to membership announcements.
- *
- * @param members the list of members now part of the group
- */
- void setMembers(List<MemberHandle> members)
- {
- if (isJoined())
- {
- List<Broker> old = _peers;
-
- synchronized (_lock)
- {
- _peers = getBrokers(members);
- _members = new ArrayList<MemberHandle>(members);
- }
-
- //remove those that are still members
- old.removeAll(_peers);
-
- //handle failure of any brokers that haven't survived
- for (Broker peer : old)
- {
- peer.remove();
- }
- }
- else
- {
- synchronized (_lock)
- {
- setState(JoinState.INITIATION);
- _members = new ArrayList<MemberHandle>(members);
- _synch.addAll(_members);
- _synch.remove(_local);
- }
- }
- fireChange();
- }
-
- List<MemberHandle> getMembers()
- {
- synchronized (_lock)
- {
- return Collections.unmodifiableList(_members);
- }
- }
-
- List<Broker> getPeers()
- {
- synchronized (_lock)
- {
- return _peers;
- }
- }
-
- /**
- * Removes the member presented from the group
- * @param peer the broker that should be removed
- */
- void remove(Broker peer)
- {
- synchronized (_lock)
- {
- _peers.remove(peer);
- _members.remove(peer);
- }
- fireChange();
- }
-
- MemberHandle getLocal()
- {
- return _local;
- }
-
- Broker getLeader()
- {
- synchronized (_lock)
- {
- return _peers.size() > 0 ? _peers.get(0) : null;
- }
- }
-
- /**
- * Allows a Broker instance to be retrieved for a given handle
- *
- * @param handle the handle for which a broker is sought
- * @param create flag to indicate whther a broker should be created for the handle if
- * one is not found within the list of known peers
- * @return the broker corresponding to handle or null if a match cannot be found and
- * create is false
- */
- Broker findBroker(MemberHandle handle, boolean create)
- {
- if (handle instanceof Broker)
- {
- return (Broker) handle;
- }
- else
- {
- for (Broker b : getPeers())
- {
- if (b.matches(handle))
- {
- return b;
- }
- }
- }
- if (create)
- {
- Broker b = _factory.create(handle);
- List<AMQMethodBody> msgs = _replayMgr.replay(isLeader(_local));
- _logger.info(new LogMessage("Replaying {0} from {1} to {2}", msgs, _local, b));
- b.connectAsynch(msgs);
-
- return b;
- }
- else
- {
- return null;
- }
- }
-
- /**
- * @param member the member to test for leadership
- * @return true if the passed in member is the group leader, false otherwise
- */
- boolean isLeader(MemberHandle member)
- {
- synchronized (_lock)
- {
- return member.matches(_members.get(0));
- }
- }
-
- /**
- * @return true if the local broker is the group leader, false otherwise
- */
- boolean isLeader()
- {
- return isLeader(_local);
- }
-
- /**
- * Used when the leader fails and the next broker in the list needs to
- * assume leadership
- * @return true if the action succeeds
- */
- boolean assumeLeadership()
- {
- boolean valid;
- synchronized (_lock)
- {
- valid = _members.size() > 1 && _local.matches(_members.get(1));
- if (valid)
- {
- _members.remove(0);
- _peers.remove(0);
- }
- }
- fireChange();
- return valid;
- }
-
- /**
- * Called in response to a Cluster.Synch message being received during the join
- * process. This indicates that the member mentioned has replayed all necessary
- * messages to the local broker.
- *
- * @param member the member from whom the synch messages was received
- */
- void synched(MemberHandle member)
- {
- _logger.info(new LogMessage("Synchronised with {0}", member));
- synchronized (_lock)
- {
- if (isLeader(member))
- {
- setState(JoinState.INDUCTION);
- }
- _synch.remove(member);
- if (_synch.isEmpty())
- {
- _peers = getBrokers(_members);
- setState(JoinState.JOINED);
- }
- }
- }
-
-
- /**
- * @return the state of the group
- */
- JoinState getState()
- {
- synchronized (_lock)
- {
- return _state;
- }
- }
-
- void addMemberhipChangeListener(MembershipChangeListener l)
- {
- _changeListeners.addListener(l);
- }
-
- void removeMemberhipChangeListener(MembershipChangeListener l)
- {
- _changeListeners.removeListener(l);
- }
-
-
-
- private void setState(JoinState state)
- {
- _logger.info(new LogMessage("Changed state from {0} to {1}", _state, state));
- _state = state;
- }
-
- private boolean isJoined()
- {
- return inState(JoinState.JOINED);
- }
-
- private boolean inState(JoinState state)
- {
- return _state.equals(state);
- }
-
- private List<Broker> getBrokers(List<MemberHandle> handles)
- {
- List<Broker> brokers = new ArrayList<Broker>();
- for (MemberHandle handle : handles)
- {
- if (!_local.matches(handle))
- {
- brokers.add(findBroker(handle, true));
- }
- }
- return brokers;
- }
-
- private void fireChange()
- {
- List<MemberHandle> members;
- synchronized(this)
- {
- members = new ArrayList(_members);
- }
- _changeListeners.getProxy().changed(Collections.unmodifiableList(members));
- }
-} \ No newline at end of file
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java
deleted file mode 100644
index 1b4a3e8327..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.mina.common.IoSession;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.client.protocol.AMQProtocolSession;
-import org.apache.qpid.client.state.AMQStateManager;
-import org.apache.qpid.framing.AMQMethodBody;
-
-/**
- * Hack to assist with reuse of the client handlers for connection setup in
- * the inter-broker communication within the cluster.
- *
- */
-class ClientAdapter implements MethodHandler
-{
- private final AMQProtocolSession _session;
- private final AMQStateManager _stateMgr;
-
- ClientAdapter(IoSession session, AMQStateManager stateMgr)
- {
- this(session, stateMgr, "guest", "guest", session.toString(), "/cluster");
- }
-
- ClientAdapter(IoSession session, AMQStateManager stateMgr, String user, String password, String name, String path)
- {
- _session = new SessionAdapter(session, new ConnectionAdapter(user, password, name, path));
- _stateMgr = stateMgr;
- }
-
- public void handle(int channel, AMQMethodBody method) throws AMQException
- {
- AMQMethodEvent evt = new AMQMethodEvent(channel, method);
- _stateMgr.methodReceived(evt);
- }
-
- private class SessionAdapter extends AMQProtocolSession
- {
- public SessionAdapter(IoSession session, AMQConnection connection)
- {
- super(null, session, connection);
- }
- }
-
- private static class ConnectionAdapter extends AMQConnection
- {
- ConnectionAdapter(String username, String password, String clientName, String virtualPath)
- {
- super(username, password, clientName, virtualPath);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java
deleted file mode 100644
index f4a8e4c1e2..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-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.state.AMQState;
-import org.apache.qpid.client.state.AMQStateManager;
-// import org.apache.qpid.client.state.IllegalStateTransitionException;
-import org.apache.qpid.client.state.StateAwareMethodListener;
-import org.apache.qpid.client.protocol.AMQProtocolSession;
-import org.apache.qpid.framing.*;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * An extension of client.AMQStateManager that allows different handlers to be registered.
- *
- */
-public class ClientHandlerRegistry extends AMQStateManager
-{
- private final Map<AMQState, ClientRegistry> _handlers = new HashMap<AMQState, ClientRegistry>();
- private final MemberHandle _identity;
-
- protected ClientHandlerRegistry(MemberHandle local, AMQProtocolSession protocolSession)
- {
- super(AMQState.CONNECTION_NOT_STARTED, false, protocolSession);
-
- _identity = local;
-
- addHandler(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance(),
- AMQState.CONNECTION_NOT_STARTED);
-
- addHandler(ConnectionTuneBody.class, new ConnectionTuneHandler(),
- AMQState.CONNECTION_NOT_TUNED);
- addHandler(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance(),
- AMQState.CONNECTION_NOT_TUNED);
- addHandler(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance(),
- AMQState.CONNECTION_NOT_OPENED);
-
- addHandlers(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance(),
- AMQState.CONNECTION_NOT_STARTED,
- AMQState.CONNECTION_NOT_TUNED,
- AMQState.CONNECTION_NOT_OPENED);
-
- }
-
- private ClientRegistry state(AMQState state)
- {
- ClientRegistry registry = _handlers.get(state);
- if (registry == null)
- {
- registry = new ClientRegistry();
- _handlers.put(state, registry);
- }
- return registry;
- }
-
- protected StateAwareMethodListener findStateTransitionHandler(AMQState state, AMQMethodBody frame) //throws IllegalStateTransitionException
- {
- ClientRegistry registry = _handlers.get(state);
- return registry == null ? null : registry.getHandler(frame);
- }
-
-
- <A extends Class<AMQMethodBody>> void addHandlers(Class type, StateAwareMethodListener handler, AMQState... states)
- {
- for (AMQState state : states)
- {
- addHandler(type, handler, state);
- }
- }
-
- <A extends Class<AMQMethodBody>> void addHandler(Class type, StateAwareMethodListener handler, AMQState state)
- {
- ClientRegistry registry = _handlers.get(state);
- if (registry == null)
- {
- registry = new ClientRegistry();
- _handlers.put(state, registry);
- }
- registry.add(type, handler);
- }
-
- static class ClientRegistry
- {
- private final Map<Class<? extends AMQMethodBody>, StateAwareMethodListener> registry
- = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener>();
-
- <A extends Class<AMQMethodBody>> void add(A type, StateAwareMethodListener handler)
- {
- registry.put(type, handler);
- }
-
- StateAwareMethodListener getHandler(AMQMethodBody frame)
- {
- return registry.get(frame.getClass());
- }
- }
-
- class ConnectionTuneHandler extends ConnectionTuneMethodHandler
- {
- 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)
- new AMQShortString(ClusterCapability.add(capabilities, _identity)),
- // capabilities
- insist,
- // insist
- path);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java
deleted file mode 100644
index 80f9ef62b1..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.server.cluster.handler.ClusterMethodHandlerFactory;
-import org.apache.qpid.server.cluster.replay.RecordingMethodHandlerFactory;
-import org.apache.qpid.server.cluster.replay.ReplayStore;
-
-import java.net.InetSocketAddress;
-
-class ClusterBuilder
-{
- private final LoadTable loadTable = new LoadTable();
- private final ReplayStore replayStore = new ReplayStore();
- private final MemberHandle handle;
- private final GroupManager groupMgr;
-
- ClusterBuilder(InetSocketAddress address)
- {
- handle = new SimpleMemberHandle(address.getHostName(), address.getPort()).resolve();
- groupMgr = new DefaultGroupManager(handle, getBrokerFactory(), replayStore, loadTable);
- }
-
- GroupManager getGroupManager()
- {
- return groupMgr;
- }
-
- ServerHandlerRegistry getHandlerRegistry()
- {
- return new ServerHandlerRegistry(getHandlerFactory(), null, null);
- }
-
- private MethodHandlerFactory getHandlerFactory()
- {
- MethodHandlerFactory factory = new ClusterMethodHandlerFactory(groupMgr, loadTable);
- //need to wrap relevant handlers with recording handler for easy replay:
- return new RecordingMethodHandlerFactory(factory, replayStore);
- }
-
- private BrokerFactory getBrokerFactory()
- {
- return new MinaBrokerProxyFactory(handle);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java
deleted file mode 100644
index 57c48f0611..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.framing.AMQShortString;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class ClusterCapability
-{
- public static final String PATTERN = ".*\\bcluster_peer=(\\S*:\\d*)\b*.*";
- public static final String PEER = "cluster_peer";
-
- public static AMQShortString add(AMQShortString original, MemberHandle identity)
- {
- return original == null ? peer(identity) : new AMQShortString(original + " " + peer(identity));
- }
-
- private static AMQShortString peer(MemberHandle identity)
- {
- return new AMQShortString(PEER + "=" + identity.getDetails());
- }
-
- public static boolean contains(AMQShortString in)
- {
- return in != null; // && in.contains(in);
- }
-
- public static MemberHandle getPeer(AMQShortString in)
- {
- Matcher matcher = Pattern.compile(PATTERN).matcher(in);
- if (matcher.matches())
- {
- return new SimpleMemberHandle(matcher.group(1));
- }
- else
- {
- throw new RuntimeException("Could not find peer in '" + in + "'");
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java
deleted file mode 100644
index ee5aa48db9..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.log4j.Logger;
-import org.apache.mina.common.IoSession;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.codec.AMQCodecFactory;
-import org.apache.qpid.framing.AMQBody;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.ConnectionOpenBody;
-import org.apache.qpid.framing.ConnectionSecureOkBody;
-import org.apache.qpid.framing.ConnectionStartOkBody;
-import org.apache.qpid.framing.ConnectionTuneOkBody;
-import org.apache.qpid.framing.ClusterMembershipBody;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.protocol.AMQPFastProtocolHandler;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.registry.IApplicationRegistry;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
-
-import java.net.InetSocketAddress;
-
-public class ClusteredProtocolHandler extends AMQPFastProtocolHandler implements InductionBuffer.MessageHandler
-{
- private static final Logger _logger = Logger.getLogger(ClusteredProtocolHandler.class);
- private final InductionBuffer _peerBuffer = new InductionBuffer(this);
- private final InductionBuffer _clientBuffer = new InductionBuffer(this);
- private final GroupManager _groupMgr;
- private final ServerHandlerRegistry _handlers;
-
- public ClusteredProtocolHandler(InetSocketAddress address)
- {
- this(ApplicationRegistry.getInstance(), address);
- }
-
- public ClusteredProtocolHandler(IApplicationRegistry registry, InetSocketAddress address)
- {
- super(registry);
- ClusterBuilder builder = new ClusterBuilder(address);
- _groupMgr = builder.getGroupManager();
- _handlers = builder.getHandlerRegistry();
- }
-
- public ClusteredProtocolHandler(ClusteredProtocolHandler handler)
- {
- super(handler);
- _groupMgr = handler._groupMgr;
- _handlers = handler._handlers;
- }
-
- protected void createSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession, AMQCodecFactory codec) throws AMQException
- {
- new ClusteredProtocolSession(session, virtualHostRegistry, codec, new ServerHandlerRegistry(_handlers, virtualHostRegistry, protocolSession));
- }
-
- void connect(String join) throws Exception
- {
- if (join == null)
- {
- _groupMgr.establish();
- }
- else
- {
- _groupMgr.join(new SimpleMemberHandle(join));
- }
- }
-
- private boolean inState(JoinState state)
- {
- return _groupMgr.getState().equals(state);
- }
-
- public void messageReceived(IoSession session, Object msg) throws Exception
- {
- JoinState state = _groupMgr.getState();
- switch (state)
- {
- case JOINED:
- _logger.debug(new LogMessage("Received {0}", msg));
- super.messageReceived(session, msg);
- break;
- case JOINING:
- case INITIATION:
- case INDUCTION:
- buffer(session, msg);
- break;
- default:
- throw new AMQException("Received message while in state: " + state);
- }
- JoinState latest = _groupMgr.getState();
- if (!latest.equals(state))
- {
- switch (latest)
- {
- case INDUCTION:
- _logger.info("Reached induction, delivering buffered message from peers");
- _peerBuffer.deliver();
- break;
- case JOINED:
- _logger.info("Reached joined, delivering buffered message from clients");
- _clientBuffer.deliver();
- break;
- }
- }
- }
-
- private void buffer(IoSession session, Object msg) throws Exception
- {
- if (isBufferable(msg))
- {
- MemberHandle peer = ClusteredProtocolSession.getSessionPeer(session);
- if (peer == null)
- {
- _logger.debug(new LogMessage("Buffering {0} for client", msg));
- _clientBuffer.receive(session, msg);
- }
- else if (inState(JoinState.JOINING) && isMembershipAnnouncement(msg))
- {
- _logger.debug(new LogMessage("Initial membership [{0}] received from {1}", msg, peer));
- super.messageReceived(session, msg);
- }
- else if (inState(JoinState.INITIATION) && _groupMgr.isLeader(peer))
- {
- _logger.debug(new LogMessage("Replaying {0} from leader ", msg));
- super.messageReceived(session, msg);
- }
- else if (inState(JoinState.INDUCTION))
- {
- _logger.debug(new LogMessage("Replaying {0} from peer {1}", msg, peer));
- super.messageReceived(session, msg);
- }
- else
- {
- _logger.debug(new LogMessage("Buffering {0} for peer {1}", msg, peer));
- _peerBuffer.receive(session, msg);
- }
- }
- else
- {
- _logger.debug(new LogMessage("Received {0}", msg));
- super.messageReceived(session, msg);
- }
- }
-
- public void deliver(IoSession session, Object msg) throws Exception
- {
- _logger.debug(new LogMessage("Delivering {0}", msg));
- super.messageReceived(session, msg);
- }
-
- private boolean isMembershipAnnouncement(Object msg)
- {
- return msg instanceof AMQFrame && (((AMQFrame) msg).getBodyFrame() instanceof ClusterMembershipBody);
- }
-
- private boolean isBufferable(Object msg)
- {
- return msg instanceof AMQFrame && isBuffereable(((AMQFrame) msg).getBodyFrame());
- }
-
- private boolean isBuffereable(AMQBody body)
- {
- return !(body instanceof ConnectionStartOkBody ||
- body instanceof ConnectionTuneOkBody ||
- body instanceof ConnectionSecureOkBody ||
- body instanceof ConnectionOpenBody);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java
deleted file mode 100644
index eea660c4f0..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.mina.common.IoSession;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.codec.AMQCodecFactory;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.protocol.AMQMinaProtocolSession;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.registry.IApplicationRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-
-public class ClusteredProtocolSession extends AMQMinaProtocolSession
-{
- private MemberHandle _peer;
-
- public ClusteredProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory, AMQStateManager stateManager) throws AMQException
-// public ClusteredProtocolSession(IoSession session, QueueRegistry queueRegistry,
-// ExchangeRegistry exchangeRegistry, AMQCodecFactory codecFactory) throws AMQException
- {
- super(session, virtualHostRegistry, codecFactory, stateManager);
-// super(session, queueRegistry, exchangeRegistry, codecFactory);
- }
-
- public boolean isPeerSession()
- {
- return _peer != null;
- }
-
- public void setSessionPeer(MemberHandle peer)
- {
- _peer = peer;
- }
-
- public MemberHandle getSessionPeer()
- {
- return _peer;
- }
-
- public AMQChannel getChannel(int channelId)
- throws AMQException
- {
- AMQChannel channel = super.getChannel(channelId);
- if (isPeerSession() && channel == null)
- {
- channel = new OneUseChannel(channelId, getVirtualHost());
- addChannel(channel);
- }
- return channel;
- }
-
- public static boolean isPeerSession(IoSession session)
- {
- return isPeerSession(getAMQProtocolSession(session));
- }
-
- public static boolean isPeerSession(AMQProtocolSession session)
- {
- return session instanceof ClusteredProtocolSession && ((ClusteredProtocolSession) session).isPeerSession();
- }
-
- public static void setSessionPeer(AMQProtocolSession session, MemberHandle peer)
- {
- ((ClusteredProtocolSession) session).setSessionPeer(peer);
- }
-
- public static MemberHandle getSessionPeer(AMQProtocolSession session)
- {
- return ((ClusteredProtocolSession) session).getSessionPeer();
- }
-
- public static MemberHandle getSessionPeer(IoSession session)
- {
- return getSessionPeer(getAMQProtocolSession(session));
- }
-
- /**
- * Cleans itself up after delivery of a message (publish frame, header and optional body frame(s))
- */
- private class OneUseChannel extends AMQChannel
- {
- public OneUseChannel(int channelId, VirtualHost virtualHost)
- throws AMQException
- {
- super(ClusteredProtocolSession.this,channelId,
- virtualHost.getMessageStore(),
- virtualHost.getExchangeRegistry());
- }
-
- protected void routeCurrentMessage() throws AMQException
- {
- super.routeCurrentMessage();
- removeChannel(getChannelId());
- }
- }
-
- public static boolean isPayloadFromPeer(AMQMessage payload)
- {
- return isPeerSession(payload.getPublisher());
- }
-
- public static boolean canRelay(AMQMessage payload, MemberHandle target)
- {
- //can only relay client messages that have not already been relayed to the given target
- return !isPayloadFromPeer(payload) && !payload.checkToken(target);
- }
-
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java
deleted file mode 100644
index a1f01eff46..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import java.io.IOException;
-
-class ConnectionStatusMonitor
-{
- private boolean _complete;
- private boolean _redirected;
- private String _host;
- private int _port;
- private RuntimeException _error;
-
- synchronized void opened()
- {
- _complete = true;
- notifyAll();
- }
-
- synchronized void redirect(String host, int port)
- {
- _complete = true;
- _redirected = true;
- this._host = host;
- this._port = port;
- }
-
- synchronized void failed(RuntimeException e)
- {
- _error = e;
- _complete = true;
- }
-
- synchronized boolean waitUntilOpen() throws InterruptedException
- {
- while (!_complete)
- {
- wait();
- }
- if (_error != null)
- {
- throw _error;
- }
- return !_redirected;
- }
-
- synchronized boolean isOpened()
- {
- return _complete;
- }
-
- String getHost()
- {
- return _host;
- }
-
- int getPort()
- {
- return _port;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java
deleted file mode 100644
index 2f473b63fb..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.server.cluster.policy.StandardPolicies;
-import org.apache.qpid.server.cluster.replay.ReplayManager;
-import org.apache.qpid.server.cluster.util.LogMessage;
-
-import java.util.List;
-
-public class DefaultGroupManager implements GroupManager, MemberFailureListener, BrokerFactory, StandardPolicies
-{
- private static final Logger _logger = Logger.getLogger(DefaultGroupManager.class);
- private final LoadTable _loadTable;
- private final BrokerFactory _factory;
- private final ReplayManager _replayMgr;
- private final BrokerGroup _group;
-
- DefaultGroupManager(MemberHandle handle, BrokerFactory factory, ReplayManager replayMgr)
- {
- this(handle, factory, replayMgr, new LoadTable());
- }
-
- DefaultGroupManager(MemberHandle handle, BrokerFactory factory, ReplayManager replayMgr, LoadTable loadTable)
- {
- handle = SimpleMemberHandle.resolve(handle);
- _logger.info(handle);
- _loadTable = loadTable;
- _factory = factory;
- _replayMgr = replayMgr;
- _group = new BrokerGroup(handle, _replayMgr, this);
- }
-
- public JoinState getState()
- {
- return _group.getState();
- }
-
- public void addMemberhipChangeListener(MembershipChangeListener l)
- {
- _group.addMemberhipChangeListener(l);
- }
-
- public void removeMemberhipChangeListener(MembershipChangeListener l)
- {
- _group.removeMemberhipChangeListener(l);
- }
-
- public void broadcast(Sendable message) throws AMQException
- {
- for (Broker b : _group.getPeers())
- {
- b.send(message, null);
- }
- }
-
- public void broadcast(Sendable message, BroadcastPolicy policy, GroupResponseHandler callback) throws AMQException
- {
- GroupRequest request = new GroupRequest(message, policy, callback);
- for (Broker b : _group.getPeers())
- {
- b.invoke(request);
- }
- request.finishedSend();
- }
-
- public void send(MemberHandle broker, Sendable message) throws AMQException
- {
- Broker destination = findBroker(broker);
- if(destination == null)
- {
- _logger.warn(new LogMessage("Invalid destination sending {0}. {1} not known", message, broker));
- }
- else
- {
- destination.send(message, null);
- _logger.debug(new LogMessage("Sent {0} to {1}", message, broker));
- }
- }
-
- private void send(Broker broker, Sendable message, ResponseHandler handler) throws AMQException
- {
- broker.send(message, handler);
- }
-
- private void ping(Broker b) throws AMQException
- {
- // 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.
- ClusterPingBody ping = new ClusterPingBody((byte)8,
- (byte)0,
- ClusterPingBody.getClazz((byte)8, (byte)0),
- ClusterPingBody.getMethod((byte)8, (byte)0),
- _group.getLocal().getDetails(),
- _loadTable.getLocalLoad(),
- true);
- BlockingHandler handler = new BlockingHandler();
- send(getLeader(), new SimpleBodySendable(ping), handler);
- handler.waitForCompletion();
- if (handler.failed())
- {
- if (isLeader())
- {
- handleFailure(b);
- }
- else
- {
- suspect(b);
- }
- }
- else
- {
- _loadTable.setLoad(b, ((ClusterPingBody) handler.getResponse()).load);
- }
- }
-
- public void handlePing(MemberHandle member, long load)
- {
- _loadTable.setLoad(findBroker(member), load);
- }
-
- public Member redirect()
- {
- return _loadTable.redirect();
- }
-
- public void establish()
- {
- _group.establish();
- _logger.info("Established cluster");
- }
-
- public void join(MemberHandle member) throws AMQException
- {
- member = SimpleMemberHandle.resolve(member);
-
- Broker leader = connectToLeader(member);
- _logger.info(new LogMessage("Connected to {0}. joining", leader));
- // 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.
- ClusterJoinBody join = new ClusterJoinBody((byte)8,
- (byte)0,
- ClusterJoinBody.getClazz((byte)8, (byte)0),
- ClusterJoinBody.getMethod((byte)8, (byte)0),
- _group.getLocal().getDetails());
-
- send(leader, new SimpleBodySendable(join));
- }
-
- private Broker connectToLeader(MemberHandle member) throws AMQException
- {
- try
- {
- return _group.connectToLeader(member);
- }
- catch (Exception e)
- {
- throw new AMQException("Could not connect to leader: " + e, e);
- }
- }
-
- public void leave() throws AMQException
- {
- // 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.
- ClusterLeaveBody leave = new ClusterLeaveBody((byte)8,
- (byte)0,
- ClusterLeaveBody.getClazz((byte)8, (byte)0),
- ClusterLeaveBody.getMethod((byte)8, (byte)0),
- _group.getLocal().getDetails());
-
- send(getLeader(), new SimpleBodySendable(leave));
- }
-
- private void suspect(MemberHandle broker) throws AMQException
- {
- if (_group.isLeader(broker))
- {
- //need new leader, if this broker is next in line it can assume leadership
- if (_group.assumeLeadership())
- {
- announceMembership();
- }
- else
- {
- _logger.warn(new LogMessage("Leader failed. Expecting {0} to succeed.", _group.getMembers().get(1)));
- }
- }
- else
- {
- // 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.
- ClusterSuspectBody suspect = new ClusterSuspectBody((byte)8,
- (byte)0,
- ClusterSuspectBody.getClazz((byte)8, (byte)0),
- ClusterSuspectBody.getMethod((byte)8, (byte)0),
- broker.getDetails());
-
- send(getLeader(), new SimpleBodySendable(suspect));
- }
- }
-
-
- public void handleJoin(MemberHandle member) throws AMQException
- {
- _logger.info(new LogMessage("Handling join request for {0}", member));
- if(isLeader())
- {
- //connect to the host and port specified:
- Broker prospect = connectToProspect(member);
- announceMembership();
- List<AMQMethodBody> msgs = _replayMgr.replay(true);
- _logger.info(new LogMessage("Replaying {0} from leader to {1}", msgs, prospect));
- prospect.replay(msgs);
- }
- else
- {
- //pass request on to leader:
- // 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.
- ClusterJoinBody request = new ClusterJoinBody((byte)8, (byte)0,
- ClusterJoinBody.getClazz((byte)8, (byte)0),
- ClusterJoinBody.getMethod((byte)8, (byte)0),
- member.getDetails());
-
- Broker leader = getLeader();
- send(leader, new SimpleBodySendable(request));
- _logger.info(new LogMessage("Passed join request for {0} to {1}", member, leader));
- }
- }
-
- private Broker connectToProspect(MemberHandle member) throws AMQException
- {
- try
- {
- return _group.connectToProspect(member);
- }
- catch (Exception e)
- {
- e.printStackTrace();
- throw new AMQException("Could not connect to prospect: " + e, e);
- }
- }
-
- public void handleLeave(MemberHandle member) throws AMQException
- {
- handleFailure(findBroker(member));
- announceMembership();
- }
-
- public void handleSuspect(MemberHandle member) throws AMQException
- {
- Broker b = findBroker(member);
- if(b != null)
- {
- //ping it to check it has failed, ping will handle failure if it has
- ping(b);
- announceMembership();
- }
- }
-
- public void handleSynch(MemberHandle member)
- {
- _group.synched(member);
- }
-
- private ClusterMembershipBody createAnnouncement(String membership)
- {
- // 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.
- ClusterMembershipBody announce = new ClusterMembershipBody((byte)8, (byte)0,
- ClusterMembershipBody.getClazz((byte)8, (byte)0),
- ClusterMembershipBody.getMethod((byte)8, (byte)0),
- membership.getBytes());
-
-
- return announce;
- }
-
- private void announceMembership() throws AMQException
- {
- String membership = SimpleMemberHandle.membersToString(_group.getMembers());
- ClusterMembershipBody announce = createAnnouncement(membership);
- broadcast(new SimpleBodySendable(announce));
- _logger.info(new LogMessage("Membership announcement sent: {0}", membership));
- }
-
- private void handleFailure(Broker peer)
- {
- peer.remove();
- _group.remove(peer);
- }
-
- public void handleMembershipAnnouncement(String membership) throws AMQException
- {
- _group.setMembers(SimpleMemberHandle.stringToMembers(membership));
- _logger.info(new LogMessage("Membership announcement received: {0}", membership));
- }
-
- public boolean isLeader()
- {
- return _group.isLeader();
- }
-
- public boolean isLeader(MemberHandle handle)
- {
- return _group.isLeader(handle);
- }
-
- public Broker getLeader()
- {
- return _group.getLeader();
- }
-
- private Broker findBroker(MemberHandle handle)
- {
- return _group.findBroker(handle, false);
- }
-
- public Member getMember(MemberHandle handle)
- {
- return findBroker(handle);
- }
-
- public boolean isMember(MemberHandle member)
- {
- for (MemberHandle handle : _group.getMembers())
- {
- if (handle.matches(member))
- {
- return true;
- }
- }
- return false;
- }
-
- public MemberHandle getLocal()
- {
- return _group.getLocal();
- }
-
- public void failed(MemberHandle member)
- {
- if (isLeader())
- {
- handleFailure(findBroker(member));
- try
- {
- announceMembership();
- }
- catch (AMQException e)
- {
- _logger.error("Error announcing failure: " + e, e);
- }
- }
- else
- {
- try
- {
- suspect(member);
- }
- catch (AMQException e)
- {
- _logger.error("Error sending suspect: " + e, e);
- }
- }
- }
-
- public Broker create(MemberHandle handle)
- {
- Broker broker = _factory.create(handle);
- broker.addFailureListener(this);
- return broker;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java
deleted file mode 100644
index 5599ae4b1f..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-
-public interface GroupManager
-{
- /**
- * Establish a new cluster with the local member as the leader.
- */
- public void establish();
-
- /**
- * Join the cluster to which member belongs
- */
- public void join(MemberHandle member) throws AMQException;
-
- public void broadcast(Sendable message) throws AMQException;
-
- public void broadcast(Sendable message, BroadcastPolicy policy, GroupResponseHandler callback) throws AMQException;
-
- public void send(MemberHandle broker, Sendable message) throws AMQException;
-
- public void leave() throws AMQException;
-
- public void handleJoin(MemberHandle member) throws AMQException;
-
- public void handleLeave(MemberHandle member) throws AMQException;
-
- public void handleSuspect(MemberHandle member) throws AMQException;
-
- public void handlePing(MemberHandle member, long load);
-
- public void handleMembershipAnnouncement(String membership) throws AMQException;
-
- public void handleSynch(MemberHandle member);
-
- public boolean isLeader();
-
- public boolean isLeader(MemberHandle handle);
-
- public boolean isMember(MemberHandle member);
-
- public MemberHandle redirect();
-
- public MemberHandle getLocal();
-
- public JoinState getState();
-
- public void addMemberhipChangeListener(MembershipChangeListener l);
-
- public void removeMemberhipChangeListener(MembershipChangeListener l);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java
deleted file mode 100644
index 8ab7856e87..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Represents a method sent to a group of Member instances. Manages the responses,
- * completion and callback.
- *
- */
-class GroupRequest
-{
- private final Map<Member, AMQMethodBody> _responses = new HashMap<Member, AMQMethodBody>();
- private final List<Member> _brokers = new ArrayList<Member>();
- private boolean _sent;
-
- private final Sendable _request;
- private final BroadcastPolicy _policy;
- private final GroupResponseHandler _callback;
-
- GroupRequest(Sendable request, BroadcastPolicy policy, GroupResponseHandler callback)
- {
- _request = request;
- _policy = policy;
- _callback = callback;
- }
-
- void send(int channel, Member session) throws AMQException
- {
- _brokers.add(session);
- _request.send(channel, session);
- }
-
- boolean finishedSend()
- {
- _sent = true;
- return checkCompletion();
- }
-
- public boolean responseReceived(Member broker, AMQMethodBody response)
- {
- _responses.put(broker, response);
- return checkCompletion();
- }
-
- public boolean removed(Member broker)
- {
- _brokers.remove(broker);
- return checkCompletion();
- }
-
- private synchronized boolean checkCompletion()
- {
- return isComplete() && callback();
- }
-
- boolean isComplete()
- {
- return _sent && _policy != null && _policy.isComplete(_responses.size(), _brokers.size());
- }
-
- boolean callback()
- {
- _callback.response(getResults(), _brokers);
- return true;
- }
-
- List<AMQMethodBody> getResults()
- {
- List<AMQMethodBody> results = new ArrayList<AMQMethodBody>(_brokers.size());
- for (Member b : _brokers)
- {
- results.add(_responses.get(b));
- }
- return results;
- }
-
- public String toString()
- {
- return "GroupRequest{request=" + _request +", brokers=" + _brokers + ", responses=" + _responses + "}";
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java
deleted file mode 100644
index d2e9de2f39..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.framing.AMQMethodBody;
-
-import java.util.List;
-
-public interface GroupResponseHandler
-{
- //Note: this implies that the response to a group request will always be a method body...
- public void response(List<AMQMethodBody> responses, List<Member> members);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java
deleted file mode 100644
index 586d7d4ae8..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.mina.common.IoSession;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Buffers any received messages until join completes.
- *
- */
-class InductionBuffer
-{
- private final Queue<Message> _buffer = new LinkedList<Message>();
- private final MessageHandler _handler;
- private boolean _buffering = true;
-
- InductionBuffer(MessageHandler handler)
- {
- _handler = handler;
- }
-
- private void process() throws Exception
- {
- for (Message o = _buffer.poll(); o != null; o = _buffer.poll())
- {
- o.deliver(_handler);
- }
- _buffering = false;
- }
-
- synchronized void deliver() throws Exception
- {
- process();
- }
-
- synchronized void receive(IoSession session, Object msg) throws Exception
- {
- if (_buffering)
- {
- _buffer.offer(new Message(session, msg));
- }
- else
- {
- _handler.deliver(session, msg);
- }
- }
-
- private static class Message
- {
- private final IoSession _session;
- private final Object _msg;
-
- Message(IoSession session, Object msg)
- {
- _session = session;
- _msg = msg;
- }
-
- void deliver(MessageHandler handler) throws Exception
- {
- handler.deliver(_session, _msg);
- }
- }
-
- static interface MessageHandler
- {
- public void deliver(IoSession session, Object msg) throws Exception;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java
deleted file mode 100644
index 5f92aa2971..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-public enum JoinState
-{
- UNINITIALISED, JOINING, INITIATION, INDUCTION, JOINED
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java
deleted file mode 100644
index 13465a8615..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.PriorityQueue;
-
-/**
- * Maintains loading information about the local member and its cluster peers.
- *
- */
-public class LoadTable
-{
- private final Map<MemberHandle, Loading> _peers = new HashMap<MemberHandle, Loading>();
- private final PriorityQueue<Loading> _loads = new PriorityQueue<Loading>();
- private final Loading _local = new Loading(null);
-
- public LoadTable()
- {
- _loads.add(_local);
- }
-
- public void setLoad(Member member, long load)
- {
- synchronized (_peers)
- {
- Loading loading = _peers.get(member);
- if (loading == null)
- {
- loading = new Loading(member);
- synchronized (_loads)
- {
- _loads.add(loading);
- }
- _peers.put(member, loading);
- }
- loading.load = load;
- }
- }
-
- public void incrementLocalLoad()
- {
- synchronized (_local)
- {
- _local.load++;
- }
- }
-
- public void decrementLocalLoad()
- {
- synchronized (_local)
- {
- _local.load--;
- }
- }
-
- public long getLocalLoad()
- {
- synchronized (_local)
- {
- return _local.load;
- }
- }
-
- public Member redirect()
- {
- synchronized (_loads)
- {
- return _loads.peek().member;
- }
- }
-
- private static class Loading implements Comparable
- {
- private final Member member;
- private long load;
-
- Loading(Member member)
- {
- this.member = member;
- }
-
- public int compareTo(Object o)
- {
- return (int) (load - ((Loading) o).load);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java
deleted file mode 100644
index 15752353d1..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.OptionBuilder;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.PosixParser;
-import org.apache.log4j.Logger;
-import org.apache.mina.common.IoAcceptor;
-import org.apache.mina.transport.socket.nio.SocketAcceptor;
-import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
-import org.apache.mina.transport.socket.nio.SocketSessionConfig;
-import org.apache.qpid.pool.ReadWriteThreadModel;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry;
-import org.apache.qpid.server.transport.ConnectorConfiguration;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-
-/**
- * TODO: This is a cut-and-paste from the original broker Main class. Would be preferrable to make that class more
- * reuseable to avoid all this duplication.
- */
-public class Main extends org.apache.qpid.server.Main
-{
- private static final Logger _logger = Logger.getLogger(Main.class);
-
- protected Main(String[] args)
- {
- super(args);
- }
-
- protected void setOptions(Options otions)
- {
- super.setOptions(options);
-
- //extensions:
- Option join = OptionBuilder.withArgName("join").hasArg().withDescription("Join the specified cluster member. Overrides any value in the config file").
- withLongOpt("join").create("j");
- options.addOption(join);
- }
-
- protected void bind(int port, ConnectorConfiguration connectorConfig)
- {
- try
- {
- IoAcceptor acceptor = new SocketAcceptor();
- SocketAcceptorConfig sconfig = (SocketAcceptorConfig) acceptor.getDefaultConfig();
- SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig();
-
- sc.setReceiveBufferSize(connectorConfig.socketReceiveBufferSize);
- sc.setSendBufferSize(connectorConfig.socketWriteBuferSize);
- sc.setTcpNoDelay(true);
-
- // if we do not use the executor pool threading model we get the default leader follower
- // implementation provided by MINA
- if (connectorConfig.enableExecutorPool)
- {
- sconfig.setThreadModel(ReadWriteThreadModel.getInstance());
- }
-
- String host = InetAddress.getLocalHost().getHostName();
- ClusteredProtocolHandler handler = new ClusteredProtocolHandler(new InetSocketAddress(host, port));
- if (!connectorConfig.enableSSL)
- {
- acceptor.bind(new InetSocketAddress(port), handler, sconfig);
- _logger.info("Qpid.AMQP listening on non-SSL port " + port);
- handler.connect(commandLine.getOptionValue("j"));
- }
- else
- {
- ClusteredProtocolHandler sslHandler = new ClusteredProtocolHandler(handler);
- acceptor.bind(new InetSocketAddress(connectorConfig.sslPort), sslHandler, sconfig);
- _logger.info("Qpid.AMQP listening on SSL port " + connectorConfig.sslPort);
- }
- }
- catch (IOException e)
- {
- _logger.error("Unable to bind service to registry: " + e, e);
- }
- catch (Exception e)
- {
- _logger.error("Unable to connect to cluster: " + e, e);
- }
- }
-
- public static void main(String[] args)
- {
- new Main(args);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java
deleted file mode 100644
index 3fbdfdde70..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQDataBlock;
-
-public interface Member extends MemberHandle
-{
- public void send(AMQDataBlock data) throws AMQException;
-
- public void addFailureListener(MemberFailureListener listener);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java
deleted file mode 100644
index 7ce45dffaa..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-interface MemberFailureListener
-{
- public void failed(MemberHandle member);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java
deleted file mode 100644
index a83f034021..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-
-interface MethodHandler
-{
- public void handle(int channel, AMQMethodBody method) throws AMQException;
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java
deleted file mode 100644
index 9bf04f5458..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.server.state.AMQState;
-
-public interface MethodHandlerFactory
-{
- public MethodHandlerRegistry register(AMQState state, MethodHandlerRegistry registry);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java
deleted file mode 100644
index 748a660bb8..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class MethodHandlerRegistry
-{
- private final Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>> registry =
- new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
-
- public <A extends AMQMethodBody, B extends Class<A>> MethodHandlerRegistry addHandler(B type, StateAwareMethodListener<A> handler)
- {
- registry.put(type, handler);
- return this;
- }
-
- public <B extends AMQMethodBody> StateAwareMethodListener<B> getHandler(B frame)
- {
- return (StateAwareMethodListener<B>) registry.get(frame.getClass());
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java
deleted file mode 100644
index b01ec491ec..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.log4j.Logger;
-import org.apache.mina.common.ConnectFuture;
-import org.apache.mina.common.IoHandlerAdapter;
-import org.apache.mina.common.IoSession;
-import org.apache.mina.common.RuntimeIOException;
-import org.apache.mina.filter.codec.ProtocolCodecFilter;
-import org.apache.mina.transport.socket.nio.SocketConnector;
-import org.apache.mina.transport.socket.nio.SocketConnectorConfig;
-import org.apache.mina.transport.socket.nio.SocketSessionConfig;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.client.state.AMQState;
-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.ConnectionRedirectBody;
-import org.apache.qpid.framing.ProtocolInitiation;
-import org.apache.qpid.framing.ProtocolVersion;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-
-/**
- * A 'client stub' for a remote cluster peer, using MINA for IO Layer
- *
- */
-public class MinaBrokerProxy extends Broker implements MethodHandler
-{
- private static final Logger _logger = Logger.getLogger(MinaBrokerProxy.class);
- private final ConnectionStatusMonitor _connectionMonitor = new ConnectionStatusMonitor();
- private final ClientHandlerRegistry _legacyHandler;
- private final MinaBinding _binding = new MinaBinding();
- private final MemberHandle _local;
- private IoSession _session;
- private MethodHandler _handler;
- private Iterable<AMQMethodBody> _replay;
-
- MinaBrokerProxy(String host, int port, MemberHandle local)
- {
- super(host, port);
- _local = local;
- _legacyHandler = new ClientHandlerRegistry(local, null);
- }
-
- private void init(IoSession session)
- {
- _session = session;
- _handler = new ClientAdapter(session, _legacyHandler);
- }
-
- private ConnectFuture connectImpl()
- {
- _logger.info("Connecting to cluster peer: " + getDetails());
- SocketConnector ioConnector = new SocketConnector();
- SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig();
-
- SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig();
- scfg.setTcpNoDelay(true);
- scfg.setSendBufferSize(32768);
- scfg.setReceiveBufferSize(32768);
- InetSocketAddress address = new InetSocketAddress(getHost(), getPort());
- return ioConnector.connect(address, _binding);
- }
-
- //extablish connection without handling redirect
- boolean connect() throws IOException, InterruptedException
- {
- ConnectFuture future = connectImpl();
- // wait for connection to complete
- future.join();
- // we call getSession which throws an IOException if there has been an error connecting
- try
- {
- future.getSession();
- }
- catch (RuntimeIOException e)
- {
- _connectionMonitor.failed(e);
- _logger.error(new LogMessage("Could not connect to {0}: {1}", this, e), e);
- throw e;
- }
- return _connectionMonitor.waitUntilOpen();
- }
-
- void connectAsynch(Iterable<AMQMethodBody> msgs)
- {
- _replay = msgs;
- connectImpl();
- }
-
- void replay(Iterable<AMQMethodBody> msgs)
- {
- _replay = msgs;
- if(_connectionMonitor.isOpened())
- {
- replay();
- }
- }
-
- //establish connection, handling redirect if required...
- Broker connectToCluster() throws IOException, InterruptedException
- {
- connect();
- //wait until the connection is open or get a redirection
- if (_connectionMonitor.waitUntilOpen())
- {
- return this;
- }
- else
- {
- Broker broker = new MinaBrokerProxy(_connectionMonitor.getHost(), _connectionMonitor.getPort(), _local);
- broker.connect();
- return broker;
- }
- }
-
- public void send(AMQDataBlock data) throws AMQConnectionWaitException
- {
- if (_session == null)
- {
- try
- {
- _connectionMonitor.waitUntilOpen();
- }
- catch (InterruptedException e)
- {
- throw new AMQConnectionWaitException("Failed to send " + data + ": " + e, e);
- }
- }
- _session.write(data);
- }
-
- private void replay()
- {
- if(_replay != null)
- {
- for(AMQMethodBody b : _replay)
- {
- _session.write(new AMQFrame(0, b));
- }
- }
- }
-
- public void handle(int channel, AMQMethodBody method) throws AMQException
- {
- _logger.info(new LogMessage("Handling method: {0} for channel {1}", method, channel));
- if (!handleResponse(channel, method))
- {
- _logger.warn(new LogMessage("Unhandled method: {0} for channel {1}", method, channel));
- }
- }
-
- private void handleMethod(int channel, AMQMethodBody method) throws AMQException
- {
- if (method instanceof ConnectionRedirectBody)
- {
- //signal redirection to waiting thread
- ConnectionRedirectBody redirect = (ConnectionRedirectBody) method;
- String[] parts = redirect.host.toString().split(":");
- _connectionMonitor.redirect(parts[0], Integer.parseInt(parts[1]));
- }
- else
- {
- _handler.handle(channel, method);
- if (AMQState.CONNECTION_OPEN.equals(_legacyHandler.getCurrentState()) && _handler != this)
- {
- _handler = this;
- _logger.info(new LogMessage("Connection opened, handler switched"));
- //replay any messages:
- replay();
- //signal waiting thread:
- _connectionMonitor.opened();
- }
- }
- }
-
- private void handleFrame(AMQFrame frame) throws AMQException
- {
- AMQBody body = frame.getBodyFrame();
- if (body instanceof AMQMethodBody)
- {
- handleMethod(frame.getChannel(), (AMQMethodBody) body);
- }
- else
- {
- throw new AMQUnexpectedBodyTypeException(AMQMethodBody.class, body);
- }
- }
-
- public String toString()
- {
- return "MinaBrokerProxy[" + (_session == null ? super.toString() : _session.getRemoteAddress()) + "]";
- }
-
- private class MinaBinding extends IoHandlerAdapter
- {
- public void sessionCreated(IoSession session) throws Exception
- {
- init(session);
- _logger.info(new LogMessage("{0}: created", MinaBrokerProxy.this));
- ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false));
- session.getFilterChain().addLast("protocolFilter", pcf);
-
- /* Find last protocol version in protocol version list. Make sure last protocol version
- listed in the build file (build-module.xml) is the latest version which will be used
- here. */
-
- session.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()));
- }
-
- public void sessionOpened(IoSession session) throws Exception
- {
- _logger.info(new LogMessage("{0}: opened", MinaBrokerProxy.this));
- }
-
- public void sessionClosed(IoSession session) throws Exception
- {
- _logger.info(new LogMessage("{0}: closed", MinaBrokerProxy.this));
- }
-
- public void exceptionCaught(IoSession session, Throwable throwable) throws Exception
- {
- _logger.error(new LogMessage("{0}: received {1}", MinaBrokerProxy.this, throwable), throwable);
- if (! (throwable instanceof IOException))
- {
- _session.close();
- }
- failed();
- }
-
- public void messageReceived(IoSession session, Object object) throws Exception
- {
- if (object instanceof AMQFrame)
- {
- handleFrame((AMQFrame) object);
- }
- else
- {
- throw new AMQUnexpectedFrameTypeException("Received message of unrecognised type: " + object);
- }
- }
-
- public void messageSent(IoSession session, Object object) throws Exception
- {
- _logger.debug(new LogMessage("{0}: sent {1}", MinaBrokerProxy.this, object));
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java
deleted file mode 100644
index 5e70de7665..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-public class MinaBrokerProxyFactory implements BrokerFactory
-{
- private final MemberHandle _local;
-
- MinaBrokerProxyFactory(MemberHandle local)
- {
- _local = local;
- }
-
- public Broker create(MemberHandle handle)
- {
- return new MinaBrokerProxy(handle.getHost(), handle.getPort(), _local);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java
deleted file mode 100644
index fe76ca6505..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.framing.AMQMethodBody;
-
-public interface ResponseHandler
-{
- public void responded(AMQMethodBody response);
-
- public void removed();
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java
deleted file mode 100644
index 159612331c..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-
-public interface Sendable
-{
- public void send(int channel, Member member) throws AMQException;
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java
deleted file mode 100644
index aadcfa4b4c..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.state.AMQState;
-import org.apache.qpid.server.state.AMQStateManager;
-//import org.apache.qpid.server.state.IllegalStateTransitionException;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * An extension of server.AMQStateManager that allows different handlers to be registered.
- *
- */
-class ServerHandlerRegistry extends AMQStateManager
-{
- private final Logger _logger = Logger.getLogger(ServerHandlerRegistry.class);
- private final Map<AMQState, MethodHandlerRegistry> _handlers = new HashMap<AMQState, MethodHandlerRegistry>();
-
- ServerHandlerRegistry(VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession)
- {
- super(AMQState.CONNECTION_NOT_STARTED, false, virtualHostRegistry, protocolSession);
- }
-
- ServerHandlerRegistry(ServerHandlerRegistry s, VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession)
- {
- this(virtualHostRegistry, protocolSession);
- _handlers.putAll(s._handlers);
- }
-
- ServerHandlerRegistry(MethodHandlerFactory factory, VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession)
- {
- this(virtualHostRegistry, protocolSession);
- init(factory);
- }
-
- void setHandlers(AMQState state, MethodHandlerRegistry handlers)
- {
- _handlers.put(state, handlers);
- }
-
- void init(MethodHandlerFactory factory)
- {
- for (AMQState s : AMQState.values())
- {
- setHandlers(s, factory.register(s, new MethodHandlerRegistry()));
- }
- }
-
- protected <B extends AMQMethodBody> StateAwareMethodListener<B> findStateTransitionHandler(AMQState state, B frame) //throws IllegalStateTransitionException
- {
- MethodHandlerRegistry registry = _handlers.get(state);
- StateAwareMethodListener<B> handler = (registry == null) ? null : registry.getHandler(frame);
- if (handler == null)
- {
- _logger.warn(new LogMessage("No handler for {0}, {1}", state, frame));
- }
- return handler;
- }
-
- <A extends AMQMethodBody, B extends Class<A>> void addHandler(AMQState state, B type, StateAwareMethodListener<A> handler)
- {
- MethodHandlerRegistry registry = _handlers.get(state);
- if (registry == null)
- {
- registry = new MethodHandlerRegistry();
- _handlers.put(state, registry);
- }
- registry.addHandler(type, handler);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java
deleted file mode 100644
index 1255094b1d..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.framing.AMQShortString;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class SimpleMemberHandle implements MemberHandle
-{
- private final String _host;
- private final int _port;
-
- public SimpleMemberHandle(String host, int port)
- {
- _host = host;
- _port = port;
- }
-
- public SimpleMemberHandle(AMQShortString details)
- {
- this(details.toString());
- }
-
- public SimpleMemberHandle(String details)
- {
- String[] parts = details.split(":");
- _host = parts[0];
- _port = Integer.parseInt(parts[1]);
- }
-
- public SimpleMemberHandle(InetSocketAddress address) throws UnknownHostException
- {
- this(address.getAddress(), address.getPort());
- }
-
- public SimpleMemberHandle(InetAddress address, int port) throws UnknownHostException
- {
- this(canonical(address).getHostAddress(), port);
- }
-
- public String getHost()
- {
- return _host;
- }
-
- public int getPort()
- {
- return _port;
- }
-
- public int hashCode()
- {
- return getPort();
- }
-
- public boolean equals(Object o)
- {
- return o instanceof MemberHandle && matches((MemberHandle) o);
- }
-
- public boolean matches(MemberHandle m)
- {
- return matches(m.getHost(), m.getPort());
- }
-
- public boolean matches(String host, int port)
- {
- return _host.equals(host) && _port == port;
- }
-
- public AMQShortString getDetails()
- {
- return new AMQShortString(_host + ":" + _port);
- }
-
- public String toString()
- {
- return getDetails().toString();
- }
-
- static List<MemberHandle> stringToMembers(String membership)
- {
- String[] names = membership.split("\\s");
- List<MemberHandle> members = new ArrayList<MemberHandle>();
- for (String name : names)
- {
- members.add(new SimpleMemberHandle(name));
- }
- return members;
- }
-
- static String membersToString(List<MemberHandle> members)
- {
- StringBuffer buffer = new StringBuffer();
- boolean first = true;
- for (MemberHandle m : members)
- {
- if (first)
- {
- first = false;
- }
- else
- {
- buffer.append(" ");
- }
- buffer.append(m.getDetails());
- }
-
- return buffer.toString();
- }
-
- private static InetAddress canonical(InetAddress address) throws UnknownHostException
- {
- if (address.isLoopbackAddress())
- {
- return InetAddress.getLocalHost();
- }
- else
- {
- return address;
- }
- }
-
- public MemberHandle resolve()
- {
- return resolve(this);
- }
-
- public static MemberHandle resolve(MemberHandle handle)
- {
- try
- {
- return new SimpleMemberHandle(new InetSocketAddress(handle.getHost(), handle.getPort()));
- }
- catch (UnknownHostException e)
- {
- e.printStackTrace();
- return handle;
- }
- }
-
-
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java
deleted file mode 100644
index 3699a9e128..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.amqp_8_0.MethodConverter_8_0;
-import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
-import org.apache.qpid.server.queue.AMQMessage;
-
-import java.util.Iterator;
-
-public class SimpleSendable implements Sendable
-{
-
- //todo fixme - remove 0-8 hard coding
- ProtocolVersionMethodConverter _methodConverter = new MethodConverter_8_0();
-
- private final AMQMessage _message;
-
- public SimpleSendable(AMQMessage message)
- {
- _message = message;
- }
-
- public void send(int channel, Member member) throws AMQException
- {
- member.send(new AMQFrame(channel, _methodConverter.convertToBody(_message.getMessagePublishInfo())));
- member.send(new AMQFrame(channel, _message.getContentHeaderBody()));
- Iterator<ContentChunk> it = _message.getContentBodyIterator();
- while (it.hasNext())
- {
- member.send(new AMQFrame(channel, _methodConverter.convertToBody(it.next())));
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java
deleted file mode 100644
index 86710e8a31..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-
-import java.util.List;
-import java.util.ArrayList;
-
-public class ChainedClusterMethodHandler <A extends AMQMethodBody> extends ClusterMethodHandler<A>
-{
- private final List<ClusterMethodHandler<A>> _handlers;
-
- private ChainedClusterMethodHandler()
- {
- this(new ArrayList<ClusterMethodHandler<A>>());
- }
-
- public ChainedClusterMethodHandler(List<ClusterMethodHandler<A>> handlers)
- {
- _handlers = handlers;
- }
-
- public ChainedClusterMethodHandler(ClusterMethodHandler<A>... handlers)
- {
- this();
- for(ClusterMethodHandler<A>handler: handlers)
- {
- _handlers.add(handler);
- }
- }
-
- protected final void peer(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- for(ClusterMethodHandler<A> handler : _handlers)
- {
- handler.peer(stateMgr, evt);
- }
- }
-
- protected final void client(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- for(ClusterMethodHandler<A> handler : _handlers)
- {
- handler.client(stateMgr, evt);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java
deleted file mode 100644
index c9f6dbfb37..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.*;
-import org.apache.log4j.Logger;
-
-import java.util.Map;
-import java.util.HashMap;
-
-/**
- * Maintains the default queue names for a channel, and alters subsequent frames where necessary
- * to use this (i.e. when no queue is explictly specified).
- *
- */
-class ChannelQueueManager
-{
- private static final Logger _logger = Logger.getLogger(ChannelQueueManager.class);
- private final Map<Integer, AMQShortString> _channelQueues = new HashMap<Integer, AMQShortString>();
-
- ClusterMethodHandler<QueueDeclareBody> createQueueDeclareHandler()
- {
- return new QueueDeclareHandler();
- }
-
- ClusterMethodHandler<QueueDeleteBody> createQueueDeleteHandler()
- {
- return new QueueDeleteHandler();
- }
-
- ClusterMethodHandler<QueueBindBody> createQueueBindHandler()
- {
- return new QueueBindHandler();
- }
-
- ClusterMethodHandler<BasicConsumeBody> createBasicConsumeHandler()
- {
- return new BasicConsumeHandler();
- }
-
- private void set(int channel, AMQShortString queue)
- {
- _channelQueues.put(channel, queue);
- _logger.info(new LogMessage("Set default queue for {0} to {1}", channel, queue));
- }
-
- private AMQShortString get(int channel)
- {
- AMQShortString queue = _channelQueues.get(channel);
- _logger.info(new LogMessage("Default queue for {0} is {1}", channel, queue));
- return queue;
- }
-
- private class QueueDeclareHandler extends ClusterMethodHandler<QueueDeclareBody>
- {
- protected void peer(AMQStateManager stateMgr, AMQMethodEvent<QueueDeclareBody> evt) throws AMQException
- {
- }
-
- protected void client(AMQStateManager stateMgr, AMQMethodEvent<QueueDeclareBody> evt) throws AMQException
- {
- set(evt.getChannelId(), evt.getMethod().queue);
- }
- }
- private class QueueBindHandler extends ClusterMethodHandler<QueueBindBody>
- {
- protected void peer(AMQStateManager stateMgr, AMQMethodEvent<QueueBindBody> evt) throws AMQException
- {
- }
-
- protected void client(AMQStateManager stateMgr, AMQMethodEvent<QueueBindBody> evt) throws AMQException
- {
- if(evt.getMethod().queue == null)
- {
- evt.getMethod().queue = get(evt.getChannelId());
- }
- }
- }
- private class QueueDeleteHandler extends ClusterMethodHandler<QueueDeleteBody>
- {
- protected void peer(AMQStateManager stateMgr, AMQMethodEvent<QueueDeleteBody> evt) throws AMQException
- {
- }
-
- protected void client(AMQStateManager stateMgr, AMQMethodEvent<QueueDeleteBody> evt) throws AMQException
- {
- if(evt.getMethod().queue == null)
- {
- evt.getMethod().queue = get(evt.getChannelId());
- }
- }
- }
-
- private class BasicConsumeHandler extends ClusterMethodHandler<BasicConsumeBody>
- {
- protected void peer(AMQStateManager stateMgr, AMQMethodEvent<BasicConsumeBody> evt) throws AMQException
- {
- }
-
- protected void client(AMQStateManager stateMgr, AMQMethodEvent<BasicConsumeBody> evt) throws AMQException
- {
- if(evt.getMethod().queue == null)
- {
- evt.getMethod().queue = get(evt.getChannelId());
- }
- }
- }
-
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java
deleted file mode 100644
index faab99b0f6..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.AMQException;
-
-public abstract class ClusterMethodHandler<A extends AMQMethodBody> implements StateAwareMethodListener<A>
-{
- public final void methodReceived(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- AMQProtocolSession session = stateMgr.getProtocolSession();
-
- if (ClusteredProtocolSession.isPeerSession(session))
- {
- peer(stateMgr, evt);
- }
- else
- {
- client(stateMgr, evt);
- }
- }
-
- protected abstract void peer(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException;
- protected abstract void client(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException;
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java
deleted file mode 100644
index e7509da32a..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.server.cluster.ClusterCapability;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.LoadTable;
-import org.apache.qpid.server.cluster.MemberHandle;
-import org.apache.qpid.server.cluster.MethodHandlerFactory;
-import org.apache.qpid.server.cluster.MethodHandlerRegistry;
-import org.apache.qpid.server.cluster.SimpleMemberHandle;
-import org.apache.qpid.server.handler.ChannelCloseHandler;
-import org.apache.qpid.server.handler.ChannelFlowHandler;
-import org.apache.qpid.server.handler.ChannelOpenHandler;
-import org.apache.qpid.server.handler.ConnectionCloseMethodHandler;
-import org.apache.qpid.server.handler.ConnectionOpenMethodHandler;
-import org.apache.qpid.server.handler.ConnectionSecureOkMethodHandler;
-import org.apache.qpid.server.handler.ConnectionStartOkMethodHandler;
-import org.apache.qpid.server.handler.ConnectionTuneOkMethodHandler;
-import org.apache.qpid.server.handler.ExchangeDeclareHandler;
-import org.apache.qpid.server.handler.ExchangeDeleteHandler;
-import org.apache.qpid.server.handler.BasicCancelMethodHandler;
-import org.apache.qpid.server.handler.BasicPublishMethodHandler;
-import org.apache.qpid.server.handler.QueueBindHandler;
-import org.apache.qpid.server.handler.QueueDeleteHandler;
-import org.apache.qpid.server.handler.BasicQosHandler;
-import org.apache.qpid.server.handler.TxSelectHandler;
-import org.apache.qpid.server.handler.TxCommitHandler;
-import org.apache.qpid.server.handler.TxRollbackHandler;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.state.AMQState;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-public class ClusterMethodHandlerFactory implements MethodHandlerFactory
-{
- private final GroupManager _groupMgr;
- private final LoadTable _loadTable;
-
- public ClusterMethodHandlerFactory(GroupManager groupMgr, LoadTable loadTable)
- {
- _groupMgr = groupMgr;
- _loadTable = loadTable;
- }
-
- public MethodHandlerRegistry register(AMQState state, MethodHandlerRegistry registry)
- {
- switch (state)
- {
- case CONNECTION_NOT_STARTED:
- return registry.addHandler(ConnectionStartOkBody.class, ConnectionStartOkMethodHandler.getInstance());
- case CONNECTION_NOT_AUTH:
- return registry.addHandler(ConnectionSecureOkBody.class, ConnectionSecureOkMethodHandler.getInstance());
- case CONNECTION_NOT_TUNED:
- return registry.addHandler(ConnectionTuneOkBody.class, ConnectionTuneOkMethodHandler.getInstance());
- case CONNECTION_NOT_OPENED:
- //connection.open override:
- return registry.addHandler(ConnectionOpenBody.class, new ConnectionOpenHandler());
- case CONNECTION_OPEN:
- return registerConnectionOpened(registry);
- }
- return registry;
- }
-
- private MethodHandlerRegistry registerConnectionOpened(MethodHandlerRegistry registry)
- {
- //new cluster method handlers:
- registry.addHandler(ClusterJoinBody.class, new JoinHandler());
- registry.addHandler(ClusterLeaveBody.class, new LeaveHandler());
- registry.addHandler(ClusterSuspectBody.class, new SuspectHandler());
- registry.addHandler(ClusterMembershipBody.class, new MembershipHandler());
- registry.addHandler(ClusterPingBody.class, new PingHandler());
- registry.addHandler(ClusterSynchBody.class, new SynchHandler());
-
- //connection.close override:
- registry.addHandler(ConnectionCloseBody.class, new ConnectionCloseHandler());
-
- //replicated handlers:
- registry.addHandler(ExchangeDeclareBody.class, replicated(ExchangeDeclareHandler.getInstance()));
- registry.addHandler(ExchangeDeleteBody.class, replicated(ExchangeDeleteHandler.getInstance()));
-
- ChannelQueueManager channelQueueMgr = new ChannelQueueManager();
-
-
- LocalQueueDeclareHandler handler = new LocalQueueDeclareHandler(_groupMgr);
- registry.addHandler(QueueDeclareBody.class,
- chain(new QueueNameGenerator(handler),
- channelQueueMgr.createQueueDeclareHandler(),
- new ReplicatingHandler<QueueDeclareBody>(_groupMgr, handler)));
-
- registry.addHandler(QueueBindBody.class, chain(channelQueueMgr.createQueueBindHandler(), replicated(QueueBindHandler.getInstance())));
- registry.addHandler(QueueDeleteBody.class, chain(channelQueueMgr.createQueueDeleteHandler(), replicated(alternate(new QueueDeleteHandler(false), new QueueDeleteHandler(true)))));
- registry.addHandler(BasicConsumeBody.class, chain(channelQueueMgr.createBasicConsumeHandler(), new ReplicatingConsumeHandler(_groupMgr)));
-
- //other modified handlers:
- registry.addHandler(BasicCancelBody.class, alternate(new RemoteCancelHandler(), BasicCancelMethodHandler.getInstance()));
-
- //other unaffected handlers:
- registry.addHandler(BasicPublishBody.class, BasicPublishMethodHandler.getInstance());
- registry.addHandler(BasicQosBody.class, BasicQosHandler.getInstance());
- registry.addHandler(ChannelOpenBody.class, ChannelOpenHandler.getInstance());
- registry.addHandler(ChannelCloseBody.class, ChannelCloseHandler.getInstance());
- registry.addHandler(ChannelFlowBody.class, ChannelFlowHandler.getInstance());
- registry.addHandler(TxSelectBody.class, TxSelectHandler.getInstance());
- registry.addHandler(TxCommitBody.class, TxCommitHandler.getInstance());
- registry.addHandler(TxRollbackBody.class, TxRollbackHandler.getInstance());
-
-
- return registry;
- }
-
- private class SynchHandler implements StateAwareMethodListener<ClusterSynchBody>
- {
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ClusterSynchBody> evt) throws AMQException
- {
- _groupMgr.handleSynch(ClusteredProtocolSession.getSessionPeer(stateManager.getProtocolSession()));
- }
- }
-
- private class JoinHandler implements StateAwareMethodListener<ClusterJoinBody>
- {
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ClusterJoinBody> evt) throws AMQException
- {
- _groupMgr.handleJoin(new SimpleMemberHandle(evt.getMethod().broker));
- }
- }
-
- private class LeaveHandler implements StateAwareMethodListener<ClusterLeaveBody>
- {
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ClusterLeaveBody> evt) throws AMQException
- {
- _groupMgr.handleLeave(new SimpleMemberHandle(evt.getMethod().broker));
- }
- }
-
- private class SuspectHandler implements StateAwareMethodListener<ClusterSuspectBody>
- {
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ClusterSuspectBody> evt) throws AMQException
- {
- _groupMgr.handleSuspect(new SimpleMemberHandle(evt.getMethod().broker));
- }
- }
-
- private class MembershipHandler implements StateAwareMethodListener<ClusterMembershipBody>
- {
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ClusterMembershipBody> evt) throws AMQException
- {
- ClusterMembershipBody body = evt.getMethod();
- _groupMgr.handleMembershipAnnouncement(new String(body.members));
- }
- }
-
- private class PingHandler implements StateAwareMethodListener<ClusterPingBody>
- {
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ClusterPingBody> evt) throws AMQException
- {
- MemberHandle peer = new SimpleMemberHandle(evt.getMethod().broker);
- _groupMgr.handlePing(peer, evt.getMethod().load);
- if (evt.getMethod().responseRequired)
- {
- evt.getMethod().load = _loadTable.getLocalLoad();
- stateManager.getProtocolSession().writeFrame(new AMQFrame(evt.getChannelId(), evt.getMethod()));
- }
- }
- }
-
- private class ConnectionOpenHandler extends ExtendedHandler<ConnectionOpenBody>
- {
- ConnectionOpenHandler()
- {
- super(ConnectionOpenMethodHandler.getInstance());
- }
-
- void postHandle(AMQStateManager stateMgr, AMQMethodEvent<ConnectionOpenBody> evt)
- {
- AMQShortString capabilities = evt.getMethod().capabilities;
- if (ClusterCapability.contains(capabilities))
- {
- ClusteredProtocolSession.setSessionPeer(stateMgr.getProtocolSession(), ClusterCapability.getPeer(capabilities));
- }
- else
- {
- _loadTable.incrementLocalLoad();
- }
- }
- }
-
- private class ConnectionCloseHandler extends ExtendedHandler<ConnectionCloseBody>
- {
- ConnectionCloseHandler()
- {
- super(ConnectionCloseMethodHandler.getInstance());
- }
-
- void postHandle(AMQStateManager stateMgr, AMQMethodEvent<ConnectionCloseBody> evt)
- {
- if (!ClusteredProtocolSession.isPeerSession(stateMgr.getProtocolSession()))
- {
- _loadTable.decrementLocalLoad();
- }
- }
- }
-
- private <B extends AMQMethodBody> ReplicatingHandler<B> replicated(StateAwareMethodListener<B> handler)
- {
- return new ReplicatingHandler<B>(_groupMgr, handler);
- }
-
- private <B extends AMQMethodBody> StateAwareMethodListener<B> alternate(StateAwareMethodListener<B> peer, StateAwareMethodListener<B> client)
- {
- return new PeerHandler<B>(peer, client);
- }
-
- private <B extends AMQMethodBody> StateAwareMethodListener<B> chain(ClusterMethodHandler<B>... h)
- {
- return new ChainedClusterMethodHandler<B>(h);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java
deleted file mode 100644
index a2f62f714b..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-class ExtendedHandler<A extends AMQMethodBody> implements StateAwareMethodListener<A>
-{
- private final StateAwareMethodListener<A> _base;
-
- ExtendedHandler(StateAwareMethodListener<A> base)
- {
- _base = base;
- }
-
- public void methodReceived(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- preHandle(stateMgr, evt);
- _base.methodReceived(stateMgr, evt);
- postHandle(stateMgr, evt);
- }
-
- void preHandle(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- }
-
- void postHandle(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java
deleted file mode 100644
index 0dc7fe00d2..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-public abstract class HandlerUtils
-{
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java
deleted file mode 100644
index f01a8349f2..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.QueueDeclareBody;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.cluster.MemberHandle;
-import org.apache.qpid.server.handler.QueueDeclareHandler;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.ClusteredQueue;
-import org.apache.qpid.server.queue.PrivateQueue;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.queue.RemoteQueueProxy;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-public class LocalQueueDeclareHandler extends QueueDeclareHandler
-{
- private static final Logger _logger = Logger.getLogger(LocalQueueDeclareHandler.class);
- private final GroupManager _groupMgr;
-
- LocalQueueDeclareHandler(GroupManager groupMgr)
- {
- _groupMgr = groupMgr;
- }
-
- protected AMQShortString createName()
- {
- return new AMQShortString(super.createName().toString() + "@" + _groupMgr.getLocal().getDetails());
- }
-
- protected AMQQueue createQueue(QueueDeclareBody body, VirtualHost virtualHost, AMQProtocolSession session) throws AMQException
- {
- //is it private or shared:
- if (body.exclusive)
- {
- if (ClusteredProtocolSession.isPeerSession(session))
- {
- //need to get peer from the session...
- MemberHandle peer = ClusteredProtocolSession.getSessionPeer(session);
- _logger.debug(new LogMessage("Creating proxied queue {0} on behalf of {1}", body.queue, peer));
- return new RemoteQueueProxy(peer, _groupMgr, body.queue, body.durable, new AMQShortString(peer.getDetails()), body.autoDelete, virtualHost);
- }
- else
- {
- _logger.debug(new LogMessage("Creating local private queue {0}", body.queue));
- return new PrivateQueue(_groupMgr, body.queue, body.durable, session.getContextKey(), body.autoDelete, virtualHost);
- }
- }
- else
- {
- _logger.debug(new LogMessage("Creating local shared queue {0}", body.queue));
- return new ClusteredQueue(_groupMgr, body.queue, body.durable, null, body.autoDelete, virtualHost);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java
deleted file mode 100644
index 8b0bb4b127..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-public class NullListener<T extends AMQMethodBody> implements StateAwareMethodListener<T>
-{
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<T> evt) throws AMQException
- {
- }
-}
-
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java
deleted file mode 100644
index 447e51ccd9..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-/**
- * Base for implementing handlers that carry out different actions based on whether the method they
- * are handling was sent by a peer (i.e. another broker in the cluster) or a client (i.e. an end-user
- * application).
- *
- */
-public class PeerHandler<A extends AMQMethodBody> extends ClusterMethodHandler<A>
-{
- private final StateAwareMethodListener<A> _peer;
- private final StateAwareMethodListener<A> _client;
-
- PeerHandler(StateAwareMethodListener<A> peer, StateAwareMethodListener<A> client)
- {
- _peer = peer;
- _client = client;
- }
-
- protected void peer(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- _peer.methodReceived(stateMgr, evt);
- }
-
- protected void client(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- _client.methodReceived(stateMgr, evt);
- }
-
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java
deleted file mode 100644
index a669171d3c..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.QueueDeclareBody;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-
-/**
- * Generates queue names for queues declared with no name.
- *
- */
-class QueueNameGenerator extends ClusterMethodHandler<QueueDeclareBody>
-{
- private final LocalQueueDeclareHandler _handler;
-
- QueueNameGenerator(LocalQueueDeclareHandler handler)
- {
- _handler = handler;
- }
-
- protected void peer(AMQStateManager stateMgr, AMQMethodEvent<QueueDeclareBody> evt) throws AMQException
- {
- }
-
- protected void client(AMQStateManager stateMgr, AMQMethodEvent<QueueDeclareBody> evt)
- throws AMQException
- {
- setName(evt.getMethod());//need to set the name before propagating this method
- }
-
- protected void setName(QueueDeclareBody body)
- {
- if (body.queue == null)
- {
- body.queue = _handler.createName();
- }
- }
-}
-
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java
deleted file mode 100644
index f09763e1ad..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.BasicCancelBody;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.ClusteredQueue;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-public class RemoteCancelHandler implements StateAwareMethodListener<BasicCancelBody>
-{
- private final Logger _logger = Logger.getLogger(RemoteCancelHandler.class);
-
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicCancelBody> evt) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
- VirtualHost virtualHost = session.getVirtualHost();
- QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
-
-
- //By convention, consumers setup between brokers use the queue name as the consumer tag:
- AMQQueue queue = queueRegistry.getQueue(evt.getMethod().consumerTag);
- if (queue instanceof ClusteredQueue)
- {
- ((ClusteredQueue) queue).removeRemoteSubscriber(ClusteredProtocolSession.getSessionPeer(session));
- }
- else
- {
- _logger.warn("Got remote cancel request for non-clustered queue: " + queue);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java
deleted file mode 100644
index 073b13688c..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.BasicConsumeBody;
-import org.apache.qpid.framing.BasicConsumeOkBody;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.ClusteredQueue;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-/**
- * Handles consume requests from other cluster members.
- *
- */
-public class RemoteConsumeHandler implements StateAwareMethodListener<BasicConsumeBody>
-{
- private final Logger _logger = Logger.getLogger(RemoteConsumeHandler.class);
-
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicConsumeBody> evt) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
- VirtualHost virtualHost = session.getVirtualHost();
- QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
-
- AMQQueue queue = queueRegistry.getQueue(evt.getMethod().queue);
- if (queue instanceof ClusteredQueue)
- {
- ((ClusteredQueue) queue).addRemoteSubcriber(ClusteredProtocolSession.getSessionPeer(session));
- // 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.
- session.writeFrame(BasicConsumeOkBody.createAMQFrame(evt.getChannelId(),
- (byte)8, (byte)0, // AMQP version (major, minor)
- evt.getMethod().queue // consumerTag
- ));
- }
- else
- {
- _logger.warn("Got remote consume request for non-clustered queue: " + queue);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java
deleted file mode 100644
index 897f8e4fb7..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.BasicConsumeBody;
-import org.apache.qpid.server.cluster.BroadcastPolicy;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.handler.BasicConsumeMethodHandler;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-public class ReplicatingConsumeHandler extends ReplicatingHandler<BasicConsumeBody>
-{
- ReplicatingConsumeHandler(GroupManager groupMgr)
- {
- this(groupMgr, null);
- }
-
- ReplicatingConsumeHandler(GroupManager groupMgr, BroadcastPolicy policy)
- {
- super(groupMgr, base(), policy);
- }
-
- protected void replicate(AMQStateManager stateManager, AMQMethodEvent<BasicConsumeBody> evt) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
- VirtualHost virtualHost = session.getVirtualHost();
- ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
- QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
-
- //only replicate if the queue in question is a shared queue
- if (isShared(queueRegistry.getQueue(evt.getMethod().queue)))
- {
- super.replicate(stateManager, evt);
- }
- else
- {
- _logger.info(new LogMessage("Handling consume for private queue ({0}) locally", evt.getMethod()));
- local(stateManager, evt);
- _logger.info(new LogMessage("Handled consume for private queue ({0}) locally", evt.getMethod()));
-
- }
- }
-
- protected boolean isShared(AMQQueue queue)
- {
- return queue != null && queue.isShared();
- }
-
- static StateAwareMethodListener<BasicConsumeBody> base()
- {
- return new PeerHandler<BasicConsumeBody>(peer(), client());
- }
-
- static StateAwareMethodListener<BasicConsumeBody> peer()
- {
- return new RemoteConsumeHandler();
- }
-
- static StateAwareMethodListener<BasicConsumeBody> client()
- {
- return BasicConsumeMethodHandler.getInstance();
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java
deleted file mode 100644
index 888fa4e426..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.cluster.*;
-import org.apache.qpid.server.cluster.policy.StandardPolicies;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import java.util.List;
-
-/**
- * Basic template for handling methods that should be broadcast to the group and
- * processed locally after 'completion' of this broadcast.
- *
- */
-class ReplicatingHandler<A extends AMQMethodBody> extends ClusterMethodHandler<A> implements StandardPolicies
-{
- protected static final Logger _logger = Logger.getLogger(ReplicatingHandler.class);
-
- private final StateAwareMethodListener<A> _base;
- private final GroupManager _groupMgr;
- private final BroadcastPolicy _policy;
-
- ReplicatingHandler(GroupManager groupMgr, StateAwareMethodListener<A> base)
- {
- this(groupMgr, base, null);
- }
-
- ReplicatingHandler(GroupManager groupMgr, StateAwareMethodListener<A> base, BroadcastPolicy policy)
- {
- _groupMgr = groupMgr;
- _base = base;
- _policy = policy;
- }
-
- protected void peer(AMQStateManager stateManager, AMQMethodEvent<A> evt) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
- VirtualHost virtualHost = session.getVirtualHost();
- ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
- QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
-
- local(stateManager, evt);
- _logger.debug(new LogMessage("Handled {0} locally", evt.getMethod()));
- }
-
- protected void client(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- replicate(stateMgr, evt);
- }
-
- protected void replicate(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- if (_policy == null)
- {
- //asynch delivery
- _groupMgr.broadcast(new SimpleBodySendable(evt.getMethod()));
- local(stateMgr, evt);
- }
- else
- {
- Callback callback = new Callback(stateMgr, evt);
- _groupMgr.broadcast(new SimpleBodySendable(evt.getMethod()), _policy, callback);
- }
- _logger.debug(new LogMessage("Replicated {0} to peers", evt.getMethod()));
- }
-
- protected void local(AMQStateManager stateMgr, AMQMethodEvent<A> evt) throws AMQException
- {
- _base.methodReceived(stateMgr, evt);
- }
-
- private class Callback implements GroupResponseHandler
- {
- private final AMQStateManager _stateMgr;
- private final AMQMethodEvent<A> _evt;
-
- Callback(AMQStateManager stateMgr, AMQMethodEvent<A> evt)
- {
- _stateMgr = stateMgr;
- _evt = evt;
- }
-
- public void response(List<AMQMethodBody> responses, List<Member> members)
- {
- try
- {
- local(_stateMgr, _evt);
- _logger.debug(new LogMessage("Handled {0} locally, in response to completion of replication", _evt.getMethod()));
- }
- catch (AMQException e)
- {
- _logger.error(new LogMessage("Error handling {0}:{1}", _evt.getMethod(), e), e);
- }
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java
deleted file mode 100644
index 8b0c638d63..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-public class WrappedListener<T extends AMQMethodBody> implements StateAwareMethodListener<T>
-{
- private final StateAwareMethodListener<T> _primary;
- private final StateAwareMethodListener _post;
- private final StateAwareMethodListener _pre;
-
- WrappedListener(StateAwareMethodListener<T> primary, StateAwareMethodListener pre, StateAwareMethodListener post)
- {
- _pre = check(pre);
- _post = check(post);
- _primary = check(primary);
- }
-
- public void methodReceived(AMQStateManager stateMgr, AMQMethodEvent<T> evt) throws AMQException
- {
- _pre.methodReceived(stateMgr, evt);
- _primary.methodReceived(stateMgr, evt);
- _post.methodReceived(stateMgr, evt);
- }
-
- private static <T extends AMQMethodBody> StateAwareMethodListener<T> check(StateAwareMethodListener<T> in)
- {
- return in == null ? new NullListener<T>() : in;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java
deleted file mode 100644
index 5ec3c9660a..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.handler;
-
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.cluster.MethodHandlerFactory;
-import org.apache.qpid.server.cluster.MethodHandlerRegistry;
-import org.apache.qpid.server.state.AMQState;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-public abstract class WrappingMethodHandlerFactory implements MethodHandlerFactory
-{
- private final MethodHandlerFactory _delegate;
- private final StateAwareMethodListener _pre;
- private final StateAwareMethodListener _post;
-
- protected WrappingMethodHandlerFactory(MethodHandlerFactory delegate,
- StateAwareMethodListener pre,
- StateAwareMethodListener post)
- {
- _delegate = delegate;
- _pre = pre;
- _post = post;
- }
-
- public MethodHandlerRegistry register(AMQState state, MethodHandlerRegistry registry)
- {
- if (isWrappableState(state))
- {
- return wrap(_delegate.register(state, registry), state);
- }
- else
- {
- return _delegate.register(state, registry);
- }
- }
-
- protected abstract boolean isWrappableState(AMQState state);
-
- protected abstract Iterable<FrameDescriptor> getWrappableFrameTypes(AMQState state);
-
- private MethodHandlerRegistry wrap(MethodHandlerRegistry registry, AMQState state)
- {
- for (FrameDescriptor fd : getWrappableFrameTypes(state))
- {
- wrap(registry, fd.type, fd.instance);
- }
- return registry;
- }
-
- private <A extends AMQMethodBody, B extends Class<A>> void wrap(MethodHandlerRegistry r, B type, A frame)
- {
- r.addHandler(type, new WrappedListener<A>(r.getHandler(frame), _pre, _post));
- }
-
- protected static class FrameDescriptor<A extends AMQMethodBody, B extends Class<A>>
- {
- protected final A instance;
- protected final B type;
-
- public FrameDescriptor(B type, A instance)
- {
- this.instance = instance;
- this.type = type;
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java
deleted file mode 100644
index 79cb558ede..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.policy;
-
-import org.apache.qpid.server.cluster.BroadcastPolicy;
-
-public class AsynchBroadcastPolicy implements BroadcastPolicy
-{
- public boolean isComplete(int responded, int members)
- {
- return true;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java
deleted file mode 100644
index 42382c6e7a..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.policy;
-
-import org.apache.qpid.server.cluster.BroadcastPolicy;
-
-public class MajorityResponseBroadcastPolicy implements BroadcastPolicy
-{
- public boolean isComplete(int responded, int members)
- {
- return responded > members / 2;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java
deleted file mode 100644
index e3072a6a40..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.policy;
-
-import org.apache.qpid.server.cluster.BroadcastPolicy;
-
-public class OneResponseBroadcastPolicy implements BroadcastPolicy
-{
- public boolean isComplete(int responded, int members)
- {
- return responded > 0;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java
deleted file mode 100644
index dbaf690d3a..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.policy;
-
-import org.apache.qpid.server.cluster.BroadcastPolicy;
-
-public interface StandardPolicies
-{
- public static final BroadcastPolicy ASYNCH_POLICY = new AsynchBroadcastPolicy();
- public static final BroadcastPolicy SYNCH_POLICY = new SynchBroadcastPolicy();
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java
deleted file mode 100644
index 605b8dd51e..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.policy;
-
-import org.apache.qpid.server.cluster.BroadcastPolicy;
-
-public class SynchBroadcastPolicy implements BroadcastPolicy
-{
- public boolean isComplete(int responded, int members)
- {
- return responded == members;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java
deleted file mode 100644
index 5a433b869b..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.replay;
-
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.framing.BasicConsumeBody;
-import org.apache.qpid.framing.AMQShortString;
-
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-
-class ConsumerCounts
-{
- private final Map<AMQShortString, Integer> _counts = new HashMap<AMQShortString, Integer>();
-
- synchronized void increment(AMQShortString queue)
- {
- _counts.put(queue, get(queue) + 1);
- }
-
- synchronized void decrement(AMQShortString queue)
- {
- _counts.put(queue, get(queue) - 1);
- }
-
- private int get(AMQShortString queue)
- {
- Integer count = _counts.get(queue);
- return count == null ? 0 : count;
- }
-
- synchronized void replay(List<AMQMethodBody> messages)
- {
- for(AMQShortString queue : _counts.keySet())
- {
- // 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.
- BasicConsumeBody m = new BasicConsumeBody((byte)8,
- (byte)0,
- BasicConsumeBody.getClazz((byte)8, (byte)0),
- BasicConsumeBody.getMethod((byte)8, (byte)0),
- null,
- queue,
- false,
- false,
- false,
- false,
- queue,
- 0);
- m.queue = queue;
- m.consumerTag = queue;
- replay(m, messages);
- }
- }
-
- private void replay(BasicConsumeBody msg, List<AMQMethodBody> messages)
- {
- int count = _counts.get(msg.queue);
- for(int i = 0; i < count; i++)
- {
- messages.add(msg);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java
deleted file mode 100644
index e45810438e..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.replay;
-
-import org.apache.qpid.framing.AMQMethodBody;
-
-/**
- * Abstraction through which a method can be recorded for replay
- *
- */
-interface MethodRecorder<T extends AMQMethodBody>
-{
- public void record(T method);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java
deleted file mode 100644
index 4d3fe1dbed..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.replay;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.framing.BasicCancelBody;
-import org.apache.qpid.framing.BasicConsumeBody;
-import org.apache.qpid.framing.ExchangeDeclareBody;
-import org.apache.qpid.framing.ExchangeDeleteBody;
-import org.apache.qpid.framing.QueueBindBody;
-import org.apache.qpid.framing.QueueDeclareBody;
-import org.apache.qpid.framing.QueueDeleteBody;
-import org.apache.qpid.server.cluster.MethodHandlerFactory;
-import org.apache.qpid.server.cluster.MethodHandlerRegistry;
-import org.apache.qpid.server.cluster.handler.WrappingMethodHandlerFactory;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQState;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-
-import java.util.Arrays;
-
-public class RecordingMethodHandlerFactory extends WrappingMethodHandlerFactory
-{
- // 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.
- private final byte major = (byte)8;
- private final byte minor = (byte)0;
- private final Iterable<FrameDescriptor> _frames = Arrays.asList(new FrameDescriptor[]
- {
- new FrameDescriptor(QueueDeclareBody.class, new QueueDeclareBody(major, minor, QueueDeclareBody.getClazz(major, minor), QueueDeclareBody.getMethod(major, minor),null,false,false,false,false,false,null,0)),
- new FrameDescriptor(QueueDeleteBody.class, new QueueDeleteBody(major, minor, QueueDeleteBody.getClazz(major, minor), QueueDeleteBody.getMethod(major, minor),false,false,false,null,0)),
- new FrameDescriptor(QueueBindBody.class, new QueueBindBody(major, minor, QueueBindBody.getClazz(major, minor), QueueBindBody.getMethod(major, minor),null,null,false,null,null,0)),
- new FrameDescriptor(ExchangeDeclareBody.class, new ExchangeDeclareBody(major, minor, ExchangeDeclareBody.getClazz(major, minor), ExchangeDeclareBody.getMethod(major, minor),null,false,false,null,false,false,false,0,null)),
- new FrameDescriptor(ExchangeDeleteBody.class, new ExchangeDeleteBody(major, minor, ExchangeDeleteBody.getClazz(major, minor), ExchangeDeleteBody.getMethod(major, minor),null,false,false,0)),
- new FrameDescriptor(BasicConsumeBody.class, new BasicConsumeBody(major, minor, BasicConsumeBody.getClazz(major, minor), BasicConsumeBody.getMethod(major, minor),null,null,false,false,false,false,null,0)),
- new FrameDescriptor(BasicCancelBody.class, new BasicCancelBody(major, minor, BasicCancelBody.getClazz(major, minor), BasicCancelBody.getMethod(major, minor),null,false))
- });
-
-
- public RecordingMethodHandlerFactory(MethodHandlerFactory factory, ReplayStore store)
- {
- super(factory, null, store);
- }
-
- protected boolean isWrappableState(AMQState state)
- {
- return AMQState.CONNECTION_OPEN.equals(state);
- }
-
- protected Iterable<FrameDescriptor> getWrappableFrameTypes(AMQState state)
- {
- return _frames;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java
deleted file mode 100644
index 898cb80cb3..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.replay;
-
-import org.apache.qpid.server.cluster.Sendable;
-import org.apache.qpid.framing.AMQDataBlock;
-import org.apache.qpid.framing.AMQMethodBody;
-
-import java.util.List;
-
-/**
- * Abstraction of a replay strategy for use in getting joining members up to
- * date with respect to cluster state.
- *
- */
-public interface ReplayManager
-{
- public List<AMQMethodBody> replay(boolean isLeader);
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java
deleted file mode 100644
index d7bbb1c36b..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.replay;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.*;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.cluster.util.Bindings;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.state.AMQStateManager;
-import org.apache.qpid.server.state.StateAwareMethodListener;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Stores method invocations for replay to new members.
- *
- */
-public class ReplayStore implements ReplayManager, StateAwareMethodListener
-{
- private static final Logger _logger = Logger.getLogger(ReplayStore.class);
-
- private final Map<Class<? extends AMQMethodBody>, MethodRecorder> _globalRecorders = new HashMap<Class<? extends AMQMethodBody>, MethodRecorder>();
- private final Map<Class<? extends AMQMethodBody>, MethodRecorder> _localRecorders = new HashMap<Class<? extends AMQMethodBody>, MethodRecorder>();
- private final Map<AMQShortString, QueueDeclareBody> _sharedQueues = new ConcurrentHashMap<AMQShortString, QueueDeclareBody>();
- private final Map<AMQShortString, QueueDeclareBody> _privateQueues = new ConcurrentHashMap<AMQShortString, QueueDeclareBody>();
- private final Bindings<AMQShortString, AMQShortString, QueueBindBody> _sharedBindings = new Bindings<AMQShortString, AMQShortString, QueueBindBody>();
- private final Bindings<AMQShortString, AMQShortString, QueueBindBody> _privateBindings = new Bindings<AMQShortString, AMQShortString, QueueBindBody>();
- private final Map<AMQShortString, ExchangeDeclareBody> _exchanges = new ConcurrentHashMap<AMQShortString, ExchangeDeclareBody>();
- private final ConsumerCounts _consumers = new ConsumerCounts();
-
- public ReplayStore()
- {
- _globalRecorders.put(QueueDeclareBody.class, new SharedQueueDeclareRecorder());
- _globalRecorders.put(QueueDeleteBody.class, new SharedQueueDeleteRecorder());
- _globalRecorders.put(QueueBindBody.class, new SharedQueueBindRecorder());
- _globalRecorders.put(ExchangeDeclareBody.class, new ExchangeDeclareRecorder());
- _globalRecorders.put(ExchangeDeleteBody.class, new ExchangeDeleteRecorder());
-
- _localRecorders.put(QueueDeclareBody.class, new PrivateQueueDeclareRecorder());
- _localRecorders.put(QueueDeleteBody.class, new PrivateQueueDeleteRecorder());
- _localRecorders.put(QueueBindBody.class, new PrivateQueueBindRecorder());
- _localRecorders.put(BasicConsumeBody.class, new BasicConsumeRecorder());
- _localRecorders.put(BasicCancelBody.class, new BasicCancelRecorder());
- _localRecorders.put(ExchangeDeclareBody.class, new ExchangeDeclareRecorder());
- _localRecorders.put(ExchangeDeleteBody.class, new ExchangeDeleteRecorder());
- }
-
- public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException
- {
- AMQProtocolSession session = stateManager.getProtocolSession();
- VirtualHost virtualHost = session.getVirtualHost();
-
- _logger.debug(new LogMessage("Replay store received {0}", evt.getMethod()));
- AMQMethodBody request = evt.getMethod();
-
- //allow any (relevant) recorder registered for this type of request to record it:
- MethodRecorder recorder = getRecorders(session).get(request.getClass());
- if (recorder != null)
- {
- recorder.record(request);
- }
- }
-
- private Map<Class<? extends AMQMethodBody>, MethodRecorder> getRecorders(AMQProtocolSession session)
- {
- if (ClusteredProtocolSession.isPeerSession(session))
- {
- return _globalRecorders;
- }
- else
- {
- return _localRecorders;
- }
- }
-
- public List<AMQMethodBody> replay(boolean isLeader)
- {
- List<AMQMethodBody> methods = new ArrayList<AMQMethodBody>();
- methods.addAll(_exchanges.values());
- methods.addAll(_privateQueues.values());
- synchronized(_privateBindings)
- {
- methods.addAll(_privateBindings.values());
- }
- if (isLeader)
- {
- methods.addAll(_sharedQueues.values());
- synchronized(_sharedBindings)
- {
- methods.addAll(_sharedBindings.values());
- }
- }
- _consumers.replay(methods);
- // 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.
- methods.add(new ClusterSynchBody((byte)8, (byte)0, ClusterSynchBody.getClazz((byte)8, (byte)0), ClusterSynchBody.getMethod((byte)8, (byte)0)));
- return methods;
- }
-
- private class BasicConsumeRecorder implements MethodRecorder<BasicConsumeBody>
- {
- public void record(BasicConsumeBody method)
- {
- if(_sharedQueues.containsKey(method.queue))
- {
- _consumers.increment(method.queue);
- }
- }
- }
-
- private class BasicCancelRecorder implements MethodRecorder<BasicCancelBody>
- {
- public void record(BasicCancelBody method)
- {
- if(_sharedQueues.containsKey(method.consumerTag))
- {
- _consumers.decrement(method.consumerTag);
- }
- }
- }
-
- private class SharedQueueDeclareRecorder extends QueueDeclareRecorder
- {
- SharedQueueDeclareRecorder()
- {
- super(false, _sharedQueues);
- }
- }
-
- private class PrivateQueueDeclareRecorder extends QueueDeclareRecorder
- {
- PrivateQueueDeclareRecorder()
- {
- super(true, _privateQueues, new SharedQueueDeclareRecorder());
- }
- }
-
- private class SharedQueueDeleteRecorder extends QueueDeleteRecorder
- {
- SharedQueueDeleteRecorder()
- {
- super(_sharedQueues, _sharedBindings);
- }
- }
-
- private class PrivateQueueDeleteRecorder extends QueueDeleteRecorder
- {
- PrivateQueueDeleteRecorder()
- {
- super(_privateQueues, _privateBindings, new SharedQueueDeleteRecorder());
- }
- }
-
- private class SharedQueueBindRecorder extends QueueBindRecorder
- {
- SharedQueueBindRecorder()
- {
- super(_sharedQueues, _sharedBindings);
- }
- }
-
- private class PrivateQueueBindRecorder extends QueueBindRecorder
- {
- PrivateQueueBindRecorder()
- {
- super(_privateQueues, _privateBindings, new SharedQueueBindRecorder());
- }
- }
-
-
- private static class QueueDeclareRecorder extends ChainedMethodRecorder<QueueDeclareBody>
- {
- private final boolean _exclusive;
- private final Map<AMQShortString, QueueDeclareBody> _queues;
-
- QueueDeclareRecorder(boolean exclusive, Map<AMQShortString, QueueDeclareBody> queues)
- {
- _queues = queues;
- _exclusive = exclusive;
- }
-
- QueueDeclareRecorder(boolean exclusive, Map<AMQShortString, QueueDeclareBody> queues, QueueDeclareRecorder recorder)
- {
- super(recorder);
- _queues = queues;
- _exclusive = exclusive;
- }
-
-
- protected boolean doRecord(QueueDeclareBody method)
- {
- if (_exclusive == method.exclusive)
- {
- _queues.put(method.queue, method);
- return true;
- }
- else
- {
- return false;
- }
- }
- }
-
- private class QueueDeleteRecorder extends ChainedMethodRecorder<QueueDeleteBody>
- {
- private final Map<AMQShortString, QueueDeclareBody> _queues;
- private final Bindings<AMQShortString, AMQShortString, QueueBindBody> _bindings;
-
- QueueDeleteRecorder(Map<AMQShortString, QueueDeclareBody> queues, Bindings<AMQShortString, AMQShortString, QueueBindBody> bindings)
- {
- this(queues, bindings, null);
- }
-
- QueueDeleteRecorder(Map<AMQShortString, QueueDeclareBody> queues, Bindings<AMQShortString, AMQShortString, QueueBindBody> bindings, QueueDeleteRecorder recorder)
- {
- super(recorder);
- _queues = queues;
- _bindings = bindings;
- }
-
- protected boolean doRecord(QueueDeleteBody method)
- {
- if (_queues.remove(method.queue) != null)
- {
- _bindings.unbind1(method.queue);
- return true;
- }
- else
- {
- return false;
- }
- }
- }
-
- private class QueueBindRecorder extends ChainedMethodRecorder<QueueBindBody>
- {
- private final Map<AMQShortString, QueueDeclareBody> _queues;
- private final Bindings<AMQShortString, AMQShortString, QueueBindBody> _bindings;
-
- QueueBindRecorder(Map<AMQShortString, QueueDeclareBody> queues, Bindings<AMQShortString, AMQShortString, QueueBindBody> bindings)
- {
- _queues = queues;
- _bindings = bindings;
- }
-
- QueueBindRecorder(Map<AMQShortString, QueueDeclareBody> queues, Bindings<AMQShortString, AMQShortString, QueueBindBody> bindings, QueueBindRecorder recorder)
- {
- super(recorder);
- _queues = queues;
- _bindings = bindings;
- }
-
- protected boolean doRecord(QueueBindBody method)
- {
- if (_queues.containsKey(method.queue))
- {
- _bindings.bind(method.queue, method.exchange, method);
- return true;
- }
- else
- {
- return false;
- }
- }
- }
-
- private class ExchangeDeclareRecorder implements MethodRecorder<ExchangeDeclareBody>
- {
- public void record(ExchangeDeclareBody method)
- {
- _exchanges.put(method.exchange, method);
- }
- }
-
- private class ExchangeDeleteRecorder implements MethodRecorder<ExchangeDeleteBody>
- {
- public void record(ExchangeDeleteBody method)
- {
- _exchanges.remove(method.exchange);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java
deleted file mode 100644
index 49de0a7cbf..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.util;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-
-/**
- * Maps two separate keys to a list of values.
- *
- */
-public class Bindings<K1, K2, V>
-{
- private final MultiValuedMap<K1, Binding<K2>> _a = new MultiValuedMap<K1, Binding<K2>>();
- private final MultiValuedMap<K2, Binding<K1>> _b = new MultiValuedMap<K2, Binding<K1>>();
- private final Collection<V> _values = new HashSet<V>();
-
- public void bind(K1 key1, K2 key2, V value)
- {
- _a.add(key1, new Binding<K2>(key2, value));
- _b.add(key2, new Binding<K1>(key1, value));
- _values.add(value);
- }
-
- public void unbind1(K1 key1)
- {
- Collection<Binding<K2>> values = _a.remove(key1);
- for (Binding<K2> v : values)
- {
- _b.remove(v.key);
- _values.remove(v.value);
- }
- }
-
- public void unbind2(K2 key2)
- {
- Collection<Binding<K1>> values = _b.remove(key2);
- for (Binding<K1> v : values)
- {
- _a.remove(v.key);
- _values.remove(v.value);
- }
- }
-
- public Collection<V> values()
- {
- return Collections.unmodifiableCollection(_values);
- }
-
- /**
- * Value needs to hold key to the other map
- */
- private class Binding<T>
- {
- private final T key;
- private final V value;
-
- Binding(T key, V value)
- {
- this.key = key;
- this.value = value;
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java
deleted file mode 100644
index 406fe45701..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.util;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Set;
-import java.util.HashSet;
-
-/**
- * Allows a method to be invoked on a list of listeners with one call
- *
- */
-public class InvokeMultiple <T> implements InvocationHandler
-{
- private final Set<T> _targets = new HashSet<T>();
- private final T _proxy;
-
- public InvokeMultiple(Class<? extends T> type)
- {
- _proxy = (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, this);
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- {
- Set<T> targets;
- synchronized(this)
- {
- targets = new HashSet<T>(_targets);
- }
-
- for(T target : targets)
- {
- method.invoke(target, args);
- }
- return null;
- }
-
- public synchronized void addListener(T t)
- {
- _targets.add(t);
- }
-
- public synchronized void removeListener(T t)
- {
- _targets.remove(t);
- }
-
- public T getProxy()
- {
- return _proxy;
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java b/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java
deleted file mode 100644
index 9be90298ea..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster.util;
-
-import java.text.MessageFormat;
-
-/**
- * Convenience class to allow log messages to be specified in terms
- * of MessageFormat patterns with a variable set of parameters. The
- * production of the string is only done if toSTring is called so it
- * works well with debug level messages, allowing complex messages
- * to be specified that are only evaluated if actually printed.
- *
- */
-public class LogMessage
-{
- private final String _message;
- private final Object[] _args;
-
- public LogMessage(String message)
- {
- this(message, new Object[0]);
- }
-
- public LogMessage(String message, Object... args)
- {
- _message = message;
- _args = args;
- }
-
- public String toString()
- {
- return MessageFormat.format(_message, _args);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java
deleted file mode 100644
index 9fa96ece1e..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.BasicCancelBody;
-import org.apache.qpid.framing.QueueDeleteBody;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.cluster.*;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Represents a shared queue in a cluster. The key difference is that as well as any
- * local consumers, there may be consumers for this queue on other members of the
- * cluster.
- *
- */
-public class ClusteredQueue extends AMQQueue
-{
- private static final Logger _logger = Logger.getLogger(ClusteredQueue.class);
- private final ConcurrentHashMap<SimpleMemberHandle, RemoteSubscriptionImpl> _peers = new ConcurrentHashMap<SimpleMemberHandle, RemoteSubscriptionImpl>();
- private final GroupManager _groupMgr;
- private final NestedSubscriptionManager _subscriptions;
-
- public ClusteredQueue(GroupManager groupMgr, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
- throws AMQException
- {
- super(name, durable, owner, autoDelete, virtualHost, new ClusteredSubscriptionManager());
- _groupMgr = groupMgr;
- _subscriptions = ((ClusteredSubscriptionManager) getSubscribers()).getAllSubscribers();
- }
-
-
- public void process(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException
- {
- _logger.info(new LogMessage("{0} delivered to clustered queue {1}", msg, this));
- super.process(storeContext, msg, deliverFirst);
- }
-
- protected void autodelete() throws AMQException
- {
- if(!_subscriptions.hasActiveSubscribers())
- {
- //delete locally:
- delete();
-
- //send deletion request to all other members:
- // 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.
- QueueDeleteBody request = new QueueDeleteBody((byte)8,
- (byte)0,
- QueueDeleteBody.getClazz((byte)8,(byte)0),
- QueueDeleteBody.getMethod((byte)8,(byte)0),
- false,
- false,
- false,
- getName(),
- 0);
-
- _groupMgr.broadcast(new SimpleBodySendable(request));
- }
- }
-
- public void unregisterProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag) throws AMQException
- {
- //handle locally:
- super.unregisterProtocolSession(ps, channel, consumerTag);
-
- //signal other members:
- // 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.
- BasicCancelBody request = new BasicCancelBody((byte)8,
- (byte)0,
- BasicCancelBody.getClazz((byte)8, (byte)0),
- BasicCancelBody.getMethod((byte)8, (byte)0),
- getName(),
- false);
-
- _groupMgr.broadcast(new SimpleBodySendable(request));
- }
-
- public void addRemoteSubcriber(MemberHandle peer)
- {
- _logger.info(new LogMessage("Added remote subscriber for {0} to clustered queue {1}", peer, this));
- //find (or create) a matching subscriber for the peer then increment the count
- getSubscriber(key(peer), true).increment();
- }
-
- public void removeRemoteSubscriber(MemberHandle peer)
- {
- //find a matching subscriber for the peer then decrement the count
- //if count is now zero, remove the subscriber
- SimpleMemberHandle key = key(peer);
- RemoteSubscriptionImpl s = getSubscriber(key, true);
- if (s == null)
- {
- throw new RuntimeException("No subscriber for " + peer);
- }
- if (s.decrement())
- {
- _peers.remove(key);
- _subscriptions.removeSubscription(s);
- }
- }
-
- public void removeAllRemoteSubscriber(MemberHandle peer)
- {
- SimpleMemberHandle key = key(peer);
- RemoteSubscriptionImpl s = getSubscriber(key, true);
- _peers.remove(key);
- _subscriptions.removeSubscription(s);
- }
-
- private RemoteSubscriptionImpl getSubscriber(SimpleMemberHandle key, boolean create)
- {
- RemoteSubscriptionImpl s = _peers.get(key);
- if (s == null && create)
- {
- return addSubscriber(key, new RemoteSubscriptionImpl(_groupMgr, key));
- }
- else
- {
- return s;
- }
- }
-
- private RemoteSubscriptionImpl addSubscriber(SimpleMemberHandle key, RemoteSubscriptionImpl s)
- {
- RemoteSubscriptionImpl other = _peers.putIfAbsent(key, s);
- if (other == null)
- {
- _subscriptions.addSubscription(s);
- new SubscriberCleanup(key, this, _groupMgr);
- return s;
- }
- else
- {
- return other;
- }
- }
-
- private SimpleMemberHandle key(MemberHandle peer)
- {
- return peer instanceof SimpleMemberHandle ? (SimpleMemberHandle) peer : (SimpleMemberHandle) SimpleMemberHandle.resolve(peer);
- }
-
- static boolean isFromBroker(AMQMessage msg)
- {
- return ClusteredProtocolSession.isPayloadFromPeer(msg);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java
deleted file mode 100644
index 39ae7e3c3e..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.cluster.util.LogMessage;
-
-import java.util.List;
-
-class ClusteredSubscriptionManager extends SubscriptionSet
-{
- private static final Logger _logger = Logger.getLogger(ClusteredSubscriptionManager.class);
- private final NestedSubscriptionManager _all;
-
- ClusteredSubscriptionManager()
- {
- this(new NestedSubscriptionManager());
- }
-
- private ClusteredSubscriptionManager(NestedSubscriptionManager all)
- {
- _all = all;
- _all.addSubscription(new Parent());
- }
-
- NestedSubscriptionManager getAllSubscribers()
- {
- return _all;
- }
-
- public boolean hasActiveSubscribers()
- {
- return _all.hasActiveSubscribers();
- }
-
- public Subscription nextSubscriber(AMQMessage msg)
- {
- if(ClusteredQueue.isFromBroker(msg))
- {
- //if message is from another broker, it should only be delivered
- //to another client to meet ordering constraints
- Subscription s = super.nextSubscriber(msg);
- _logger.info(new LogMessage("Returning next *client* subscriber {0}", s));
- if(s == null)
- {
- //TODO: deliver to another broker, but set the redelivered flag on the msg
- //(this should be policy based)
-
- //for now just don't deliver it
- return null;
- }
- else
- {
- return s;
- }
- }
- Subscription s = _all.nextSubscriber(msg);
- _logger.info(new LogMessage("Returning next subscriber {0}", s));
- return s;
- }
-
- private class Parent implements WeightedSubscriptionManager
- {
- public int getWeight()
- {
- return ClusteredSubscriptionManager.this.getWeight();
- }
-
- public List<Subscription> getSubscriptions()
- {
- return ClusteredSubscriptionManager.super.getSubscriptions();
- }
-
- public boolean hasActiveSubscribers()
- {
- return ClusteredSubscriptionManager.super.hasActiveSubscribers();
- }
-
- public Subscription nextSubscriber(AMQMessage msg)
- {
- return ClusteredSubscriptionManager.super.nextSubscriber(msg);
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java
deleted file mode 100644
index 0566c5203b..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import java.util.List;
-import java.util.LinkedList;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Distributes messages among a list of subsscription managers, using their
- * weighting.
- */
-class NestedSubscriptionManager implements SubscriptionManager
-{
- private final List<WeightedSubscriptionManager> _subscribers = new CopyOnWriteArrayList<WeightedSubscriptionManager>();
- private int _iterations;
- private int _index;
-
- void addSubscription(WeightedSubscriptionManager s)
- {
- _subscribers.add(s);
- }
-
- void removeSubscription(WeightedSubscriptionManager s)
- {
- _subscribers.remove(s);
- }
-
-
- public List<Subscription> getSubscriptions()
- {
- List<Subscription> allSubs = new LinkedList<Subscription>();
-
- for (WeightedSubscriptionManager subMans : _subscribers)
- {
- allSubs.addAll(subMans.getSubscriptions());
- }
-
- return allSubs;
- }
-
- public boolean hasActiveSubscribers()
- {
- for (WeightedSubscriptionManager s : _subscribers)
- {
- if (s.hasActiveSubscribers())
- {
- return true;
- }
- }
- return false;
- }
-
- public Subscription nextSubscriber(AMQMessage msg)
- {
- WeightedSubscriptionManager start = current();
- for (WeightedSubscriptionManager s = start; s != null; s = next(start))
- {
- if (hasMore(s))
- {
- return nextSubscriber(s);
- }
- }
- return null;
- }
-
- private Subscription nextSubscriber(WeightedSubscriptionManager s)
- {
- _iterations++;
- return s.nextSubscriber(null);
- }
-
- private WeightedSubscriptionManager current()
- {
- return _subscribers.isEmpty() ? null : _subscribers.get(_index);
- }
-
- private boolean hasMore(WeightedSubscriptionManager s)
- {
- return _iterations < s.getWeight();
- }
-
- private WeightedSubscriptionManager next(WeightedSubscriptionManager start)
- {
- WeightedSubscriptionManager s = next();
- return s == start && !hasMore(s) ? null : s;
- }
-
- private WeightedSubscriptionManager next()
- {
- _iterations = 0;
- if (++_index >= _subscribers.size())
- {
- _index = 0;
- }
- return _subscribers.get(_index);
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java
deleted file mode 100644
index f8e4311a77..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.server.cluster.SimpleSendable;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.SimpleBodySendable;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.framing.QueueDeleteBody;
-import org.apache.qpid.framing.AMQShortString;
-
-import java.util.concurrent.Executor;
-
-/**
- * Used to represent a private queue held locally.
- *
- */
-public class PrivateQueue extends AMQQueue
-{
- private final GroupManager _groupMgr;
-
- public PrivateQueue(GroupManager groupMgr, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
- throws AMQException
- {
- super(name, durable, owner, autoDelete, virtualHost);
- _groupMgr = groupMgr;
-
- }
-
- protected void autodelete() throws AMQException
- {
- //delete locally:
- super.autodelete();
-
- //send delete request to peers:
- // 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.
- QueueDeleteBody request = new QueueDeleteBody((byte)8, (byte)0,
- QueueDeleteBody.getClazz((byte)8, (byte)0),
- QueueDeleteBody.getMethod((byte)8, (byte)0),
- false,false,false,null,0);
- request.queue = getName();
- _groupMgr.broadcast(new SimpleBodySendable(request));
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java
deleted file mode 100644
index efc0540c18..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.server.cluster.MembershipChangeListener;
-import org.apache.qpid.server.cluster.MemberHandle;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.AMQException;
-import org.apache.log4j.Logger;
-
-import java.util.List;
-
-class ProxiedQueueCleanup implements MembershipChangeListener
-{
- private static final Logger _logger = Logger.getLogger(ProxiedQueueCleanup.class);
-
- private final MemberHandle _subject;
- private final RemoteQueueProxy _queue;
-
- ProxiedQueueCleanup(MemberHandle subject, RemoteQueueProxy queue)
- {
- _subject = subject;
- _queue = queue;
- }
-
- public void changed(List<MemberHandle> members)
- {
- if(!members.contains(_subject))
- {
- try
- {
- _queue.delete();
- _logger.info(new LogMessage("Deleted {0} in response to exclusion of {1}", _queue, _subject));
- }
- catch (AMQException e)
- {
- _logger.info(new LogMessage("Failed to delete {0} in response to exclusion of {1}: {2}", _queue, _subject, e), e);
- }
- }
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java
deleted file mode 100644
index 2a83d65ae5..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.BasicPublishBody;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.cluster.ClusteredProtocolSession;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.MemberHandle;
-import org.apache.qpid.server.cluster.SimpleSendable;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-/**
- * TODO: separate out an abstract base class from AMQQueue from which this inherits. It does
- * not require all the functionality currently in AMQQueue.
- *
- */
-public class RemoteQueueProxy extends AMQQueue
-{
- private static final Logger _logger = Logger.getLogger(RemoteQueueProxy.class);
- private final MemberHandle _target;
- private final GroupManager _groupMgr;
-
- public RemoteQueueProxy(MemberHandle target, GroupManager groupMgr, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
- throws AMQException
- {
- super(name, durable, owner, autoDelete, virtualHost);
- _target = target;
- _groupMgr = groupMgr;
- _groupMgr.addMemberhipChangeListener(new ProxiedQueueCleanup(target, this));
- }
-
-
- public void deliver(AMQMessage msg) throws NoConsumersException
- {
- if (ClusteredProtocolSession.canRelay(msg, _target))
- {
- try
- {
- _logger.debug(new LogMessage("Relaying {0} to {1}", msg, _target));
- relay(msg);
- }
- catch (NoConsumersException e)
- {
- throw e;
- }
- catch (AMQException e)
- {
- //TODO: sort out exception handling...
- e.printStackTrace();
- }
- }
- else
- {
- _logger.debug(new LogMessage("Cannot relay {0} to {1}", msg, _target));
- }
- }
-
- void relay(AMQMessage msg) throws AMQException
- {
- // TODO FIXME - can no longer update the publish body as it is an opaque wrapper object
- // if cluster can handle immediate then it should wrap the wrapper...
-
-// BasicPublishBody publish = msg.getMessagePublishInfo();
-// publish.immediate = false; //can't as yet handle the immediate flag in a cluster
-
- // send this on to the broker for which it is acting as proxy:
- _groupMgr.send(_target, new SimpleSendable(msg));
- }
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java
deleted file mode 100644
index e396432cea..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.server.cluster.MemberHandle;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.SimpleSendable;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.AMQException;
-
-import java.util.Queue;
-import java.util.List;
-
-class RemoteSubscriptionImpl implements Subscription, WeightedSubscriptionManager
-{
- private final GroupManager _groupMgr;
- private final MemberHandle _peer;
- private boolean _suspended;
- private int _count;
-
- RemoteSubscriptionImpl(GroupManager groupMgr, MemberHandle peer)
- {
- _groupMgr = groupMgr;
- _peer = peer;
- }
-
- synchronized void increment()
- {
- _count++;
- }
-
- synchronized boolean decrement()
- {
- return --_count <= 0;
- }
-
- public void send(AMQMessage msg, AMQQueue queue)
- {
- try
- {
- _groupMgr.send(_peer, new SimpleSendable(msg));
- }
- catch (AMQException e)
- {
- //TODO: handle exceptions properly...
- e.printStackTrace();
- }
- }
-
- public synchronized void setSuspended(boolean suspended)
- {
- _suspended = suspended;
- }
-
- public synchronized boolean isSuspended()
- {
- return _suspended;
- }
-
- public synchronized int getWeight()
- {
- return _count;
- }
-
- public List<Subscription> getSubscriptions()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public boolean hasActiveSubscribers()
- {
- return getWeight() == 0;
- }
-
- public Subscription nextSubscriber(AMQMessage msg)
- {
- return this;
- }
-
- public void queueDeleted(AMQQueue queue)
- {
- if (queue instanceof ClusteredQueue)
- {
- ((ClusteredQueue) queue).removeAllRemoteSubscriber(_peer);
- }
- }
-
- public boolean filtersMessages()
- {
- return false;
- }
-
- public boolean hasInterest(AMQMessage msg)
- {
- return true;
- }
-
- public Queue<AMQMessage> getPreDeliveryQueue()
- {
- return null;
- }
-
- public Queue<AMQMessage> getResendQueue()
- {
- return null;
- }
-
- public Queue<AMQMessage> getNextQueue(Queue<AMQMessage> messages)
- {
- return messages;
- }
-
- public void enqueueForPreDelivery(AMQMessage msg, boolean deliverFirst)
- {
- //no-op -- if selectors are implemented on RemoteSubscriptions then look at SubscriptionImpl
- }
-
- public boolean isAutoClose()
- {
- return false;
- }
-
- public void close()
- {
- //no-op
- }
-
- public boolean isClosed()
- {
- return false;
- }
-
- public boolean isBrowser()
- {
- return false;
- }
-
- public boolean wouldSuspend(AMQMessage msg)
- {
- return _suspended;
- }
-
- public void addToResendQueue(AMQMessage msg)
- {
- //no-op
- }
-
- public Object getSendLock()
- {
- return new Object();
- }
-
- public AMQChannel getChannel()
- {
- return null;
- }
-
-}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java b/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java
deleted file mode 100644
index cc951a4709..0000000000
--- a/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.server.cluster.MembershipChangeListener;
-import org.apache.qpid.server.cluster.MemberHandle;
-import org.apache.qpid.server.cluster.GroupManager;
-import org.apache.qpid.server.cluster.util.LogMessage;
-import org.apache.log4j.Logger;
-
-import java.util.List;
-
-class SubscriberCleanup implements MembershipChangeListener
-{
- private static final Logger _logger = Logger.getLogger(SubscriberCleanup.class);
-
- private final MemberHandle _subject;
- private final ClusteredQueue _queue;
- private final GroupManager _manager;
-
- SubscriberCleanup(MemberHandle subject, ClusteredQueue queue, GroupManager manager)
- {
- _subject = subject;
- _queue = queue;
- _manager = manager;
- _manager.addMemberhipChangeListener(this);
- }
-
- public void changed(List<MemberHandle> members)
- {
- if(!members.contains(_subject))
- {
- _queue.removeAllRemoteSubscriber(_subject);
- _manager.removeMemberhipChangeListener(this);
- _logger.info(new LogMessage("Removed {0} from {1}", _subject, _queue));
- }
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java
deleted file mode 100644
index b91d7140e0..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import junit.framework.TestCase;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-public class BrokerGroupTest extends TestCase
-{
- private final MemberHandle a = new SimpleMemberHandle("A", 1);
- private final MemberHandle b = new SimpleMemberHandle("B", 1);
- private final MemberHandle c = new SimpleMemberHandle("C", 1);
- private final MemberHandle d = new SimpleMemberHandle("D", 1);
-
- //join (new members perspective)
- // (i) connectToLeader()
- // ==> check state
- // (ii) setMembers()
- // ==> check state
- // ==> check members
- // (iii) synched(leader)
- // ==> check state
- // ==> check peers
- // (iv) synched(other)
- // ==> check state
- // ==> check peers
- // repeat for all others
- public void testJoin_newMember() throws Exception
- {
- MemberHandle[] pre = new MemberHandle[]{a, b, c};
- MemberHandle[] post = new MemberHandle[]{a, b, c};
-
- BrokerGroup group = new BrokerGroup(d, new TestReplayManager(), new TestBrokerFactory());
- assertEquals(JoinState.UNINITIALISED, group.getState());
- //(i)
- group.connectToLeader(a);
- assertEquals(JoinState.JOINING, group.getState());
- assertEquals("Wrong number of peers", 1, group.getPeers().size());
- //(ii)
- group.setMembers(Arrays.asList(post));
- assertEquals(JoinState.INITIATION, group.getState());
- assertEquals(Arrays.asList(post), group.getMembers());
- //(iii) & (iv)
- for (MemberHandle member : pre)
- {
- group.synched(member);
- if (member == c)
- {
- assertEquals(JoinState.JOINED, group.getState());
- assertEquals("Wrong number of peers", pre.length, group.getPeers().size());
- }
- else
- {
- assertEquals(JoinState.INDUCTION, group.getState());
- assertEquals("Wrong number of peers", 1, group.getPeers().size());
- }
- }
- }
-
- //join (leaders perspective)
- // (i) extablish()
- // ==> check state
- // ==> check members
- // ==> check peers
- // (ii) connectToProspect()
- // ==> check members
- // ==> check peers
- // repeat (ii)
- public void testJoin_Leader() throws IOException, InterruptedException
- {
- MemberHandle[] prospects = new MemberHandle[]{b, c, d};
-
- BrokerGroup group = new BrokerGroup(a, new TestReplayManager(), new TestBrokerFactory());
- assertEquals(JoinState.UNINITIALISED, group.getState());
- //(i)
- group.establish();
- assertEquals(JoinState.JOINED, group.getState());
- assertEquals("Wrong number of peers", 0, group.getPeers().size());
- assertEquals("Wrong number of members", 1, group.getMembers().size());
- assertEquals(a, group.getMembers().get(0));
- //(ii)
- for (int i = 0; i < prospects.length; i++)
- {
- group.connectToProspect(prospects[i]);
- assertEquals("Wrong number of peers", i + 1, group.getPeers().size());
- for (int j = 0; j <= i; j++)
- {
- assertTrue(prospects[i].matches(group.getPeers().get(i)));
- }
- assertEquals("Wrong number of members", i + 2, group.getMembers().size());
- assertEquals(a, group.getMembers().get(0));
- for (int j = 0; j <= i; j++)
- {
- assertEquals(prospects[i], group.getMembers().get(i + 1));
- }
- }
- }
-
- //join (general perspective)
- // (i) set up group
- // (ii) setMembers()
- // ==> check members
- // ==> check peers
- public void testJoin_general() throws Exception
- {
- MemberHandle[] view1 = new MemberHandle[]{a, b, c};
- MemberHandle[] view2 = new MemberHandle[]{a, b, c, d};
- MemberHandle[] peers = new MemberHandle[]{a, b, d};
-
- BrokerGroup group = new BrokerGroup(c, new TestReplayManager(), new TestBrokerFactory());
- //(i)
- group.connectToLeader(a);
- group.setMembers(Arrays.asList(view1));
- for (MemberHandle h : view1)
- {
- group.synched(h);
- }
- //(ii)
- group.setMembers(Arrays.asList(view2));
- assertEquals(Arrays.asList(view2), group.getMembers());
- assertEquals(peers.length, group.getPeers().size());
- for (int i = 0; i < peers.length; i++)
- {
- assertTrue(peers[i].matches(group.getPeers().get(i)));
- }
- }
-
- //leadership transfer (valid)
- // (i) set up group
- // (ii) assumeLeadership()
- // ==> check return value
- // ==> check members
- // ==> check peers
- // ==> check isLeader()
- // ==> check isLeader(old_leader)
- // ==> check isMember(old_leader)
- public void testTransferLeadership_valid() throws Exception
- {
- MemberHandle[] view1 = new MemberHandle[]{a, b};
- MemberHandle[] view2 = new MemberHandle[]{a, b, c, d};
- MemberHandle[] view3 = new MemberHandle[]{b, c, d};
-
- BrokerGroup group = new BrokerGroup(b, new TestReplayManager(), new TestBrokerFactory());
- //(i)
- group.connectToLeader(a);
- group.setMembers(Arrays.asList(view1));
- for (MemberHandle h : view1)
- {
- group.synched(h);
- }
- group.setMembers(Arrays.asList(view2));
- //(ii)
- boolean result = group.assumeLeadership();
- assertTrue(result);
- assertTrue(group.isLeader());
- assertFalse(group.isLeader(a));
- assertEquals(Arrays.asList(view3), group.getMembers());
- assertEquals(2, group.getPeers().size());
- assertTrue(c.matches(group.getPeers().get(0)));
- assertTrue(d.matches(group.getPeers().get(1)));
- }
-
- //leadership transfer (invalid)
- // (i) set up group
- // (ii) assumeLeadership()
- // ==> check return value
- // ==> check members
- // ==> check peers
- // ==> check isLeader()
- // ==> check isLeader(old_leader)
- // ==> check isMember(old_leader)
- public void testTransferLeadership_invalid() throws Exception
- {
- MemberHandle[] view1 = new MemberHandle[]{a, b, c};
- MemberHandle[] view2 = new MemberHandle[]{a, b, c, d};
-
- BrokerGroup group = new BrokerGroup(c, new TestReplayManager(), new TestBrokerFactory());
- //(i)
- group.connectToLeader(a);
- group.setMembers(Arrays.asList(view1));
- for (MemberHandle h : view1)
- {
- group.synched(h);
- }
- group.setMembers(Arrays.asList(view2));
- //(ii)
- boolean result = group.assumeLeadership();
- assertFalse(result);
- assertFalse(group.isLeader());
- assertTrue(group.isLeader(a));
- assertEquals(Arrays.asList(view2), group.getMembers());
- assertEquals(3, group.getPeers().size());
- assertTrue(a.matches(group.getPeers().get(0)));
- assertTrue(b.matches(group.getPeers().get(1)));
- assertTrue(d.matches(group.getPeers().get(2)));
-
- }
-
- //leave (leaders perspective)
- // (i) set up group
- // (ii) remove a member
- // ==> check members
- // ==> check peers
- // ==> check isMember(removed_member)
- // repeat (ii)
- public void testLeave_leader()
- {
- MemberHandle[] view1 = new MemberHandle[]{a, b, c, d};
- MemberHandle[] view2 = new MemberHandle[]{a, b, d};
- MemberHandle[] view3 = new MemberHandle[]{a, d};
- MemberHandle[] view4 = new MemberHandle[]{a};
- //(i)
- BrokerGroup group = new BrokerGroup(a, new TestReplayManager(), new TestBrokerFactory());
- group.establish();
- group.setMembers(Arrays.asList(view1));
- //(ii)
- group.remove(group.findBroker(c, false));
- assertEquals(Arrays.asList(view2), group.getMembers());
-
- group.remove(group.findBroker(b, false));
- assertEquals(Arrays.asList(view3), group.getMembers());
-
- group.remove(group.findBroker(d, false));
- assertEquals(Arrays.asList(view4), group.getMembers());
- }
-
-
- //leave (general perspective)
- // (i) set up group
- // (ii) setMember
- // ==> check members
- // ==> check peers
- // ==> check isMember(removed_member)
- // repeat (ii)
- public void testLeave_general()
- {
- MemberHandle[] view1 = new MemberHandle[]{a, b, c, d};
- MemberHandle[] view2 = new MemberHandle[]{a, c, d};
- //(i)
- BrokerGroup group = new BrokerGroup(c, new TestReplayManager(), new TestBrokerFactory());
- group.establish(); //not strictly the correct way to build up the group, but ok for here
- group.setMembers(Arrays.asList(view1));
- //(ii)
- group.setMembers(Arrays.asList(view2));
- assertEquals(Arrays.asList(view2), group.getMembers());
- assertEquals(2, group.getPeers().size());
- assertTrue(a.matches(group.getPeers().get(0)));
- assertTrue(d.matches(group.getPeers().get(1)));
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java
deleted file mode 100644
index f1da312eea..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import junit.framework.TestCase;
-import org.apache.mina.common.ByteBuffer;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQBody;
-import org.apache.qpid.framing.AMQDataBlock;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.AMQFrameDecodingException;
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.cluster.policy.StandardPolicies;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class BrokerTest extends TestCase
-{
- //group request (no failure)
- public void testGroupRequest_noFailure() throws AMQException
- {
- RecordingBroker[] brokers = new RecordingBroker[]{
- new RecordingBroker("A", 1),
- new RecordingBroker("B", 2),
- new RecordingBroker("C", 3)
- };
- GroupResponseValidator handler = new GroupResponseValidator(new TestMethod("response"), new ArrayList<Member>(Arrays.asList(brokers)));
- GroupRequest grpRequest = new GroupRequest(new SimpleBodySendable(new TestMethod("request")), StandardPolicies.SYNCH_POLICY, handler);
- for (Broker b : brokers)
- {
- b.invoke(grpRequest);
- }
- grpRequest.finishedSend();
-
- for (RecordingBroker b : brokers)
- {
- b.handleResponse(((AMQFrame) b.getMessages().get(0)).getChannel(), new TestMethod("response"));
- }
-
- assertTrue("Handler did not receive response", handler.isCompleted());
- }
-
- //group request (failure)
- public void testGroupRequest_failure() throws AMQException
- {
- RecordingBroker a = new RecordingBroker("A", 1);
- RecordingBroker b = new RecordingBroker("B", 2);
- RecordingBroker c = new RecordingBroker("C", 3);
- RecordingBroker[] all = new RecordingBroker[]{a, b, c};
- RecordingBroker[] succeeded = new RecordingBroker[]{a, c};
-
- GroupResponseValidator handler = new GroupResponseValidator(new TestMethod("response"), new ArrayList<Member>(Arrays.asList(succeeded)));
- GroupRequest grpRequest = new GroupRequest(new SimpleBodySendable(new TestMethod("request")), StandardPolicies.SYNCH_POLICY, handler);
-
- for (Broker broker : all)
- {
- broker.invoke(grpRequest);
- }
- grpRequest.finishedSend();
-
- for (RecordingBroker broker : succeeded)
- {
- broker.handleResponse(((AMQFrame) broker.getMessages().get(0)).getChannel(), new TestMethod("response"));
- }
- b.remove();
-
- assertTrue("Handler did not receive response", handler.isCompleted());
- }
-
-
- //simple send (no response)
- public void testSend_noResponse() throws AMQException
- {
- AMQBody[] msgs = new AMQBody[]{
- new TestMethod("A"),
- new TestMethod("B"),
- new TestMethod("C")
- };
- RecordingBroker broker = new RecordingBroker("myhost", 1);
- for (AMQBody msg : msgs)
- {
- broker.send(new SimpleBodySendable(msg), null);
- }
- List<AMQDataBlock> sent = broker.getMessages();
- assertEquals(msgs.length, sent.size());
- for (int i = 0; i < msgs.length; i++)
- {
- assertTrue(sent.get(i) instanceof AMQFrame);
- assertEquals(msgs[i], ((AMQFrame) sent.get(i)).getBodyFrame());
- }
- }
-
- //simple send (no failure)
- public void testSend_noFailure() throws AMQException
- {
- RecordingBroker broker = new RecordingBroker("myhost", 1);
- BlockingHandler handler = new BlockingHandler();
- broker.send(new SimpleBodySendable(new TestMethod("A")), handler);
- List<AMQDataBlock> sent = broker.getMessages();
- assertEquals(1, sent.size());
- assertTrue(sent.get(0) instanceof AMQFrame);
- assertEquals(new TestMethod("A"), ((AMQFrame) sent.get(0)).getBodyFrame());
-
- broker.handleResponse(((AMQFrame) sent.get(0)).getChannel(), new TestMethod("B"));
-
- assertEquals(new TestMethod("B"), handler.getResponse());
- }
-
- //simple send (failure)
- public void testSend_failure() throws AMQException
- {
- RecordingBroker broker = new RecordingBroker("myhost", 1);
- BlockingHandler handler = new BlockingHandler();
- broker.send(new SimpleBodySendable(new TestMethod("A")), handler);
- List<AMQDataBlock> sent = broker.getMessages();
- assertEquals(1, sent.size());
- assertTrue(sent.get(0) instanceof AMQFrame);
- assertEquals(new TestMethod("A"), ((AMQFrame) sent.get(0)).getBodyFrame());
- broker.remove();
- assertEquals(null, handler.getResponse());
- assertTrue(handler.isCompleted());
- assertTrue(handler.failed());
- }
-
- private static class TestMethod extends AMQMethodBody
- {
- private final Object id;
-
- TestMethod(Object id)
- {
- // 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.
- super((byte)8, (byte)0);
- this.id = id;
- }
-
- protected int getBodySize()
- {
- return 0;
- }
-
- protected int getClazz()
- {
- return 1002;
- }
-
- protected int getMethod()
- {
- return 1003;
- }
-
- protected void writeMethodPayload(ByteBuffer buffer)
- {
- }
-
- protected byte getType()
- {
- return 0;
- }
-
- protected int getSize()
- {
- return 0;
- }
-
- protected void writePayload(ByteBuffer buffer)
- {
- }
-
- protected void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException
- {
- }
-
- protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException
- {
- }
-
- public boolean equals(Object o)
- {
- return o instanceof TestMethod && id.equals(((TestMethod) o).id);
- }
-
- public int hashCode()
- {
- return id.hashCode();
- }
-
- }
-
- private static class GroupResponseValidator implements GroupResponseHandler
- {
- private final AMQMethodBody _response;
- private final List<Member> _members;
- private boolean _completed = false;
-
- GroupResponseValidator(AMQMethodBody response, List<Member> members)
- {
- _response = response;
- _members = members;
- }
-
- public void response(List<AMQMethodBody> responses, List<Member> members)
- {
- for (AMQMethodBody r : responses)
- {
- assertEquals(_response, r);
- }
- assertEquals(_members, members);
- _completed = true;
- }
-
- boolean isCompleted()
- {
- return _completed;
- }
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java
deleted file mode 100644
index 830a00f4c2..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import junit.framework.TestCase;
-import org.apache.qpid.framing.AMQShortString;
-
-public class ClusterCapabilityTest extends TestCase
-{
- public void testStartWithNull()
- {
- MemberHandle peer = new SimpleMemberHandle("myhost:9999");
- AMQShortString c = ClusterCapability.add(null, peer);
- assertTrue(ClusterCapability.contains(c));
- assertTrue(peer.matches(ClusterCapability.getPeer(c)));
- }
-
- public void testStartWithText()
- {
- MemberHandle peer = new SimpleMemberHandle("myhost:9999");
- AMQShortString c = ClusterCapability.add(new AMQShortString("existing text"), peer);
- assertTrue(ClusterCapability.contains(c));
- assertTrue(peer.matches(ClusterCapability.getPeer(c)));
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java
deleted file mode 100644
index 7e58add91e..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.mina.common.IoSession;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import junit.framework.TestCase;
-
-public class InductionBufferTest extends TestCase
-{
- public void test() throws Exception
- {
- IoSession session1 = new TestSession();
- IoSession session2 = new TestSession();
- IoSession session3 = new TestSession();
-
- TestMessageHandler handler = new TestMessageHandler();
- InductionBuffer buffer = new InductionBuffer(handler);
-
- buffer.receive(session1, "one");
- buffer.receive(session2, "two");
- buffer.receive(session3, "three");
-
- buffer.receive(session1, "four");
- buffer.receive(session1, "five");
- buffer.receive(session1, "six");
-
- buffer.receive(session3, "seven");
- buffer.receive(session3, "eight");
-
- handler.checkEmpty();
- buffer.deliver();
-
- handler.check(session1, "one");
- handler.check(session2, "two");
- handler.check(session3, "three");
-
- handler.check(session1, "four");
- handler.check(session1, "five");
- handler.check(session1, "six");
-
- handler.check(session3, "seven");
- handler.check(session3, "eight");
- handler.checkEmpty();
-
- buffer.receive(session1, "nine");
- buffer.receive(session2, "ten");
- buffer.receive(session3, "eleven");
-
- handler.check(session1, "nine");
- handler.check(session2, "ten");
- handler.check(session3, "eleven");
-
- handler.checkEmpty();
- }
-
- private static class TestMessageHandler implements InductionBuffer.MessageHandler
- {
- private final List<IoSession> _sessions = new ArrayList<IoSession>();
- private final List<Object> _msgs = new ArrayList<Object>();
-
- public synchronized void deliver(IoSession session, Object msg) throws Exception
- {
- _sessions.add(session);
- _msgs.add(msg);
- }
-
- void check(IoSession actualSession, Object actualMsg)
- {
- assertFalse(_sessions.isEmpty());
- assertFalse(_msgs.isEmpty());
- IoSession expectedSession = _sessions.remove(0);
- Object expectedMsg = _msgs.remove(0);
- assertEquals(expectedSession, actualSession);
- assertEquals(expectedMsg, actualMsg);
- }
-
- void checkEmpty()
- {
- assertTrue(_sessions.isEmpty());
- assertTrue(_msgs.isEmpty());
- }
- }
-}
-
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java
deleted file mode 100644
index d3e972e273..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-class RecordingBrokerFactory implements BrokerFactory
-{
- public Broker create(MemberHandle handle)
- {
- return new RecordingBroker(handle.getHost(), handle.getPort());
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java
deleted file mode 100644
index 86cde3cee7..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.url.URLSyntaxException;
-import org.apache.qpid.client.AMQConnection;
-import org.apache.qpid.client.AMQSession;
-
-import javax.jms.JMSException;
-
-import junit.framework.TestCase;
-
-public class SimpleClusterTest extends TestCase
-{
- public void testDeclareExchange() throws AMQException, JMSException, URLSyntaxException
- {
- AMQConnection con = new AMQConnection("localhost:9000", "guest", "guest", "test", "/test");
- AMQSession session = (AMQSession) con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
- System.out.println("Session created");
- session.declareExchange(new AMQShortString("my_exchange"), new AMQShortString("direct"), true);
- System.out.println("Exchange declared");
- con.close();
- System.out.println("Connection closed");
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java
deleted file mode 100644
index 8ff8357377..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import junit.framework.TestCase;
-
-public class SimpleMemberHandleTest extends TestCase
-{
- public void testMatches()
- {
- assertMatch(new SimpleMemberHandle("localhost", 8888), new SimpleMemberHandle("localhost", 8888));
- assertNoMatch(new SimpleMemberHandle("localhost", 8889), new SimpleMemberHandle("localhost", 8888));
- assertNoMatch(new SimpleMemberHandle("localhost", 8888), new SimpleMemberHandle("localhost2", 8888));
- }
-
- public void testResolve()
- {
- assertEquivalent(new SimpleMemberHandle("WGLAIBD8XGR0J:9000"), new SimpleMemberHandle("localhost:9000"));
- }
-
- private void assertEquivalent(MemberHandle a, MemberHandle b)
- {
- String msg = a + " is not equivalent to " + b;
- a = SimpleMemberHandle.resolve(a);
- b = SimpleMemberHandle.resolve(b);
- msg += "(" + a + " does not match " + b + ")";
- assertTrue(msg, a.matches(b));
- }
-
- private void assertMatch(MemberHandle a, MemberHandle b)
- {
- assertTrue(a + " does not match " + b, a.matches(b));
- }
-
- private void assertNoMatch(MemberHandle a, MemberHandle b)
- {
- assertFalse(a + " matches " + b, a.matches(b));
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java
deleted file mode 100644
index d3ccbf0ac6..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQDataBlock;
-import org.apache.qpid.framing.AMQFrame;
-import org.apache.qpid.framing.AMQMethodBody;
-
-import java.io.IOException;
-
-class TestBroker extends Broker
-{
- TestBroker(String host, int port)
- {
- super(host, port);
- }
-
- boolean connect() throws IOException, InterruptedException
- {
- return true;
- }
-
- void connectAsynch(Iterable<AMQMethodBody> msgs)
- {
- replay(msgs);
- }
-
- void replay(Iterable<AMQMethodBody> msgs)
- {
- try
- {
- for (AMQMethodBody b : msgs)
- {
- send(new AMQFrame(0, b));
- }
- }
- catch (AMQException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- Broker connectToCluster() throws IOException, InterruptedException
- {
- return this;
- }
-
- public void send(AMQDataBlock data) throws AMQException
- {
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java
deleted file mode 100644
index 92eaec876a..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-class TestBrokerFactory implements BrokerFactory
-{
- public Broker create(MemberHandle handle)
- {
- return new TestBroker(handle.getHost(), handle.getPort());
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java
deleted file mode 100644
index c529c83cc0..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.server.cluster.replay.ReplayManager;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class TestReplayManager implements ReplayManager
-{
- private final List<AMQMethodBody> _msgs;
-
- TestReplayManager()
- {
- this(new ArrayList<AMQMethodBody>());
- }
-
- TestReplayManager(List<AMQMethodBody> msgs)
- {
- _msgs = msgs;
- }
-
- public List<AMQMethodBody> replay(boolean isLeader)
- {
- return _msgs;
- }
-}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java b/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java
deleted file mode 100644
index 86ec808924..0000000000
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.cluster;
-
-import org.apache.mina.common.*;
-
-import java.net.SocketAddress;
-import java.util.Set;
-
-class TestSession implements IoSession
-{
- public IoService getService()
- {
- return null; //TODO
- }
-
- public IoServiceConfig getServiceConfig()
- {
- return null; //TODO
- }
-
- public IoHandler getHandler()
- {
- return null; //TODO
- }
-
- public IoSessionConfig getConfig()
- {
- return null; //TODO
- }
-
- public IoFilterChain getFilterChain()
- {
- return null; //TODO
- }
-
- public WriteFuture write(Object message)
- {
- return null; //TODO
- }
-
- public CloseFuture close()
- {
- return null; //TODO
- }
-
- public Object getAttachment()
- {
- return null; //TODO
- }
-
- public Object setAttachment(Object attachment)
- {
- return null; //TODO
- }
-
- public Object getAttribute(String key)
- {
- return null; //TODO
- }
-
- public Object setAttribute(String key, Object value)
- {
- return null; //TODO
- }
-
- public Object setAttribute(String key)
- {
- return null; //TODO
- }
-
- public Object removeAttribute(String key)
- {
- return null; //TODO
- }
-
- public boolean containsAttribute(String key)
- {
- return false; //TODO
- }
-
- public Set getAttributeKeys()
- {
- return null; //TODO
- }
-
- public TransportType getTransportType()
- {
- return null; //TODO
- }
-
- public boolean isConnected()
- {
- return false; //TODO
- }
-
- public boolean isClosing()
- {
- return false; //TODO
- }
-
- public CloseFuture getCloseFuture()
- {
- return null; //TODO
- }
-
- public SocketAddress getRemoteAddress()
- {
- return null; //TODO
- }
-
- public SocketAddress getLocalAddress()
- {
- return null; //TODO
- }
-
- public SocketAddress getServiceAddress()
- {
- return null; //TODO
- }
-
- public int getIdleTime(IdleStatus status)
- {
- return 0; //TODO
- }
-
- public long getIdleTimeInMillis(IdleStatus status)
- {
- return 0; //TODO
- }
-
- public void setIdleTime(IdleStatus status, int idleTime)
- {
- //TODO
- }
-
- public int getWriteTimeout()
- {
- return 0; //TODO
- }
-
- public long getWriteTimeoutInMillis()
- {
- return 0; //TODO
- }
-
- public void setWriteTimeout(int writeTimeout)
- {
- //TODO
- }
-
- public TrafficMask getTrafficMask()
- {
- return null; //TODO
- }
-
- public void setTrafficMask(TrafficMask trafficMask)
- {
- //TODO
- }
-
- public void suspendRead()
- {
- //TODO
- }
-
- public void suspendWrite()
- {
- //TODO
- }
-
- public void resumeRead()
- {
- //TODO
- }
-
- public void resumeWrite()
- {
- //TODO
- }
-
- public long getReadBytes()
- {
- return 0; //TODO
- }
-
- public long getWrittenBytes()
- {
- return 0; //TODO
- }
-
- public long getReadMessages()
- {
- return 0;
- }
-
- public long getWrittenMessages()
- {
- return 0;
- }
-
- public long getWrittenWriteRequests()
- {
- return 0; //TODO
- }
-
- public int getScheduledWriteRequests()
- {
- return 0; //TODO
- }
-
- public int getScheduledWriteBytes()
- {
- return 0; //TODO
- }
-
- public long getCreationTime()
- {
- return 0; //TODO
- }
-
- public long getLastIoTime()
- {
- return 0; //TODO
- }
-
- public long getLastReadTime()
- {
- return 0; //TODO
- }
-
- public long getLastWriteTime()
- {
- return 0; //TODO
- }
-
- public boolean isIdle(IdleStatus status)
- {
- return false; //TODO
- }
-
- public int getIdleCount(IdleStatus status)
- {
- return 0; //TODO
- }
-
- public long getLastIdleTime(IdleStatus status)
- {
- return 0; //TODO
- }
-}
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/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/qpid/AMQConnectionFailureException.java b/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java
index f78307d16f..eddd225d28 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)
- {
- super(message);
- }
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid;
+
+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)
+ {
+ super(message);
+ }
+
+ 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 41599ed880..00396f6583 100644
--- a/java/common/src/main/java/org/apache/qpid/AMQException.java
+++ b/java/common/src/main/java/org/apache/qpid/AMQException.java
@@ -98,4 +98,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 278128f924..6725c1cfe8 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);
}
+
+ 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 03220cc95e..1e2788f9f5 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 ff0bc798da..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();
@@ -171,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 3abd97ddb7..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
@@ -21,6 +21,8 @@
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
{
@@ -34,6 +36,5 @@ public interface AMQBody
public void writePayload(ByteBuffer buffer);
- //public void populateFromBuffer(ByteBuffer buffer, long size)
- // throws AMQFrameDecodingException, AMQProtocolVersionException;
+ 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/AMQMethodBodyImpl.java b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java
index 5215bcbd66..64af717342 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java
@@ -24,7 +24,9 @@ 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;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
public abstract class AMQMethodBodyImpl implements AMQMethodBody
{
@@ -86,4 +88,9 @@ public abstract class AMQMethodBodyImpl implements AMQMethodBody
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/AMQShortString.java b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
index cf64b0475a..665cbf7a84 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,438 +1,692 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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];
-
- public static final AMQShortString EMPTY_STRING = new AMQShortString((String)null);
-
- 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.setAutoExpand(true);
- 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()
- {
- final int size = length();
- final char[] chars = new char[size];
-
- for (int i = 0; i < size; i++)
- {
- chars[i] = (char) _data.get(i);
- }
-
- return chars;
- }
-
- public String asString()
- {
- return new String(asChars());
- }
-
- 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, to);
+ }
+
+
+ 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)
+ {
+ byte[] dataBytes = new byte[length];
+ data.get(dataBytes);
+ _data = dataBytes;
+ _length = length;
+ _offset = 0;
+
+ }
+
+ private AMQShortString(final byte[] data, final int from, final int to)
+ {
+ _offset = from;
+ _length = to - from;
+ _data = data;
+ }
+
+
+ /**
+ * 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 = new AMQShortString(getBytes());
+ 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 = 0;
+ int val = 0;
+
+
+ boolean isNegative = (_data[pos] == MINUS);
+ if(isNegative)
+ {
+ pos++;
+ }
+ while(pos < _length)
+ {
+ 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)
+ {
+ for(int i = 0; i < _length; 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 cbee1680f7..969df954ce 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,6 +21,8 @@
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 implements AMQBody
{
@@ -68,6 +70,12 @@ public class ContentBody implements AMQBody
}
}
+ 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 80a61544b3..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,6 +21,8 @@
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 implements AMQBody
{
@@ -110,6 +112,12 @@ public class ContentHeaderBody implements 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/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/FieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
index 46b10b5963..ee6762181d 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,14 +766,14 @@ 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.debug("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() + "...");
if (_properties != null)
{
- _logger.trace(_properties.toString());
+ _logger.debug(_properties.toString());
}
}
@@ -898,13 +958,15 @@ public class FieldTable
if (_encodedForm != null)
{
- if (_encodedForm.position() != 0)
+ ByteBuffer encodedForm = _encodedForm.duplicate();
+
+ if (encodedForm.position() != 0)
{
- _encodedForm.flip();
+ encodedForm.flip();
}
// _encodedForm.limit((int)getEncodedSize());
- buffer.put(_encodedForm);
+ buffer.put(encodedForm);
}
else if (_properties != null)
{
@@ -918,11 +980,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
@@ -931,12 +993,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);
@@ -948,7 +1010,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)
{
@@ -964,7 +1026,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() + "'");
}
@@ -979,7 +1041,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 ef7163bd40..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,6 +21,8 @@
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 implements AMQBody
{
@@ -55,6 +57,12 @@ public class HeartbeatBody implements AMQBody
{
}
+ 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/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/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/MethodConverter_0_9.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java
index de0007c132..d7194640d4 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java
@@ -67,10 +67,10 @@ public class MethodConverter_0_9 extends AbstractMethodConverter implements Prot
final AMQShortString exchange = publishBody.getExchange();
final AMQShortString routingKey = publishBody.getRoutingKey();
- return new MethodConverter_0_9.MessagePublishInfoImpl(exchange == null ? null : exchange.intern(),
+ return new MethodConverter_0_9.MessagePublishInfoImpl(exchange,
publishBody.getImmediate(),
publishBody.getMandatory(),
- routingKey == null ? null : routingKey.intern());
+ routingKey);
}
@@ -87,7 +87,7 @@ public class MethodConverter_0_9 extends AbstractMethodConverter implements Prot
private static class MessagePublishInfoImpl implements MessagePublishInfo
{
- private final AMQShortString _exchange;
+ private AMQShortString _exchange;
private final boolean _immediate;
private final boolean _mandatory;
private final AMQShortString _routingKey;
@@ -108,6 +108,11 @@ public class MethodConverter_0_9 extends AbstractMethodConverter implements Prot
return _exchange;
}
+ public void setExchange(AMQShortString exchange)
+ {
+ _exchange = exchange;
+ }
+
public boolean isImmediate()
{
return _immediate;
diff --git a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java
index 7a13af8a43..b1be49a350 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java
@@ -107,7 +107,7 @@ public class MethodConverter_8_0 extends AbstractMethodConverter implements Prot
private static class MessagePublishInfoImpl implements MessagePublishInfo
{
- private final AMQShortString _exchange;
+ private AMQShortString _exchange;
private final boolean _immediate;
private final boolean _mandatory;
private final AMQShortString _routingKey;
@@ -128,6 +128,11 @@ public class MethodConverter_8_0 extends AbstractMethodConverter implements Prot
return _exchange;
}
+ public void setExchange(AMQShortString exchange)
+ {
+ _exchange = exchange;
+ }
+
public boolean isImmediate()
{
return _immediate;
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 cbe08a192e..8352b5af77 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
@@ -29,8 +29,8 @@ import org.apache.qpid.pool.Event.CloseEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+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
@@ -84,9 +84,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;
@@ -94,7 +91,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.
@@ -102,10 +101,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;
}
/**
@@ -160,20 +160,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");
+ }
}
}
@@ -186,7 +200,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);
}
@@ -197,7 +211,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);
}
@@ -233,17 +247,57 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
// }
// }
// else
+
+
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.
*
@@ -400,7 +454,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));
}
/**
@@ -412,8 +466,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));
}
/**
@@ -424,7 +478,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));
}
}
@@ -442,7 +497,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));
}
/**
@@ -454,7 +509,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));
}
/**
@@ -465,7 +521,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 035645aad2..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,8 +20,8 @@
*/
package org.apache.qpid.protocol;
-import org.apache.qpid.framing.VersionSpecificRegistry;
-import org.apache.qpid.framing.MethodRegistry;
+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
@@ -46,4 +46,12 @@ public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, Proto
// 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/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
index 0b6ed81d18..b93dc46741 100644
--- a/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java
+++ b/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java
@@ -131,19 +131,20 @@ public class IOWriterClient implements Runnable
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();
- int buf_size = ((SocketSessionConfig) session.getConfig()).getSendBufferSize();
ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
- readfilter.setMaximumConnectionBufferSize(buf_size);
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER)));
readfilter.attach(chain);
WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
-// writefilter.setMaximumConnectionBufferCount(1000);
- writefilter.setMaximumConnectionBufferSize(buf_size * 2);
+
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER)));
writefilter.attach(chain);
}
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
index 82ef3d57cc..423e98c67b 100644
--- a/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java
+++ b/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java
@@ -39,6 +39,9 @@ public class IOWriterServer
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
{
@@ -52,14 +55,14 @@ public class IOWriterServer
{
IoFilterChain chain = ioSession.getFilterChain();
- int buf_size = ((SocketSessionConfig) ioSession.getConfig()).getReceiveBufferSize();
-
ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder();
- readfilter.setMaximumConnectionBufferSize(buf_size);
+ readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.read.buffer.limit", DEFAULT_READ_BUFFER)));
readfilter.attach(chain);
WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder();
- writefilter.setMaximumConnectionBufferSize(buf_size * 2);
+
+ writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty("qpid.write.buffer.limit", DEFAULT_WRITE_BUFFER)));
+
writefilter.attach(chain);
}
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..90584183ee
--- /dev/null
+++ b/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.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 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/integrationtests/pom.xml b/java/integrationtests/pom.xml
index 69cf8fef00..851ef27377 100644
--- a/java/integrationtests/pom.xml
+++ b/java/integrationtests/pom.xml
@@ -1,146 +1,146 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT 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">
-
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-integrationtests</artifactId>
- <packaging>jar</packaging>
- <version>1.0-incubating-M2.1-SNAPSHOT</version>
- <name>Qpid Integration Tests</name>
-
- <parent>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid</artifactId>
- <version>1.0-incubating-M2.1-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <properties>
- <topDirectoryLocation>..</topDirectoryLocation>
- </properties>
-
- <dependencies>
-
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-client</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.apache.qpid</groupId>
- <artifactId>qpid-systests</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.4.0</version>
- </dependency>
-
- <dependency>
- <groupId>uk.co.thebadgerset</groupId>
- <artifactId>junit-toolkit</artifactId>
- <scope>compile</scope>
- </dependency>
-
- <!-- JUnit is a compile and runtime dependancy for these tests, not just a test time dependency. -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>compile</scope>
- </dependency>
-
- <!-- These need to be included at compile time only, for the retrotranslator verification to find them. -->
- <dependency>
- <groupId>net.sf.retrotranslator</groupId>
- <artifactId>retrotranslator-runtime</artifactId>
- <scope>provided</scope>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- </plugin>
-
- <!-- Backports the module to Java 1.4. This is done during the packaging phase as a transformation of the Jar. -->
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>retrotranslator-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>retro-intergration-tests</id>
- <phase>package</phase>
- <goals>
- <goal>translate-project</goal>
- </goals>
- <configuration>
- <!--<destdir>${project.build.directory}/retro-classes</destdir>-->
- <destjar>${project.build.directory}/${project.build.finalName}-java14.jar</destjar>
- <verify>${retrotranslator.verify}</verify>
- <verifyClasspath>
- <element>${retrotranslator.1.4-rt-path}</element>
- <element>${retrotranslator.1.4-jce-path}</element>
- <element>${retrotranslator.1.4-jsse-path}</element>
- <element>${retrotranslator.1.4-sasl-path}</element>
- </verifyClasspath>
- <failonwarning>false</failonwarning>
- <classifier>java14</classifier>
- <attach>true</attach>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-assembly-plugin</artifactId>
- <version>2.2-SNAPSHOT</version>
- <configuration>
- <descriptors>
- <descriptor>jar-with-dependencies.xml</descriptor>
- </descriptors>
- <outputDirectory>target</outputDirectory>
- <workDirectory>target/assembly/work</workDirectory>
- </configuration>
- </plugin>
-
- </plugins>
-
- <resources>
- <!-- Ensure all resources defined in the resources directory are copied into the build jar. -->
- <resource>
- <targetPath></targetPath>
- <filtering>false</filtering>
- <directory>src/resources</directory>
- <includes>
- <include>**/*</include>
- </includes>
- </resource>
- </resources>
-
- </build>
-
-</project>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-integrationtests</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <name>Qpid Integration Tests</name>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-systests</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- JUnit is a compile and runtime dependancy for these tests, not just a test time dependency. -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- These need to be included at compile time only, for the retrotranslator verification to find them. -->
+ <dependency>
+ <groupId>net.sf.retrotranslator</groupId>
+ <artifactId>retrotranslator-runtime</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+
+ <!-- Backports the module to Java 1.4. This is done during the packaging phase as a transformation of the Jar. -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>retrotranslator-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>retro-intergration-tests</id>
+ <phase>package</phase>
+ <goals>
+ <goal>translate-project</goal>
+ </goals>
+ <configuration>
+ <!--<destdir>${project.build.directory}/retro-classes</destdir>-->
+ <destjar>${project.build.directory}/${project.build.finalName}-java14.jar</destjar>
+ <verify>${retrotranslator.verify}</verify>
+ <verifyClasspath>
+ <element>${retrotranslator.1.4-rt-path}</element>
+ <element>${retrotranslator.1.4-jce-path}</element>
+ <element>${retrotranslator.1.4-jsse-path}</element>
+ <element>${retrotranslator.1.4-sasl-path}</element>
+ </verifyClasspath>
+ <failonwarning>false</failonwarning>
+ <classifier>java14</classifier>
+ <attach>true</attach>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2-SNAPSHOT</version>
+ <configuration>
+ <descriptors>
+ <descriptor>jar-with-dependencies.xml</descriptor>
+ </descriptors>
+ <outputDirectory>target</outputDirectory>
+ <workDirectory>target/assembly/work</workDirectory>
+ </configuration>
+ </plugin>
+
+ </plugins>
+
+ <resources>
+ <!-- Ensure all resources defined in the resources directory are copied into the build jar. -->
+ <resource>
+ <targetPath></targetPath>
+ <filtering>false</filtering>
+ <directory>src/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+
+ </build>
+
+</project>
diff --git a/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java
index 2764f2c3aa..5ed502287d 100644
--- a/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java
+++ b/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java
@@ -335,9 +335,9 @@ public class SustainedClientTestCase extends TestCase3BasicPubSub implements Exc
public void onMessage(Message message)
{
- if (log.isTraceEnabled())
+ if (log.isDebugEnabled())
{
- log.trace("Message " + _received + "received in listener");
+ log.debug("Message " + _received + "received in listener");
}
if (message instanceof TextMessage)
diff --git a/java/junit-toolkit-maven-plugin/pom.xml b/java/junit-toolkit-maven-plugin/pom.xml
new file mode 100644
index 0000000000..8d995aaa2d
--- /dev/null
+++ b/java/junit-toolkit-maven-plugin/pom.xml
@@ -0,0 +1,61 @@
+<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">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit-maven-plugin</artifactId>
+ <name>junit-toolkit-maven-plugin</name>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+
+ <packaging>maven-plugin</packaging>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.12</version>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>2.0.4</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/main</sourceDirectory>
+ <testSourceDirectory>src/unittests</testSourceDirectory>
+ </build>
+
+</project> \ No newline at end of file
diff --git a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java
new file mode 100644
index 0000000000..dc74590f60
--- /dev/null
+++ b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java
@@ -0,0 +1,113 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.maven;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @author Rupert Smith
+ *
+ * @noinspection CustomClassloader
+ */
+public class IsolatedClassLoader extends URLClassLoader
+{
+
+ private static final URL[] EMPTY_URL_ARRAY = new URL[0];
+ private ClassLoader parent = ClassLoader.getSystemClassLoader();
+
+ private Set urls = new HashSet();
+
+ private boolean childDelegation = true;
+
+ public IsolatedClassLoader()
+ {
+ super(EMPTY_URL_ARRAY, null);
+ }
+
+ public IsolatedClassLoader(ClassLoader parent, boolean childDelegation)
+ {
+ super(EMPTY_URL_ARRAY, parent);
+
+ this.childDelegation = childDelegation;
+ }
+
+ public IsolatedClassLoader(ClassLoader parent)
+ {
+ super(EMPTY_URL_ARRAY, parent);
+ }
+
+ public void addURL(URL url)
+ {
+ // avoid duplicates
+ if (!urls.contains(url))
+ {
+ super.addURL(url);
+ urls.add(url);
+ }
+ }
+
+ public synchronized Class loadClass(String name) throws ClassNotFoundException
+ {
+ Class c;
+
+ if (childDelegation)
+ {
+ c = findLoadedClass(name);
+
+ ClassNotFoundException ex = null;
+
+ if (c == null)
+ {
+ try
+ {
+ c = findClass(name);
+ }
+ catch (ClassNotFoundException e)
+ {
+ ex = e;
+
+ if (parent != null)
+ {
+ c = parent.loadClass(name);
+ }
+ }
+ }
+
+ if (c == null)
+ {
+ throw ex;
+ }
+ }
+ else
+ {
+ c = super.loadClass(name);
+ }
+
+ return c;
+ }
+}
diff --git a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java
new file mode 100644
index 0000000000..442529b609
--- /dev/null
+++ b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java
@@ -0,0 +1,274 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.maven;
+
+import org.apache.maven.plugin.AbstractMojo;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * TKTestRunnerMojo is a JUnit test runner plugin for Maven 2. It is intended to be compatible with the surefire
+ * plugin (though not all features of that are yet implemented), with some extended capabilities.
+ *
+ * <p/>This plugin adds the ability to use different JUnit test runners, and to pass arbitrary options to them.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @author Rupert Smith
+ *
+ * @goal tktest
+ * @phase test
+ * @requiresDependencyResolution test
+ */
+public class TKTestRunnerMojo extends AbstractMojo
+{
+ private static final BitSet UNRESERVED = new BitSet(256);
+
+ /**
+ * Set this to 'true' to bypass unit tests entirely. Its use is NOT RECOMMENDED, but quite convenient on occasion.
+ *
+ * @parameter expression="${maven.test.skip}"
+ */
+ private boolean skip;
+
+ /**
+ * The TKTest runner command lines. There are passed directly to the TKTestRunner main method.
+ *
+ * @parameter
+ */
+ private Map<String, String> commands = new LinkedHashMap<String, String>();
+
+ /**
+ * The base directory of the project being tested. This can be obtained in your unit test by
+ * System.getProperty("basedir").
+ *
+ * @parameter expression="${basedir}"
+ * @required
+ */
+ private File basedir;
+
+ /**
+ * The directory containing generated classes of the project being tested.
+ *
+ * @parameter expression="${project.build.outputDirectory}"
+ * @required
+ */
+ private File classesDirectory;
+
+ /**
+ * The directory containing generated test classes of the project being tested.
+ *
+ * @parameter expression="${project.build.testOutputDirectory}"
+ * @required
+ */
+ private File testClassesDirectory;
+
+ /**
+ * The classpath elements of the project being tested.
+ *
+ * @parameter expression="${project.testClasspathElements}"
+ * @required
+ * @readonly
+ */
+ private List classpathElements;
+
+ /**
+ * List of System properties to pass to the tests.
+ *
+ * @parameter
+ */
+ private Properties systemProperties;
+
+ /**
+ * Map of of plugin artifacts.
+ *
+ * @parameter expression="${plugin.artifactMap}"
+ * @required
+ * @readonly
+ */
+ private Map pluginArtifactMap;
+
+ /**
+ * Map of of project artifacts.
+ *
+ * @parameter expression="${project.artifactMap}"
+ * @required
+ * @readonly
+ */
+ private Map projectArtifactMap;
+
+ /**
+ * Option to specify the forking mode. Can be "never" (default), "once" or "always".
+ * "none" and "pertest" are also accepted for backwards compatibility.
+ *
+ * @parameter expression="${forkMode}" default-value="once"
+ */
+ private String forkMode;
+
+ /**
+ * Option to specify the jvm (or path to the java executable) to use with
+ * the forking options. For the default we will assume that java is in the path.
+ *
+ * @parameter expression="${jvm}"
+ * default-value="java"
+ */
+ private String jvm;
+
+ /**
+ * The test runner to use.
+ *
+ * @parameter
+ */
+ private String testrunner;
+
+ /**
+ * The additional properties to append to the test runner invocation command line.
+ *
+ * @parameter
+ */
+ private Properties testrunnerproperties;
+
+ /**
+ * The options to pass to all test runner invocation command lines.
+ *
+ * @parameter
+ */
+ private String[] testrunneroptions;
+
+ /**
+ * Implementation of the tktest goal.
+ */
+ public void execute()
+ {
+ // Skip these tests if test skipping is turned on.
+ if (skip)
+ {
+ getLog().info("Skipping Tests.");
+
+ return;
+ }
+
+ // Log out the classpath if debugging is on.
+ if (getLog().isDebugEnabled())
+ {
+ getLog().info("Test Classpath :");
+
+ for (Object classpathElement1 : classpathElements)
+ {
+ String classpathElement = (String) classpathElement1;
+ getLog().info(" " + classpathElement);
+ }
+ }
+
+ try
+ {
+ // Create a class loader to load the test runner with. This also gets set as the context loader for this
+ // thread, so that all subsequent class loading activity by the test runner or the test code, has the
+ // test classes available to it. The system loader is set up for the maven build, which is why a seperate
+ // loader needs to be created; in order to inject the test dependencies into it.
+ ClassLoader runnerClassLoader = createClassLoader(classpathElements, ClassLoader.getSystemClassLoader(), true);
+ Thread.currentThread().setContextClassLoader(runnerClassLoader);
+
+ // Load the test runner implementation that will be used to run the tests.
+ if ((testrunner == null) || "".equals(testrunner))
+ {
+ testrunner = "org.apache.qpid.junit.extensions.TKTestRunner";
+ }
+
+ Class testRunnerClass = Class.forName(testrunner, false, runnerClassLoader);
+ Method run = testRunnerClass.getMethod("main", String[].class);
+
+ // Concatenate all of the options to pass on the command line to the test runner.
+ String preOptions = "";
+
+ for (String option : testrunneroptions)
+ {
+ preOptions += option + " ";
+ }
+
+ // Concatenate all of the additional properties as name=value pairs on the command line.
+ String nvPairs = "";
+
+ if (testrunnerproperties != null)
+ {
+ for (Object objKey : testrunnerproperties.keySet())
+ {
+ String key = (String) objKey;
+ String value = testrunnerproperties.getProperty(key);
+
+ nvPairs = key + "=" + value + " ";
+ }
+ }
+
+ // Pass each of the test runner command lines in turn to the toolkit test runner.
+ // The command line is made up of the options, the command specific command line, then the trailing
+ // name=value pairs.
+ for (String testName : commands.keySet())
+ {
+ String commandLine = preOptions + " " + commands.get(testName) + " " + nvPairs;
+ getLog().info("commandLine = " + commandLine);
+
+ // Tokenize the command line on white space, into an array of string components.
+ String[] tokenizedCommandLine = commandLine.split("\\s+");
+
+ // Run the tests.
+ run.invoke(testRunnerClass, new Object[] { tokenizedCommandLine });
+ }
+ }
+ catch (Exception e)
+ {
+ getLog().error("There was an exception: " + e.getMessage(), e);
+ }
+ }
+
+ private static ClassLoader createClassLoader(List classPathUrls, ClassLoader parent, boolean childDelegation)
+ throws MalformedURLException
+ {
+ List urls = new ArrayList();
+
+ for (Iterator i = classPathUrls.iterator(); i.hasNext();)
+ {
+ String url = (String) i.next();
+
+ if (url != null)
+ {
+ File f = new File(url);
+ urls.add(f.toURL());
+ }
+ }
+
+ IsolatedClassLoader classLoader = new IsolatedClassLoader(parent, childDelegation);
+
+ for (Iterator iter = urls.iterator(); iter.hasNext();)
+ {
+ URL url = (URL) iter.next();
+ classLoader.addURL(url);
+ }
+
+ return classLoader;
+ }
+}
diff --git a/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java
new file mode 100644
index 0000000000..5c7669e069
--- /dev/null
+++ b/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java
@@ -0,0 +1,148 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.maven;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @author Rupert Smith
+ * @goal tkscriptgen
+ * @phase test
+ * @execute phase="test"
+ * @requiresDependencyResolution test
+ */
+public class TKTestScriptGenMojo extends AbstractMojo
+{
+ private static final String _scriptLanguage = "#!/bin/bash\n\n";
+
+ private static final String _javaOptArgParser =
+ "# Parse arguements taking all - prefixed args as JAVA_OPTS\n" + "for arg in \"$@\"; do\n"
+ + " if [[ $arg == -java:* ]]; then\n" + " JAVA_OPTS=\"${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` \"\n"
+ + " else\n" + " ARGS=\"${ARGS}$arg \"\n" + " fi\n" + "done\n\n";
+
+ /**
+ * Where to write out the scripts.
+ *
+ * @parameter
+ */
+ private String scriptOutDirectory;
+
+ /**
+ * The all-in-one test jar location.
+ *
+ * @parameter
+ */
+ private String testJar;
+
+ /**
+ * The system properties to pass to java runtime.
+ *
+ * @parameter
+ */
+ private Properties systemproperties;
+
+ /**
+ * The TKTest runner command lines. There are passed directly to the TKTestRunner main method.
+ *
+ * @parameter
+ */
+ private Map<String, String> commands = new LinkedHashMap<String, String>();
+
+ /**
+ * Implementation of the tkscriptgen goal.
+ *
+ * @throws MojoExecutionException
+ */
+ public void execute() throws MojoExecutionException
+ {
+ // Turn each of the test runner command lines into a script.
+ for (String testName : commands.keySet())
+ {
+ String testOptions = commands.get(testName);
+ String commandLine = "java ";
+
+ String logdir = null;
+
+ for (Object key : systemproperties.keySet())
+ {
+ String keyString = (String) key;
+ String value = systemproperties.getProperty(keyString);
+
+ if (keyString.equals("logdir"))
+ {
+ logdir = value;
+ }
+ else
+ {
+ if (keyString.startsWith("-X"))
+ {
+ commandLine += keyString + value + " ";
+ }
+ else
+ {
+ commandLine += "-D" + keyString + "=" + value + " ";
+ }
+ }
+ }
+
+ commandLine +=
+ "${JAVA_OPTS} -cp " + testJar + " org.apache.qpid.junit.extensions.TKTestRunner " + testOptions + " ${ARGS}";
+
+ getLog().info("Generating Script for test: " + testName);
+ getLog().debug(commandLine);
+
+ String fileName = scriptOutDirectory + "/" + testName + ".sh";
+
+ try
+ {
+ File scriptFile = new File(fileName);
+ Writer scriptWriter = new FileWriter(scriptFile);
+ scriptWriter.write(_scriptLanguage);
+ scriptWriter.write(_javaOptArgParser);
+ if (logdir != null)
+ {
+ scriptWriter.write("mkdir -p " + logdir + "\n");
+ }
+
+ scriptWriter.write(commandLine);
+ scriptWriter.flush();
+ scriptWriter.close();
+ }
+ catch (IOException e)
+ {
+ getLog().error("Failed to write: " + fileName);
+ }
+ }
+ }
+}
diff --git a/java/junit-toolkit/pom.xml b/java/junit-toolkit/pom.xml
new file mode 100644
index 0000000000..d5f8c24734
--- /dev/null
+++ b/java/junit-toolkit/pom.xml
@@ -0,0 +1,57 @@
+<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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <name>junit-toolkit</name>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+
+ <packaging>jar</packaging>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.12</version>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/main</sourceDirectory>
+ <testSourceDirectory>src/unittests</testSourceDirectory>
+ </build>
+
+</project> \ No newline at end of file
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java
index 2baaa344ef..6c88d019c4 100644
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java
@@ -18,25 +18,31 @@
* under the License.
*
*/
-package org.apache.qpid.server.cluster;
+package org.apache.qpid.junit.concurrency;
-import org.apache.qpid.AMQException;
+import java.util.concurrent.ThreadFactory;
/**
- * AMQConnectionWaitException represents a failure to connect to a cluster peer in a timely manner.
+ * Implements a default thread factory.
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represents failure to connect to a cluster peer in a timely manner.
+ * <tr><td> Create default threads with no specialization.
* </table>
*
- * @todo Not an AMQP exception as no status code.
+ * @author Rupert Smith
*/
-public class AMQConnectionWaitException extends AMQException
+public class DefaultThreadFactory implements ThreadFactory
{
- public AMQConnectionWaitException(String s, Throwable e)
+ /**
+ * Constructs a new <tt>Thread</tt>.
+ *
+ * @param r A runnable to be executed by new thread instance.
+ *
+ * @return The constructed thread.
+ */
+ public Thread newThread(Runnable r)
{
- super(s, e);
-
+ return new Thread(r);
}
}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java
index 4dd318f90d..0bb07a4557 100644
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java
@@ -18,28 +18,29 @@
* under the License.
*
*/
-package org.apache.qpid.server.cluster;
-
-import org.apache.qpid.AMQException;
+package org.apache.qpid.junit.concurrency;
/**
- * AMQUnexpectedFrameTypeException represents a failure when Mina passes an unexpected frame type.
+ * PossibleDeadlockException is used to signal that two test threads being executed by a {@link ThreadTestCoordinator}
+ * may be in a state of deadlock because they are mutually blocking each other or one is waiting on the other and the
+ * other has been blocked elsewhere for longer than a specified timeout.
*
* <p/><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represents failure to cast a frame to its expected type.
+ * <tr><td> Signal a possible state of deadlock between coordinated test threads.
* </table>
*
- * @todo Not an AMQP exception as no status code.
- *
- * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would
- * be better just to leave that as a ClassCastException. However, check the framing layer catches this error
- * first.
+ * @author Rupert Smith
*/
-public class AMQUnexpectedFrameTypeException extends AMQException
+public class PossibleDeadlockException extends RuntimeException
{
- public AMQUnexpectedFrameTypeException(String s)
+ /**
+ * Create a new possible deadlock execption.
+ *
+ * @param message The exception message.
+ */
+ public PossibleDeadlockException(String message)
{
- super(s);
+ super(message);
}
}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java
new file mode 100644
index 0000000000..5bf0c430cd
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java
@@ -0,0 +1,239 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.concurrency;
+
+/**
+ * TestRunnable is an extension of java.util.Runnable that adds some features to make it easier to coordinate the
+ * activities of threads in such a way as to expose bugs in multi threaded code.
+ *
+ * <p/>Sometimes several threads will run in a particular order so that a bug is not revealed. Other times the ordering
+ * of the threads will expose a bug. Such bugs can be hard to replicate as the exact execution ordering of threads is not
+ * usually controlled. This class adds some methods that allow threads to synchronize other threads, either allowing them
+ * to run, or waiting for them to allow this thread to run. It also provides convenience methods to gather error messages
+ * and exceptions from threads, which will often be reported in unit testing code.
+ *
+ * <p/>Coordination between threads is handled by the {@link ThreadTestCoordinator}. It is called through the convenience
+ * methods {@link #allow} and {@link #waitFor}. Threads to be coordinated must be set up with the coordinator and assigned
+ * integer ids. It is then possible to call the coordinator with an array of thread ids requesting that those threads
+ * be allowed to continue, or to wait until one of them allows this thread to continue. The otherwise non-deterministic
+ * execution order of threads can be controlled into a carefully determined sequence using these methods in order
+ * to reproduce race conditions, dead locks, live locks, dirty reads, phantom reads, non repeatable reads and so on.
+ *
+ * <p/>When waiting for another thread to give a signal to continue it is sometimes the case that the other thread has
+ * become blocked by the code under test. For example in testing for a dirty read (for example in database code),
+ * thread 1 lets thread 2 perform a write but not commit it, then thread 2 lets thread 1 run and attempt to perform a
+ * dirty read on its uncommitted write. Transaction synchronization code being tested against the possibility of a dirty
+ * write may make use of snapshots in which case both threads should be able to read and write without blocking. It may
+ * make use of explicit keys in which case thread 2 may become blocked on its write attempt because thread 1 holds a
+ * read lock and it must wait until thread 1 completes its transaction before it can acquire this lock. The
+ * {@link #waitFor} method accepts a boolean parameter to indicate that threads being blocked (other than on the
+ * coordinator) can be interpreted the same as if the thread explicitly allows the thread calling waitFor to continue.
+ * Using this technique a dirty read test could be written that works against either the snapshot or the locking
+ * implementation, allowing both approaches to pass the test yet arranging for multiple threads to run against the
+ * implementation in such a way that a potential dirty read bug is exposed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Wait for another thread to allow this one to continue.
+ * <tr><td> Allow another thread to continue.
+ * <tr><td> Accumulate error messages.
+ * <tr><td> Record exceptions from thread run.
+ * <tr><td> Maintain link to thread coordinator.
+ * <tr><td> Explicitly mark a thread with an integer id.
+ * <tr><td> Maintian a flag to indicate whether or not this thread is waiting on the coordinator.
+ * </table>
+ *
+ * @todo The allow then waitFor operations are very often used as a pair. So create a method allowAndWait that combines
+ * them into a single method call.
+ *
+ * @author Rupert Smith
+ */
+public abstract class TestRunnable implements Runnable
+{
+ /** Holds a reference to the thread coordinator. */
+ private ThreadTestCoordinator coordinator;
+
+ /** Holds the explicit integer id of this thread. */
+ private int id;
+
+ /** Used to indicate that this thread is waiting on the coordinator and not elsewhere. */
+ private boolean waitingOnCoordinator = false;
+
+ /** Used to accumulate error messsages. */
+ private String errorMessage = "";
+
+ /** Holds the Java thread object that this is running under. */
+ private Thread thisThread;
+
+ /** Used to hold any exceptions resulting from the run method. */
+ private Exception runException = null;
+
+ /**
+ * Implementations override this to perform coordinated thread sequencing.
+ *
+ * @throws Exception Any exception raised by the implementation will be caught by the default {@link #run()}
+ * implementation for later querying by the {@link #getException()} method.
+ */
+ public abstract void runWithExceptions() throws Exception;
+
+ /**
+ * Provides a default implementation of the run method that allows exceptions to be thrown and keeps a record
+ * of those exceptions. Defers to the {@link #runWithExceptions()} method to provide the thread body implementation
+ * and catches any exceptions thrown by it.
+ */
+ public void run()
+ {
+ try
+ {
+ runWithExceptions();
+ }
+ catch (Exception e)
+ {
+ this.runException = e;
+ }
+ }
+
+ /**
+ * Attempt to consume an allow event from one of the specified threads and blocks until such an event occurrs.
+ *
+ * @param threads The set of threads that can allow this one to continue.
+ * @param otherWaitIsAllow If set to <tt>true</tt> if the threads being waited on are blocked other than on
+ * the coordinator itself then this is to be interpreted as allowing this thread to
+ * continue.
+ *
+ * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found
+ * to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions.
+ */
+ protected boolean waitFor(int[] threads, boolean otherWaitIsAllow)
+ {
+ return coordinator.consumeAllowEvent(threads, otherWaitIsAllow, id, this);
+ }
+
+ /**
+ * Produces allow events on each of the specified threads.
+ *
+ * @param threads The set of threads that are to be allowed to continue.
+ */
+ protected void allow(int[] threads)
+ {
+ coordinator.produceAllowEvents(threads, id, this);
+ }
+
+ /**
+ * Keeps the error message for later reporting by the coordinator.
+ *
+ * @param message The error message to keep.
+ */
+ protected void addErrorMessage(String message)
+ {
+ errorMessage += message;
+ }
+
+ /**
+ * Sets the coordinator for this thread.
+ *
+ * @param coordinator The coordinator for this thread.
+ */
+ void setCoordinator(ThreadTestCoordinator coordinator)
+ {
+ this.coordinator = coordinator;
+ }
+
+ /**
+ * Reports whether or not this thread is waiting on the coordinator.
+ *
+ * @return <tt>If this thread is waiting on the coordinator.
+ */
+ boolean isWaitingOnCoordinator()
+ {
+ return waitingOnCoordinator;
+ }
+
+ /**
+ * Sets the value of the waiting on coordinator flag.
+ *
+ * @param waiting The value of the waiting on coordinator flag.
+ */
+ void setWaitingOnCoordinator(boolean waiting)
+ {
+ waitingOnCoordinator = waiting;
+ }
+
+ /**
+ * Sets up the explicit int id for this thread.
+ *
+ * @param id The integer id.
+ */
+ void setId(int id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Reports any accumulated error messages.
+ *
+ * @return Any accumulated error messages.
+ */
+ String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * Reports any exception thrown by the {@link #runWithExceptions} method.
+ *
+ * @return Any exception thrown by the {@link #runWithExceptions} method.
+ */
+ Exception getException()
+ {
+ return runException;
+ }
+
+ /**
+ * Sets the Java thread under which this runs.
+ *
+ * @param thread The Java thread under which this runs.
+ */
+ void setThread(Thread thread)
+ {
+ thisThread = thread;
+ }
+
+ /**
+ * Gets the Java thread under which this runs.
+ *
+ * @return The Java thread under which this runs.
+ */
+ Thread getThread()
+ {
+ return thisThread;
+ }
+
+ /**
+ * Provides a string summary of this test threads status.
+ *
+ * @return Summarizes this threads status.
+ */
+ public String toString()
+ {
+ return "id = " + id + ", waitingOnCoordinator = " + waitingOnCoordinator;
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java
new file mode 100644
index 0000000000..0be0fe37dc
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java
@@ -0,0 +1,485 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.concurrency;
+
+import org.apache.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * ThreadTestCoordinator provides an array of binary latches that allows threads to wait for other threads or to send
+ * them a signal that allows them to continue running or to wait for another thread to signal them. The binary latch
+ * array is always a square array, allowing one latch from and to every thread. Upon accepting an allow signal from one
+ * sender the latches for all senders for a are cleared. This class is always used in conjunction with
+ * {@link TestRunnable} for writing concurrent test code that coordinates multi-threaded activity in order to reproduce
+ * concurrency bugs.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept test threads to coordinate.
+ * <tr><td> Allow test threads to send 'allow to continue' signals.
+ * <tr><td> Allow test threads to wait on this coordinator for 'allow to continue' signals.
+ * <tr><td> Report error messages from test threads.
+ * <tr><td> Report exceptions from test threads.
+ * <tr><td> Provide method to wait until all test threads have completed.
+ * </table>
+ *
+ * @todo This code was hacked together as a bit of an experiment, because I wasn't sure if this idea would work. It has
+ * proved extremely usefull. Some documentation for this needs to be written to explain it better.
+ *
+ * @todo Consider how deadlock detection will be handled. If all threads are blocking on the coordinator, waiting for
+ * each other, they are deadlocked and there is something wrong with the test code that put them in that
+ * situation. If they are all blocked elsewhere, they may be deadlocked, or could just be waiting on some
+ * external event. A timeout should be used. Timeout is already implemented, just need to sanity check how
+ * this is working and document it.
+ *
+ * @todo Consider how livelock detection could be implemented? LockFree data structures might cause live locks. I
+ * guess a longish timeout is the only thing that can be done for that.
+ *
+ * @todo Only course grained synchronous at the method class level can be obtained. This is because test code can
+ * only insert synchronization points between method calls it makes. So this code will not be usefull for
+ * checking sequences of events within methods, unless the code under test is explicitly instrumented for it.
+ * It might be possible to instrument code by using labels, and then use the debugger/profiler interface to
+ * put breakpoints on the labels and use them as synchronization points. Not perfect, but at the unused labels
+ * can be left in the code, without altering its behaviour.
+ *
+ * @author Rupert Smith
+ */
+public class ThreadTestCoordinator
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(ThreadTestCoordinator.class);
+
+ /** Keeps track of the test threads by their ids. */
+ private TestRunnable[] testThreads; // = new TestRunnable[2];
+
+ /** An explicit thread monitor for the coordinator. Threads wait on the coordinator whilst waiting for events. */
+ private final Object coordinatorLock = new Object();
+
+ /** A set of monitors for each test thread. */
+ private Object[] locks;
+
+ /** The binary latch array, this is always a square array allowing one event from and to every thread. */
+ private boolean[][] allowEvents;
+
+ /** Keeps track of the number of threads being coordinated. */
+ private int threadCount = 0;
+
+ /** Accumulates any exceptions resulting from the threads run methods. */
+ private Collection<Exception> exceptions = new ArrayList<Exception>();
+
+ /**
+ * Holds the deadlock timeout after which threads are given a runtime exception to signal that a potential
+ * deadlock may be happening.
+ */
+ private long deadlockTimeout = 1000 * 1000000;
+
+ /** Holds the factory to create test thread with. */
+ private ThreadFactory threadFactory;
+
+ /**
+ * Creates a new test thread coordinator. The number of threads to run must be specified here.
+ *
+ * @param numThreads The number of threads to run.
+ */
+ public ThreadTestCoordinator(int numThreads)
+ {
+ this.threadCount = numThreads;
+
+ // Create an array big enough to hold all the test threads.
+ testThreads = new TestRunnable[threadCount];
+
+ // Use the default thread factory, as none specified.
+ threadFactory = new DefaultThreadFactory();
+ }
+
+ /**
+ * Creates a new test thread coordinator with a specific thread factory. The number of threads to run must be
+ * specified here.
+ *
+ * @param numThreads The number of threads to run.
+ * @param threadFactory The factory to use to create the test threads.
+ */
+ public ThreadTestCoordinator(int numThreads, ThreadFactory threadFactory)
+ {
+ this.threadCount = numThreads;
+
+ // Create an array big enough to hold all the test threads.
+ testThreads = new TestRunnable[threadCount];
+
+ // Use the specified thread factory.
+ this.threadFactory = threadFactory;
+ }
+
+ /**
+ * Adds a thread to this coordinator and assigns an id to it. The ids must be numbered sequentially from 0 and
+ * it is up to the caller to do this.
+ *
+ * @param runnable The test thread.
+ * @param id The explicit id to assign to the test thread.
+ */
+ public void addTestThread(TestRunnable runnable, int id)
+ {
+ testThreads[id] = runnable;
+ runnable.setCoordinator(this);
+ runnable.setId(id);
+ }
+
+ /**
+ * Starts all the coordinated threads running.
+ */
+ public void run()
+ {
+ // Create the monitors for each thread.
+ locks = new Object[threadCount];
+
+ // Create an appropriately sized event queue to allow one event from and to each thread.
+ allowEvents = new boolean[threadCount][threadCount];
+
+ // Initialize the monitors and clear the event queues.
+ for (int i = 0; i < locks.length; i++)
+ {
+ locks[i] = new Object();
+
+ for (int j = 0; j < locks.length; j++)
+ {
+ allowEvents[i][j] = false;
+ }
+ }
+
+ // Start all the threads running.
+ for (TestRunnable nextRunnable : testThreads)
+ {
+ // Create a Java thread for the test thread.
+ Thread newThread = threadFactory.newThread(nextRunnable);
+ nextRunnable.setThread(newThread);
+
+ // Start it running.
+ newThread.start();
+ }
+ }
+
+ /**
+ * Waits until all the test threads have completed and returns any accumulated error messages from them. Any
+ * exceptions thrown by their run methods are also kept at this point.
+ *
+ * @return The accumulated error messages from all the threads concatenated together.
+ */
+ public String joinAndRetrieveMessages()
+ {
+ // Create an empty error message.
+ String errorMessage = "";
+
+ // Join all the test threads.
+ for (TestRunnable r : testThreads)
+ {
+ Thread t = r.getThread();
+
+ try
+ {
+ t.join();
+ }
+ catch (InterruptedException e)
+ { }
+
+ // Add any accumulated error messages to the return value.
+ errorMessage += r.getErrorMessage();
+
+ // Keep any exceptions resulting from the threads run method.
+ Exception e = r.getException();
+
+ if (e != null)
+ {
+ exceptions.add(e);
+ }
+ }
+
+ return errorMessage;
+ }
+
+ /**
+ * Reports any accumulated exceptions from the test threads run methods. This method must be called after
+ * {@link #joinAndRetrieveMessages}.
+ *
+ * @return Any accumulated exceptions from the test threads run methods. This method must be called after
+ */
+ public Collection<Exception> getExceptions()
+ {
+ return exceptions;
+ }
+
+ /**
+ * Sets a timeout to break out of potential deadlocks. If all threads are waiting for other threads to send
+ * them continue events for longer than this timeout then the threads are all terminated.
+ *
+ * @param millis The minimum time to allow to pass before breaking out of any potential deadlocks.
+ *
+ * @todo This has not been implemented yet. If a potential deadlock happens then the joinAndRetrieveMessages
+ * method should throw a PotentialDeadlockException.
+ */
+ public void setDeadlockTimeout(long millis)
+ {
+ deadlockTimeout = millis * 1000000;
+ }
+
+ /**
+ * Creates a set of 'allow to continue' events on the event queues of the specified threads.
+ *
+ * @param threads The set of threads to allow to continue.
+ * @param callerId The explicit id of the calling test thread.
+ * @param caller The calling test thread.
+ */
+ void produceAllowEvents(int[] threads, int callerId, TestRunnable caller)
+ {
+ // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
+ String message = "Thread " + callerId + " is allowing threads [ ";
+
+ for (int j = 0; j < threads.length; j++)
+ {
+ message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
+ }
+
+ message += " ] to continue.";
+ log.debug(message);
+
+ // For each allow event, synchronize on the threads lock then set the event flag to true.
+ for (int id : threads)
+ {
+ // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+ // being blocked at this time.
+ caller.setWaitingOnCoordinator(true);
+
+ synchronized (locks[id])
+ {
+ // Release the wating on coordinator flag now that this thread is running again.
+ caller.setWaitingOnCoordinator(false);
+
+ // Send the allow to continue event to the receiving thread.
+ allowEvents[id][callerId] = true;
+ }
+ }
+
+ // Wake up any threads waiting on the coordinator lock to recheck their event queues.
+ // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+ // being blocked at this time.
+ caller.setWaitingOnCoordinator(true);
+
+ synchronized (coordinatorLock)
+ {
+ // Release the wating on coordinator flag now that this thread is running again.
+ caller.setWaitingOnCoordinator(false);
+ coordinatorLock.notifyAll();
+ }
+ }
+
+ /**
+ * Consumes an 'allow to continue' from one of the specified threads or waits until one is available or in some
+ * cases if one of the specified threads is blocked elsewhere to accept that as an 'allow to continue' event.
+ *
+ * @param threads The set of threads to accept an allow to continue event from.
+ * @param otherWaitIsAllow Whether or not to accept threads being blocked elsewhere as permission to continue.
+ * @param callerId The explicit id of the calling test thread.
+ * @param caller The calling test thread.
+ *
+ * @return If the <tt>otherWaitIsAllow</tt> flag is set, then <tt>true</tt> is returned when the thread being waited on is found
+ * to be blocked outside of the thread test coordinator. <tt>false</tt> under all other conditions.
+ */
+ boolean consumeAllowEvent(int[] threads, boolean otherWaitIsAllow, int callerId, TestRunnable caller)
+ {
+ // Generate some debugging messages. Very usefull to know how thread synchronization is progressing.
+ String message = "Thread " + callerId + " is requesting threads [ ";
+
+ // Record the time at which this method was called. Will be used for breaking out of potential deadlocks.
+ long startTime = System.nanoTime();
+
+ for (int j = 0; j < threads.length; j++)
+ {
+ message += threads[j] + ((j < (threads.length - 1)) ? ", " : "");
+ }
+
+ message += " ] to allow it to continue.";
+ log.debug(message);
+
+ // Loop until an allow to continue event is received.
+ while (true)
+ {
+ // Look at all the allowing thread to see if one has created an event for consumption.
+ for (int allowerId : threads)
+ {
+ // Get the threads lock for the event to consume.
+ // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+ // being blocked at this time.
+ caller.setWaitingOnCoordinator(true);
+
+ synchronized (locks[callerId])
+ {
+ // Release the wating on coordinator flag now that this thread is running again.
+ caller.setWaitingOnCoordinator(false);
+
+ // Check if there is an event on the queue from the allowing thread to this one.
+ if (allowEvents[callerId][allowerId])
+ {
+ log.debug("Found an allow event, thread " + allowerId + ", is allowing thread " + callerId
+ + ", to continue.");
+
+ // Consume all the allow events for this thread.
+ /*for (int i = 0; i < allowEvents[callerId].length; i++)
+ {
+ allowEvents[callerId][i] = false;
+ }*/
+
+ // Consume just the event from the allower to the consumer, leaving other pending allow events alone.
+ allowEvents[callerId][allowerId] = false;
+
+ return false;
+ }
+ }
+ }
+
+ // If waiting elsewhere is to be interpreted as an 'allow to continue' event, then look at the thread status
+ // for the threads being waited on to see if any are blocked on other resources.
+ if (otherWaitIsAllow)
+ {
+ log.debug("Other wait is to be interpreted as an allow event.");
+
+ // Look at all the potential allower threads.
+ for (int allowerId : threads)
+ {
+ // Get the Java thread state for the allowing thread.
+ Thread threadToTest = testThreads[allowerId].getThread();
+ Thread.State state = threadToTest.getState();
+
+ // Check if the thread is blocked and so a potential candidate for releasing this one.
+ if ((state == Thread.State.BLOCKED) || (state == Thread.State.WAITING)
+ || (state == Thread.State.TIMED_WAITING))
+ {
+ log.debug("Found an allower thread, id = " + allowerId + ", that is blocked or wating.");
+
+ // Check that the allower thread is not waiting on the coordinator lock or any of the
+ // individual thread locks. It must be waiting or blocked on another monitor.
+ TestRunnable allowingRunnable = testThreads[allowerId];
+ boolean isWaitingOnCoordinator = allowingRunnable.isWaitingOnCoordinator();
+
+ if (!isWaitingOnCoordinator)
+ {
+ log.debug("The allower thread, id = " + allowerId
+ + ", is blocked or waiting other than on the coordinator.");
+
+ // Get the threads lock for the event to consume.
+ caller.setWaitingOnCoordinator(true);
+
+ synchronized (locks[callerId])
+ {
+ caller.setWaitingOnCoordinator(false);
+
+ // Consume all the allow events for this thread.
+ for (int i = 0; i < allowEvents[callerId].length; i++)
+ {
+ allowEvents[callerId][i] = false;
+ }
+
+ return true;
+ }
+ }
+ else
+ {
+ log.debug("The waiting allower thread, " + allowerId
+ + ", is waiting on the coordinator so does not allow thread " + callerId + " to continue.");
+ }
+ }
+ }
+ }
+
+ // Keep waiting until an 'allow to continue' event can be consumed.
+ try
+ {
+ // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+ // being blocked at this time.
+ caller.setWaitingOnCoordinator(true);
+
+ synchronized (coordinatorLock)
+ {
+ // Release the wating on coordinator flag now that this thread is running again.
+ caller.setWaitingOnCoordinator(false);
+
+ log.debug("Thread " + callerId + " is waiting on coordinator lock for more allow events.");
+
+ // Set the waiting on coordinator flag to true in case the coordinator tries to test this thread for
+ // being blocked at this time.
+ caller.setWaitingOnCoordinator(true);
+ coordinatorLock.wait(10);
+ }
+ }
+ catch (InterruptedException e)
+ { }
+
+ // Release the waiting on coordinator flag now that this thread is running again.
+ caller.setWaitingOnCoordinator(false);
+
+ // Check if this thread has been waiting for longer than the deadlock timeout and raise a possible
+ // deadlock exception if so.
+ long waitTime = System.nanoTime() - startTime;
+ log.debug("Thread " + callerId + " has been waiting for " + (waitTime / 1000000) + " milliseconds.");
+
+ if (waitTime > deadlockTimeout)
+ {
+ // Throw a possible deadlock exception.
+ throw new PossibleDeadlockException("Possible deadlock due to timeout with state:\n" + this);
+ }
+
+ log.debug("Thread " + callerId + " has woken up, was waiting for more allow events to become available.");
+ }
+ }
+
+ /**
+ * Pretty prints the state of the thread test coordinator, for debugging purposes.
+ *
+ * @return Pretty printed state of the thread test coordinator.
+ */
+ public String toString()
+ {
+ String result = "[";
+
+ for (int i = 0; i < allowEvents.length; i++)
+ {
+ for (int j = 0; j < allowEvents[i].length; j++)
+ {
+ result += allowEvents[i][j];
+
+ result += (j < (allowEvents[i].length - 1)) ? ", " : "";
+ }
+
+ result += (i < (allowEvents.length - 1)) ? ",\n " : "";
+ }
+
+ result += "]";
+
+ for (int i = 0; i < testThreads.length; i++)
+ {
+ result += "thread[" + i + "] = " + testThreads[i].toString();
+ }
+
+ return result;
+ }
+
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java
new file mode 100644
index 0000000000..ef177a4255
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java
@@ -0,0 +1,145 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.concurrency;
+
+import org.apache.log4j.Logger;
+
+/**
+ * An example to illustrate the use of the {@link ThreadTestCoordinator} and {@link TestRunnable}s.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Demo multi-threaded testing.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ThreadTestExample
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(ThreadTestExample.class);
+
+ /** Test thread 1. */
+ TestRunnable testThread1 =
+ new TestRunnable()
+ {
+ public void runWithExceptions() throws Exception
+ {
+ log.debug("public void run(): called");
+ log.info("in testThread0, block 1");
+
+ // Wait for t2 to allow t1 to continue.
+ allow(new int[] { 1 });
+ waitFor(new int[] { 1 }, false);
+
+ log.info("in testThread0, block 2");
+
+ // Wait for t2 to allow t1 to continue. T2 is allowed to be blocked elsewhere than giving explicit
+ // permission to allow t1 to continue.
+ allow(new int[] { 1 });
+ waitFor(new int[] { 1 }, true);
+
+ log.info("in testThread0, block 3");
+
+ // Release thread 2 from waiting on the shared lock.
+ synchronized (sharedLock)
+ {
+ sharedLock.notifyAll();
+ }
+
+ allow(new int[] { 1 });
+ }
+ };
+
+ /** A shared lock between the test threads. */
+ final Object sharedLock = new Object();
+
+ /** Test thread 2. */
+ TestRunnable testThread2 =
+ new TestRunnable()
+ {
+ public void runWithExceptions() throws Exception
+ {
+ log.debug("public void run(): called");
+ log.info("in testThread1, block 1");
+
+ // Wait for t1 to allow t2 to continue.
+ allow(new int[] { 0 });
+ waitFor(new int[] { 0 }, false);
+
+ log.info("in testThread1, block 2");
+
+ // Wait on another resource. T1 should accept this as permission to continue.
+ try
+ {
+ synchronized (sharedLock)
+ {
+ log.debug("in testThread1, waiting on shared lock.");
+ sharedLock.wait();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // Bail-out with a runtime if this happens.
+ throw new RuntimeException("Interrupted whilst waiting for shared lock.", e);
+ }
+
+ log.info("in testThread1, finished waiting on shared lock.");
+
+ // allow(new int[] { 0 });
+
+ // Wait for t1 to allow t2 to continue.
+ waitFor(new int[] { 0 }, false);
+
+ log.info("in testThread1, block 3");
+
+ allow(new int[] { 0 });
+ }
+ };
+
+ /**
+ * Executes the test threads with coordination.
+ *
+ * @param args Ignored.
+ */
+ public void main(String[] args)
+ {
+ ThreadTestCoordinator tt = new ThreadTestCoordinator(2);
+
+ tt.addTestThread(testThread1, 0);
+ tt.addTestThread(testThread2, 1);
+ tt.setDeadlockTimeout(500);
+ tt.run();
+
+ String errorMessage = tt.joinAndRetrieveMessages();
+
+ // Print any error messages or exceptions.
+ log.info(errorMessage);
+
+ if (!tt.getExceptions().isEmpty())
+ {
+ for (Exception e : tt.getExceptions())
+ {
+ log.warn("Exception thrown during test thread: ", e);
+ }
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/package.html b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/package.html
new file mode 100644
index 0000000000..4264367690
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/package.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+Contains code to assist in testing concurrency issues using coordinated threads to present code under test with
+oportunities to expose concurrency bugs. Some example concurrency bugs that may be tested using these techniques are
+race conditions, dead locks, live locks, dirty reads, phantom reads, non repeatable reads and so on.
+</body>
+</html> \ No newline at end of file
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java
new file mode 100644
index 0000000000..03e465695e
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.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.junit.extensions;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour.
+ *
+ * <p>ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal
+ * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is
+ * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure
+ * for different numbers of data elements held in the data structure the int parameter should be interpreted as the
+ * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour
+ * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class
+ * will also be run.
+ *
+ * <p>TestCases derived from this class may also define tear down methods to clean up their memory usage. This is
+ * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is
+ * to write a test that allocates memory in the main test method in such a way that it leaves that memory still
+ * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method
+ * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the
+ * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of
+ * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Store the current int parameter value. <td> {@link TKTestResult} and see {@link AsymptoticTestDecorator} too.
+ * <tr><td> Invoke parameterized test methods.
+ * </table>
+ *
+ * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or
+ * {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this
+ * class to do time and space performance analysis, these methods could be added to any JUnit TestCase class
+ * instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to
+ * work with this extension, and also tests that extend other junit extension classes could have parameterized
+ * and tear down methods too.
+ *
+ * @author Rupert Smith
+ */
+public class AsymptoticTestCase extends TestCase implements InstrumentedTest
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(AsymptoticTestCase.class);
+
+ /** The name of the test case. */
+ private String testCaseName;
+
+ /** Thread local for holding measurements on a per thread basis. */
+ ThreadLocal<TestMeasurements> threadLocalMeasurement =
+ new ThreadLocal<TestMeasurements>()
+ {
+ /**
+ * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1).
+ *
+ * @return A set of default test measurements.
+ */
+ protected synchronized TestMeasurements initialValue()
+ {
+ return new TestMeasurements();
+ }
+ };
+
+ /**
+ * Constructs a test case with the given name.
+ *
+ * @param name The name of the test.
+ */
+ public AsymptoticTestCase(String name)
+ {
+ super(name);
+
+ log.debug("public AsymptoticTestCase(String " + name + "): called");
+ testCaseName = name;
+ }
+
+ /**
+ * Gets the current value of the integer parameter to be passed to the parameterized test.
+ *
+ * @return The current value of the integer parameter.
+ */
+ public int getN()
+ {
+ log.debug("public int getN(): called");
+ int n = threadLocalMeasurement.get().n;
+
+ log.debug("return: " + n);
+
+ return n;
+ }
+
+ /**
+ * Sets the current value of the integer parameter to be passed to the parameterized test.
+ *
+ * @param n The new current value of the integer parameter.
+ */
+ public void setN(int n)
+ {
+ log.debug("public void setN(int " + n + "): called");
+ threadLocalMeasurement.get().n = n;
+ }
+
+ /**
+ * Reports how long the test took to run.
+ *
+ * @return The time in milliseconds that the test took to run.
+ */
+ public long getTestTime()
+ {
+ log.debug("public long getTestTime(): called");
+ long startTime = threadLocalMeasurement.get().startTime;
+ long endTime = threadLocalMeasurement.get().endTime;
+ long testTime = endTime - startTime;
+
+ log.debug("return: " + testTime);
+
+ return testTime;
+ }
+
+ /**
+ * Reports the memory usage at the start of the test.
+ *
+ * @return The memory usage at the start of the test.
+ */
+ public long getTestStartMemory()
+ {
+ // log.debug("public long getTestStartMemory(): called");
+ long startMem = threadLocalMeasurement.get().startMem;
+
+ // log.debug("return: " + startMem);
+
+ return startMem;
+ }
+
+ /**
+ * Reports the memory usage at the end of the test.
+ *
+ * @return The memory usage at the end of the test.
+ */
+ public long getTestEndMemory()
+ {
+ // log.debug("public long getTestEndMemory(): called");
+ long endMem = threadLocalMeasurement.get().endMem;
+
+ // log.debug("return: " + endMem);
+ return endMem;
+ }
+
+ /**
+ * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory
+ * can be reclaimed.
+ */
+ public void reset()
+ {
+ log.debug("public void reset(): called");
+ threadLocalMeasurement.remove();
+ }
+
+ /**
+ * Runs the test method for this test case.
+ *
+ * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through.
+ */
+ protected void runTest() throws Throwable
+ {
+ log.debug("protected void runTest(): called");
+
+ // Check that a test name has been set. This is used to define which method to run.
+ assertNotNull(testCaseName);
+ log.debug("testCaseName = " + testCaseName);
+
+ // Try to get the method with matching name.
+ Method runMethod = null;
+ boolean isParameterized = false;
+
+ // Check if a parameterized test method is available.
+ try
+ {
+ // Use getMethod to get all public inherited methods. getDeclaredMethods returns all
+ // methods of this class but excludes the inherited ones.
+ runMethod = getClass().getMethod(testCaseName, int.class);
+ isParameterized = true;
+ }
+ catch (NoSuchMethodException e)
+ {
+ // log.debug("Parameterized method \"" + testCaseName + "\" not found.");
+ // Set run method to null (it already will be but...) to indicate that no parameterized method
+ // version could be found.
+ runMethod = null;
+ }
+
+ // If no parameterized method is available, try and get the unparameterized method.
+ if (runMethod == null)
+ {
+ try
+ {
+ runMethod = getClass().getMethod(testCaseName);
+ isParameterized = false;
+
+ }
+ catch (NoSuchMethodException e)
+ {
+ fail("Method \"" + testCaseName + "\" not found.");
+ }
+ }
+
+ // Check that the method is publicly accessable.
+ if (!Modifier.isPublic(runMethod.getModifiers()))
+ {
+ fail("Method \"" + testCaseName + "\" should be public.");
+ }
+
+ // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or
+ // resulting exceptions from the method to fall through.
+ try
+ {
+ Integer paramN = getN();
+ log.debug("paramN = " + paramN);
+
+ // Calculate parameters for parameterized tests so new does not get called during memory measurement.
+ Object[] params = new Object[] { paramN };
+
+ // Take the test start memory and start time.
+ threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory();
+
+ threadLocalMeasurement.get().startTime = System.nanoTime();
+
+ if (isParameterized)
+ {
+ runMethod.invoke(this, params);
+ }
+ else
+ {
+ runMethod.invoke(this);
+ }
+ }
+ catch (InvocationTargetException e)
+ {
+ e.fillInStackTrace();
+ throw e.getTargetException();
+ }
+ catch (IllegalAccessException e)
+ {
+ e.fillInStackTrace();
+ throw e;
+ }
+ finally
+ {
+ // Take the test end memory and end time and calculate how long it took to run.
+ long endTime = System.nanoTime();
+ threadLocalMeasurement.get().endTime = endTime;
+ log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = "
+ + getTestTime());
+
+ threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory();
+ }
+ }
+
+ /**
+ * The test parameters, encapsulated as a unit for attaching on a per thread basis.
+ */
+ private static class TestMeasurements
+ {
+ /** Holds the current value of the integer parameter to run tests on. */
+ public int n = 1;
+
+ /** Holds the test start memory. */
+ public long startTime = 0;
+
+ /** Holds the test end memory. */
+ public long endTime = 0;
+
+ /** Holds the test start memory. */
+ public long startMem = 0;
+
+ /** Holds the test end memory. */
+ public long endMem = 0;
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java
new file mode 100644
index 0000000000..e99f904331
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java
@@ -0,0 +1,170 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.TestResult;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.util.MathUtils;
+
+/**
+ * A Decorator that runs a test repeatedly on an increasing int parameter, or for a fixed number of repeats. If both
+ * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each
+ * integer parameter.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Repeat a test for each of a set of integer parameters. <td> {@link TKTestResult}
+ * <tr><td> Repeat a test multiple times.
+ * <tr><td>
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class AsymptoticTestDecorator extends WrappedSuiteTestDecorator
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(AsymptoticTestDecorator.class);
+
+ /** The int size parameters to run the test with. */
+ private int[] params;
+
+ /** The number of times the whole test should be repeated. */
+ private int repeat;
+
+ /**
+ * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters
+ * to call the test with.
+ *
+ * @param test The test to wrap.
+ * @param params The integer 'size' parameters.
+ * @param repeat The number of times to repeat the test.
+ */
+ public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat)
+ {
+ super(test);
+
+ log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] "
+ + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called");
+
+ this.params = params;
+ this.repeat = repeat;
+ }
+
+ /**
+ * Creates a new AsymptoticTestDecorator object.
+ *
+ * @param test The test to decorate.
+ * @param start The starting asymptotic integer parameter value.
+ * @param end The ending asymptotic integer parameter value.
+ * @param step The increment size to move from the start to end values by.
+ * @param repeat The number of times to repeat the test at each step of the cycle.
+ */
+ public AsymptoticTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat)
+ {
+ super(test);
+
+ if (start < 0)
+ {
+ throw new IllegalArgumentException("Start must be >= 0");
+ }
+
+ if (end < start)
+ {
+ throw new IllegalArgumentException("End must be >= start");
+ }
+
+ if (step < 1)
+ {
+ throw new IllegalArgumentException("Step must be >= 1");
+ }
+
+ if (repeat < 1)
+ {
+ throw new IllegalArgumentException("Repeat must be >= 1");
+ }
+
+ // Generate the sequence.
+ params = new int[((end - start) / step) + 1];
+ int i = 0;
+ for (int n = start; n <= end; n += step)
+ {
+ params[i++] = n;
+ }
+
+ this.repeat = repeat;
+ }
+
+ /**
+ * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test
+ * repeats.
+ *
+ * @param result The test result object that the tests will indicate their results to. This is also used
+ * to pass the int parameter from this class to the decorated test class.
+ */
+ public void run(TestResult result)
+ {
+ log.debug("public void run(TestResult result): called");
+
+ if (!(result instanceof TKTestResult))
+ {
+ throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult");
+ }
+
+ // Cast the test result into a TKTestResult to place the current parameter into.
+ TKTestResult tkResult = (TKTestResult) result;
+
+ log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params)));
+ log.debug("repeat = " + repeat);
+
+ for (int n : params)
+ {
+ for (int j = 0; j < repeat; j++)
+ {
+ log.debug("n = " + n);
+
+ // Set the integer parameter in the TKTestResult to be passed to the tests.
+ tkResult.setN(n);
+
+ if (tkResult.shouldStop())
+ {
+ log.debug("tkResult.shouldStop = " + true);
+
+ break;
+ }
+
+ log.debug("Calling super#run");
+ super.run(tkResult);
+ }
+ }
+ }
+
+ /**
+ * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes.
+ *
+ * @return The name of this test with the string "(parameterized)" appended onto it.
+ */
+ public String toString()
+ {
+ return super.toString() + "(parameterized)";
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java
new file mode 100644
index 0000000000..e8e203f0a3
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java
@@ -0,0 +1,98 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * Provides a base implementation of the non-waiting throttle checking method, using the system nano timer.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check against a throttle speed without waiting.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public abstract class BaseThrottle implements Throttle
+{
+ /** Holds the length of a single cycle in nano seconds. */
+ protected long cycleTimeNanos;
+
+ /** Holds the time of the last succesfull call to the check method. */
+ private long lastCheckTimeNanos;
+
+ /** Flag used to detect the first call to the {@link #checkThrottle()} method. */
+ boolean firstCheckCall = true;
+
+ /**
+ * Flag used to detect the first call to the {@link #throttle()} method. Zero or negative start time cannot be
+ * relied on to detect this as System.nanoTime can return zero or negative values.
+ */
+ boolean firstCall = true;
+
+ /**
+ * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse
+ * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer.
+ * The value must also be larger than zero.
+ *
+ * @param hertz The throttling rate in cycles per second.
+ */
+ public void setRate(float hertz)
+ {
+ // Check that the argument is above zero.
+ if (hertz <= 0.0f)
+ {
+ throw new IllegalArgumentException("The throttle rate must be above zero.");
+ }
+
+ // Calculate the cycle time.
+ cycleTimeNanos = (long) (1000000000f / hertz);
+
+ // Reset the first pass flag.
+ firstCall = false;
+ firstCheckCall = false;
+ }
+
+ /**
+ * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater
+ * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>true</tt>
+ *
+ * @return <tt>true</tt> if a length of time greater than that equal to the inverse of the throttling rate has
+ * passed since this method was last called and returned <tt>true</tt>, <tt>false</tt> otherwise. The very
+ * first time this method is called on a throttle, it returns <tt>true</tt> as the base case to the above
+ * self-referential definition.
+ */
+ public boolean checkThrottle()
+ {
+ long now = System.nanoTime();
+
+ if ((now > (cycleTimeNanos + lastCheckTimeNanos)) || firstCheckCall)
+ {
+ firstCheckCall = false;
+ lastCheckTimeNanos = now;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java
new file mode 100644
index 0000000000..1d00fcf3b6
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java
@@ -0,0 +1,94 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * BatchedThrottle is a {@link SleepThrottle} that uses batching to achieve much higher throttling rates than a sleep
+ * throttle can. Sleep throttle has difficulties once the rate gets above a few hundred hertz, because the JVM cannot
+ * generate timed pauses that are that short. BatchedThrottle gets around this by only inserting pauses once every so
+ * many calls to the {@link #throttle()} method, and using a sleep throttle run at a lower rate. The rate for the sleep
+ * throttle is chosen so that it remains under 100hz. The final throttling rate of this throttle is equal to the batch
+ * size times the rate of the underlying sleep throttle.
+ *
+ * <p/>The batching calculation involves taking the log to the base 100 of the desired rate and rounding this to
+ * an integer. The batch size is always an exact power of 100 because of the rounding. The rate for an underlying
+ * sleep throttle is then chosen appropriately.
+ *
+ * <p/>In practice, the accuracy of a BacthedThrottle skews off but can sometimes even be reasonable up to ten thousand
+ * hertz compared with 100 Hz for a {@link SleepThrottle}.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept throttling rate in operations per second.
+ * <tr><td> Inject short pauses, occasionaly, to fill out processing cycles to a specified rate.
+ * <tr><td> Check against a throttle speed without waiting.
+ * </table>
+ *
+ * @todo Should always round the log base 100 down to the nearest integer?
+ *
+ * @author Rupert Smith
+ */
+public class BatchedThrottle extends BaseThrottle
+{
+ /** Holds the batch size. */
+ int batchSize;
+
+ /** The call count within the current batch. */
+ long callCount;
+
+ /** Holds a sleep throttle configured to run at the batched rate. */
+ private Throttle batchRateThrottle = new SleepThrottle();
+
+ /**
+ * Specifies the throttling rate in operations per second.
+ *
+ * @param hertz The throttling rate in cycles per second.
+ */
+ public void setRate(float hertz)
+ {
+ // Pass the rate unaltered down to the base implementation, for the check method.
+ super.setRate(hertz);
+
+ // Log base 10 over 2 is used here to get a feel for what power of 100 the total rate is.
+ // As the total rate goes up the powers of 100 the batch size goes up by powers of 100 to keep the
+ // throttle rate in the range 1 to 100.
+ int x = (int) (Math.log10(hertz) / 2);
+ batchSize = (int) Math.pow(100, x);
+ float throttleRate = hertz / batchSize;
+
+ // Reset the call count.
+ callCount = 0;
+
+ // Set the sleep throttle wrapped implementation at a rate within its abilities.
+ batchRateThrottle.setRate(throttleRate);
+ }
+
+ /**
+ * Throttle calls to this method to the rate specified by the {@link #setRate(float)} method.
+ */
+ public void throttle()
+ {
+ if ((callCount++ % batchSize) == 0)
+ {
+ batchRateThrottle.throttle();
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java
new file mode 100644
index 0000000000..fe1e044e67
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java
@@ -0,0 +1,199 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.log4j.Logger;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * A test decorator that runs a test repeatedly until a specified length of time has passed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Repeatedly run a test for a fixed length of time.
+ * </table>
+ *
+ * @todo The count of the number of tests run is an important number to keep. Also num passed/error/failed is also
+ * important to record. What to do with these numbers? They are already logged to the test listeners.
+ *
+ * @todo The duration test runner wraps on top of size, repeat or thread wrappers, need a way for it to tell
+ * TKTestResult when the duration is up, so that it can terminate any repeats in progress. It should end
+ * as soon as possible once the test method exits.
+ *
+ * @author Rupert Smith
+ */
+public class DurationTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(DurationTestDecorator.class);
+
+ /** The test to run. */
+ private Test test;
+
+ /** The length of time to run the test for. */
+ private long duration;
+
+ /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */
+ private boolean shutdown = false;
+
+ /**
+ * Creates an active test with default multiplier (1).
+ *
+ * @param test The target test.
+ */
+ public DurationTestDecorator(WrappedSuiteTestDecorator test)
+ {
+ super(test);
+ this.test = test;
+ }
+
+ /**
+ * Creates active test with default multiplier (1).
+ *
+ * @param test The target test.
+ * @param duration The duration in milliseconds.
+ */
+ public DurationTestDecorator(WrappedSuiteTestDecorator test, long duration)
+ {
+ super(test);
+
+ // log.debug("public DurationTestDecorator(Test \"" + test + "\", long " + duration + "): called");
+
+ this.test = test;
+ this.duration = duration;
+ }
+
+ /**
+ * Runs the test repeatedly for the fixed duration.
+ *
+ * @param testResult The the results object to monitor the test results with.
+ */
+ public void run(TestResult testResult)
+ {
+ log.debug("public void run(TestResult testResult): called");
+
+ // Cast the test result to expose it as a TKTestResult if the test is running under the TKTestRunner.
+ TKTestResult tkTestResult = null;
+
+ if (testResult instanceof TKTestResult)
+ {
+ tkTestResult = (TKTestResult) testResult;
+ }
+
+ // Work out when the test should end.
+ long now = System.nanoTime();
+ long end = (duration * 1000000) + now;
+
+ // If running under the TKTestRunner, set up a timer to notify the test framework when the test reaches its
+ // completion time.
+ Timer durationTimer = null;
+
+ if (tkTestResult != null)
+ {
+ log.debug("Creating duration timer.");
+
+ durationTimer = new Timer();
+ durationTimer.schedule(new DurationTimerTask((TKTestResult) testResult), duration);
+ }
+
+ // Run the test until the duration times out or the shutdown flag is set. The test method may not exit until
+ // interrupted in some cases, in which case the timer will do the interrupting.
+ while ((now < end) && !shutdown)
+ {
+ test.run(testResult);
+
+ now = System.nanoTime();
+ }
+
+ // Clean up any timer that was used.
+ if (durationTimer != null)
+ {
+ log.debug("Cancelling duration timer.");
+
+ durationTimer.cancel();
+ }
+ }
+
+ /**
+ * Supplies the shutdown hook. This shutdown hook does not call {@link TKTestResult#shutdownNow()} because the
+ * {@link ScaledTestDecorator} already takes care of that.
+ *
+ * @return The shut down hook.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ // log.debug("DurationTestDecorator::ShutdownHook: called");
+
+ // Set the shutdown flag so that no new tests are started.
+ shutdown = true;
+ }
+ });
+ }
+
+ /**
+ * DurationTimerTask is a timer task that is configured, upon expiry of its timer, to invoke
+ * {@link TKTestResult#shutdownNow()}, for the test result object on which it is set. It also sets
+ * the {@link DurationTestDecorator#shutdown} flag to indicate that no new tests should be run.
+ *
+ * <p/>The test loop implemented by DurationTestDecorator checks that the duration has not expired, on each
+ * test case that it runs. However, it is possible to write test cases that never return until explicitly
+ * interrupted by the test framework. This timer task exists to notify the test framework
+ */
+ private class DurationTimerTask extends TimerTask
+ {
+ /** Used for debugging purposes. */
+ private final Logger log = Logger.getLogger(DurationTimerTask.class);
+
+ /** Holds the test result for the test to which a duration limit is being applied. */
+ TKTestResult testResult;
+
+ /**
+ * Creates a duration limit timer which will notify the specified test result when the duration has
+ * expired.
+ *
+ * @param testResult The test result to notify upon expiry of the test duration.
+ */
+ public DurationTimerTask(TKTestResult testResult)
+ {
+ this.testResult = testResult;
+ }
+
+ /**
+ * The action to be performed by this timer task.
+ */
+ public void run()
+ {
+ log.debug("public void run(): called");
+
+ shutdown = true;
+ testResult.shutdownNow();
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java
new file mode 100644
index 0000000000..ed792fcd5a
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java
@@ -0,0 +1,66 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.Test;
+
+/**
+ * An InstrumentedTest is one which can supply some additional instrumentation on top of the pass/fail/error behaviour
+ * of normal junit tests. Tests implementing this interface must additionally supply information about how long they
+ * took to run and how much memory they used.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Report test run time.
+ * <tr><td> Report test memory usage.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface InstrumentedTest extends Test
+{
+ /**
+ * Reports how long the test took to run.
+ *
+ * @return The time in milliseconds that the test took to run.
+ */
+ public long getTestTime();
+
+ /**
+ * Reports the memory usage at the start of the test.
+ *
+ * @return The memory usage at the start of the test.
+ */
+ public long getTestStartMemory();
+
+ /**
+ * Reports the memory usage at the end of the test.
+ *
+ * @return The memory usage at the end of the test.
+ */
+ public long getTestEndMemory();
+
+ /**
+ * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory
+ * can be reclaimed.
+ */
+ public void reset();
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java
new file mode 100644
index 0000000000..2ffbcb5bb8
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import junit.textui.ResultPrinter;
+
+import java.io.PrintStream;
+
+/**
+ * A ResultPrinter that prints nothing. This exists, in order to provide a replacement to JUnit's ResultPrinter, which
+ * is refered to directly by JUnit code, rather that as an abstracted TestListener. JUnit's text ui TestRunner must
+ * have a ResultPrinter. This provides an implementation of it that prints nothing, so that a better mechanism can
+ * be used for providing feedback to the console instead.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ *
+ * @todo See todo in TKTestRunner about completely replacing the test ui runner. Doing things like this in order to
+ * extend JUnit is not nice, and there needs to be a better way to do it. Delete this class and use a listener
+ * instead.
+ *
+ * @author Rupert Smith
+ */
+public class NullResultPrinter extends ResultPrinter
+{
+ /**
+ * Builds a fake ResultPrinter that prints nothing.
+ *
+ * @param writer The writer to send output to.
+ */
+ public NullResultPrinter(PrintStream writer)
+ {
+ super(writer);
+ }
+
+ /**
+ * Does nothing.
+ *
+ * @param test Ignored.
+ * @param t Ignored.
+ */
+ public void addError(Test test, Throwable t)
+ { }
+
+ /**
+ * Does nothing.
+ *
+ * @param test Ignored.
+ * @param t Ignored.
+ */
+ public void addFailure(Test test, AssertionFailedError t)
+ { }
+
+ /**
+ * Does nothing.
+ *
+ * @param test Ignored.
+ */
+ public void endTest(Test test)
+ { }
+
+ /**
+ * Does nothing.
+ *
+ * @param test Ignored.
+ */
+ public void startTest(Test test)
+ { }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java
new file mode 100644
index 0000000000..60ec156354
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.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.junit.extensions;
+
+import junit.framework.TestResult;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.util.MathUtils;
+
+/**
+ * ParameterVariationTestDecorator is a test decorator that runs a test repeatedly under all permutations of its
+ * test parameters.
+ *
+ * a set of integer parameters and a repeat count are specified, then each test is run for the repeat count at each
+ * integer parameter.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Repeat a test for each of a set of integer parameters. <td> {@link org.apache.qpid.junit.extensions.TKTestResult}
+ * <tr><td> Repeat a test multiple times.
+ * <tr><td>
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ParameterVariationTestDecorator extends WrappedSuiteTestDecorator
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(ParameterVariationTestDecorator.class);
+
+ /** The int size parameters to run the test with. */
+ private int[] params;
+
+ /** The number of times the whole test should be repeated. */
+ private int repeat;
+
+ /**
+ * Creates an asymptotic test decorator that wraps a test with repeats and a set of integer 'size' paramters
+ * to call the test with.
+ *
+ * @param test The test to wrap.
+ * @param params The integer 'size' parameters.
+ * @param repeat The number of times to repeat the test.
+ */
+ public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int[] params, int repeat)
+ {
+ super(test);
+
+ log.debug("public AsymptoticTestDecorator(Test \"" + test + "\", int[] "
+ + ((params == null) ? null : MathUtils.printArray(params)) + ", int " + repeat + "): called");
+
+ this.params = params;
+ this.repeat = repeat;
+ }
+
+ /**
+ * Creates a new AsymptoticTestDecorator object.
+ *
+ * @param test The test to decorate.
+ * @param start The starting asymptotic integer parameter value.
+ * @param end The ending asymptotic integer parameter value.
+ * @param step The increment size to move from the start to end values by.
+ * @param repeat The number of times to repeat the test at each step of the cycle.
+ */
+ public ParameterVariationTestDecorator(WrappedSuiteTestDecorator test, int start, int end, int step, int repeat)
+ {
+ super(test);
+
+ if (start < 0)
+ {
+ throw new IllegalArgumentException("Start must be >= 0");
+ }
+
+ if (end < start)
+ {
+ throw new IllegalArgumentException("End must be >= start");
+ }
+
+ if (step < 1)
+ {
+ throw new IllegalArgumentException("Step must be >= 1");
+ }
+
+ if (repeat < 1)
+ {
+ throw new IllegalArgumentException("Repeat must be >= 1");
+ }
+
+ // Generate the sequence.
+ params = new int[((end - start) / step) + 1];
+ int i = 0;
+ for (int n = start; n <= end; n += step)
+ {
+ params[i++] = n;
+ }
+
+ this.repeat = repeat;
+ }
+
+ /**
+ * Runs the test repeatedly for each value of the int parameter specified and for the correct number of test
+ * repeats.
+ *
+ * @param result The test result object that the tests will indicate their results to. This is also used
+ * to pass the int parameter from this class to the decorated test class.
+ */
+ public void run(TestResult result)
+ {
+ log.debug("public void run(TestResult result): called");
+
+ if (!(result instanceof TKTestResult))
+ {
+ throw new IllegalArgumentException("AsymptoticTestDecorator only works with TKTestResult");
+ }
+
+ // Cast the test result into a TKTestResult to place the current parameter into.
+ TKTestResult tkResult = (TKTestResult) result;
+
+ log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params)));
+ log.debug("repeat = " + repeat);
+
+ for (int n : params)
+ {
+ for (int j = 0; j < repeat; j++)
+ {
+ log.debug("n = " + n);
+
+ // Set the integer parameter in the TKTestResult to be passed to the tests.
+ tkResult.setN(n);
+
+ if (tkResult.shouldStop())
+ {
+ log.debug("tkResult.shouldStop = " + true);
+
+ break;
+ }
+
+ log.debug("Calling super#run");
+ super.run(tkResult);
+ }
+ }
+ }
+
+ /**
+ * Prints out the name of this test with the string "(parameterized)" appended onto it for debugging purposes.
+ *
+ * @return The name of this test with the string "(parameterized)" appended onto it.
+ */
+ public String toString()
+ {
+ return super.toString() + "(parameterized)";
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java
new file mode 100644
index 0000000000..f5e3e1a758
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java
@@ -0,0 +1,375 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+/**
+ * A test decorator that runs a test many times simultaneously in many threads.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Clone a test run into many threads and run them simultaneously.
+ * <tr><td> Inform the test results of the start and end of each concurrent test batch. <td> {@link TKTestResult}
+ * <tr><td> Inform the test results of the concurrency level. <td> {@link TKTestResult}
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ScaledTestDecorator extends WrappedSuiteTestDecorator implements ShutdownHookable // TestDecorator
+{
+ /** Used for logging. */
+ // private static final Logger log = Logger.getLogger(ScaledTestDecorator.class);
+
+ /** Determines how long to wait for tests to cleanly exit on shutdown. */
+ private static final long SHUTDOWN_PAUSE = 3000;
+
+ /**
+ * The stress levels or numbers of simultaneous threads to run the test in. The test is repeated at each of
+ * the concurrency levels specified here. Defaults to 1 thread.
+ */
+ private int[] threads = new int[] { 1 };
+
+ /** Used to hold the number of tests currently being run in parallel. */
+ private int concurrencyLevel;
+
+ /** The test to run. */
+ private WrappedSuiteTestDecorator test;
+
+ /**
+ * Used to hold the current {@link TKTestResult} for the tests currently being run. This is made available so that
+ * the shutdown hook can ask it to cleanly end the current tests in the event of a shutdown.
+ */
+ private TKTestResult currentTestResult;
+
+ /** Flag set by the shutdown hook. This decorator will not start any new tests when this is set. */
+ private boolean shutdown = false;
+
+ /**
+ * Creates an active test with default multiplier (1).
+ *
+ * @param test The target test.
+ */
+ public ScaledTestDecorator(WrappedSuiteTestDecorator test)
+ {
+ super(test);
+ this.test = test;
+ }
+
+ /**
+ * Creates a concurrently scaled test with the specified number of threads.
+ *
+ * @param test The target test.
+ * @param numThreads The stress level.
+ */
+ public ScaledTestDecorator(WrappedSuiteTestDecorator test, int numThreads)
+ {
+ this(test, new int[] { numThreads });
+ }
+
+ /**
+ * Creates a concurrently scaled test with the specified thread levels, the test is repeated at each level.
+ *
+ * @param test The target test.
+ * @param threads The concurrency levels.
+ */
+ public ScaledTestDecorator(WrappedSuiteTestDecorator test, int[] threads)
+ {
+ super(test);
+
+ /*log.debug("public ScaledTestDecorator(WrappedSuiteTestDecorator test = \"" + test + "\", int[] threads = "
+ + MathUtils.printArray(threads) + "): called");*/
+
+ this.test = test;
+ this.threads = threads;
+ }
+
+ /**
+ * Runs the test simultaneously in at the specified concurrency levels.
+ *
+ * @param testResult The results object to monitor the test results with.
+ */
+ public void run(TestResult testResult)
+ {
+ // log.debug("public void run(TestResult testResult = " + testResult + "): called");
+
+ // Loop through all of the specified concurrent levels for the test, provided shutdown has not been called.
+ for (int i = 0; (i < threads.length) && !shutdown; i++)
+ {
+ // Get the number of threads for this run.
+ int numThreads = threads[i];
+
+ // Create test thread handlers for all the threads.
+ TestThreadHandler[] threadHandlers = new TestThreadHandler[numThreads];
+
+ // Create a cyclic barrier for the test threads to synch their setups and teardowns on.
+ CyclicBarrier barrier = new CyclicBarrier(numThreads);
+
+ // Set up the test thread handlers to output results to the same test results object.
+ for (int j = 0; j < numThreads; j++)
+ {
+ threadHandlers[j] = new TestThreadHandler(testResult, test, barrier);
+ }
+
+ // Ensure the concurrency level statistic is set up correctly.
+ concurrencyLevel = numThreads;
+
+ // Begin batch.
+ if (testResult instanceof TKTestResult)
+ {
+ TKTestResult tkResult = (TKTestResult) testResult;
+ // tkResult.notifyStartBatch();
+ tkResult.setConcurrencyLevel(numThreads);
+
+ // Set the test result for the currently running tests, so that the shutdown hook can call it if necessary.
+ currentTestResult = tkResult;
+ }
+
+ // Run all the tests and wait for them all to finish.
+ executeAndWaitForRunnables(threadHandlers);
+
+ // Clear the test result for the currently running tests.
+ currentTestResult = null;
+
+ // End batch.
+ if (testResult instanceof TKTestResult)
+ {
+ TKTestResult tkResult = (TKTestResult) testResult;
+ tkResult.notifyEndBatch();
+ }
+
+ // Clear up all the test threads, they hold references to their associated TestResult object and Test object,
+ // which may prevent them from being garbage collected as the TestResult and Test objects are long lived.
+ for (int j = 0; j < numThreads; j++)
+ {
+ threadHandlers[j].testResult = null;
+ threadHandlers[j].test = null;
+ threadHandlers[j] = null;
+ }
+ }
+ }
+
+ /**
+ * Reports the number of tests that the scaled decorator is currently running concurrently.
+ *
+ * @return The number of tests that the scaled decorator is currently running concurrently.
+ */
+ public int getConcurrencyLevel()
+ {
+ return concurrencyLevel;
+ }
+
+ /**
+ * Executes all of the specifed runnable using the thread pool and waits for them all to complete.
+ *
+ * @param runnables The set of runnables to execute concurrently.
+ */
+ private void executeAndWaitForRunnables(Runnable[] runnables)
+ {
+ int numThreads = runnables.length;
+
+ // Used to keep track of the test threads in order to know when they have all completed.
+ Thread[] threads = new Thread[numThreads];
+
+ // Create all the test threads.
+ for (int j = 0; j < numThreads; j++)
+ {
+ threads[j] = new Thread(runnables[j]);
+ }
+
+ // Start all the test threads.
+ for (int j = 0; j < numThreads; j++)
+ {
+ threads[j].start();
+ }
+
+ // Wait for all the test threads to complete.
+ for (int j = 0; j < numThreads; j++)
+ {
+ try
+ {
+ threads[j].join();
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the interrupted state of the thread.
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * Supplies the shut-down hook.
+ *
+ * @return The shut-down hook.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ // log.debug("ScaledTestDecorator::ShutdownHook: called");
+
+ // Set the shutdown flag so that no new tests are started.
+ shutdown = true;
+
+ // Check if tests are currently running, and ask them to complete as soon as possible. Allow
+ // a short pause for this to happen.
+ TKTestResult testResult = currentTestResult;
+
+ if (testResult != null)
+ {
+ // log.debug("There is a test result currently running tests, asking it to terminate ASAP.");
+ testResult.shutdownNow();
+
+ try
+ {
+ Thread.sleep(SHUTDOWN_PAUSE);
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the interrupted state of the thread.
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Prints a string summarizing this test decorator, mainly for debugging purposes.
+ *
+ * @return String representation for debugging purposes.
+ */
+ public String toString()
+ {
+ return "ScaledTestDecorator: [ test = " + test + ", concurrencyLevel = " + concurrencyLevel + " ]";
+ }
+
+ /**
+ * TestThreadHandler is a runnable used to execute a test in. This is static to avoid implicit 'this' reference to
+ * the longer lived ScaledTestDecorator class. The scaled test decorator may execute many repeats but creates fresh
+ * handlers for each one. It re-uses the threads in a pool but does not re-use these handlers.
+ */
+ private static class TestThreadHandler implements Runnable
+ {
+ /** The test result object for the test to be run with. */
+ TestResult testResult;
+
+ /** The test to run. */
+ WrappedSuiteTestDecorator test;
+
+ /** Holds the cyclic barrier to synchronize on the end of the setups and before the tear downs. */
+ CyclicBarrier barrier;
+
+ /**
+ * Creates a new TestThreadHandler object.
+ *
+ * @param testResult The test result object for the test to be run with.
+ * @param test The test to run in a sperate thread.
+ * @param barrier The barrier implementation to use to synchronize per-thread setup completion and test
+ * completion before moving on through the setup, test, teardown phases. The barrier should
+ * be configured for the number of test threads.
+ */
+ TestThreadHandler(TestResult testResult, WrappedSuiteTestDecorator test, CyclicBarrier barrier)
+ {
+ this.testResult = testResult;
+ this.test = test;
+ this.barrier = barrier;
+ }
+
+ /**
+ * Runs the test associated with this pool.
+ */
+ public void run()
+ {
+ try
+ {
+ // Call setup on all underlying tests in the suite that are thread aware.
+ for (Test childTest : test.getAllUnderlyingTests())
+ {
+ // Check that the test is concurrency aware, so provides a setup method to call.
+ if (childTest instanceof TestThreadAware)
+ {
+ // Call the tests per thread setup.
+ TestThreadAware setupTest = (TestThreadAware) childTest;
+ setupTest.threadSetUp();
+ }
+ }
+
+ // Wait until all test threads have completed their setups.
+ barrier.await();
+
+ // Start timing the test batch, only after thread setups have completed.
+ if (testResult instanceof TKTestResult)
+ {
+ ((TKTestResult) testResult).notifyStartBatch();
+ }
+
+ // Run the tests.
+ test.run(testResult);
+
+ // Wait unitl all test threads have completed their tests.
+ barrier.await();
+
+ // Call tear down on all underlying tests in the suite that are thread aware.
+ for (Test childTest : test.getAllUnderlyingTests())
+ {
+ // Check that the test is concurrency aware, so provides a teardown method to call.
+ if (childTest instanceof TestThreadAware)
+ {
+ // Call the tests per thread tear down.
+ TestThreadAware setupTest = (TestThreadAware) childTest;
+ setupTest.threadTearDown();
+ }
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the interrupted state of the thread.
+ Thread.currentThread().interrupt();
+ }
+ catch (BrokenBarrierException e)
+ {
+ // Set the interrupted state on the thread. The BrokenBarrierException may be caused where one thread
+ // waiting for the barrier is interrupted, causing the remaining threads correctly waiting on the
+ // barrier to fail. This condition is expected during test interruptions, and the response to it is to
+ // interrupt all the other threads running in the same scaled test.
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * Prints the name of the test for debugging purposes.
+ *
+ * @return The name of the test.
+ */
+ public String toString()
+ {
+ return "ScaledTestDecorator: [test = \"" + test + "\"]";
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java
new file mode 100644
index 0000000000..0e8e1879b6
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java
@@ -0,0 +1,55 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * SetupTaskAware is an interface that tests that can accept injectable setup tasks may implement. Typically this
+ * is used by configurable decorator stack to inject setup tasks into tests. It is then up to the test case to run
+ * the tasks in the setup or threadSetup methods as it chooses.
+ *
+ * <p/>Set up tasks should be chained so that they are executed in the order that they are applied. Tear down tasks
+ * should be chained so that they are executed in the reverse order to which they are applied. That way the set up and
+ * tear down tasks act as a 'task' stack, with nested setups and tear downs.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities.
+ * <tr><td> Handle injection of set up tasks.
+ * <tr><td> Handle injection of tear down tasks.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface SetupTaskAware
+{
+ /**
+ * Adds the specified task to the tests setup.
+ *
+ * @param task The task to add to the tests setup.
+ */
+ public void chainSetupTask(Runnable task);
+
+ /**
+ * Adds the specified task to the tests tear down.
+ *
+ * @param task The task to add to the tests tear down.
+ */
+ public void chainTearDownTask(Runnable task);
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java
new file mode 100644
index 0000000000..00736c59e5
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import org.apache.qpid.junit.extensions.util.StackQueue;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * SetupTaskHandler implements a task stack. It can be used, by delegation, as a base implementation for tests that want
+ * to have configurable setup/teardown task stacks. Typically it is up to the test implementation to decide whether the
+ * stack is executed in the setup/teardown methods or in the threadSetup/threadTeaddown methods.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Handle injection of set up tasks.
+ * <tr><td> Handle injection of tear down tasks.
+ * <tr><td> Run set up tasks in chain order.
+ * <tr><td> Run tear down tasks in reverse chain order.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class SetupTaskHandler implements SetupTaskAware
+{
+ /** Holds the set up tasks. */
+ Queue<Runnable> setups = new LinkedList<Runnable>();
+
+ /** Holds the tear down tasks. */
+ Queue<Runnable> teardowns = new StackQueue<Runnable>();
+
+ /**
+ * Adds the specified task to the tests setup.
+ *
+ * @param task The task to add to the tests setup.
+ */
+ public void chainSetupTask(Runnable task)
+ {
+ setups.offer(task);
+ }
+
+ /**
+ * Adds the specified task to the tests tear down.
+ *
+ * @param task The task to add to the tests tear down.
+ */
+ public void chainTearDownTask(Runnable task)
+ {
+ teardowns.offer(task);
+ }
+
+ /**
+ * Runs the set up tasks in the order that they way chained.
+ */
+ public void runSetupTasks()
+ {
+ while (!setups.isEmpty())
+ {
+ setups.remove().run();
+ }
+ }
+
+ /**
+ * Runs the tear down tasks in the reverse of the order in which they were chained.
+ */
+ public void runTearDownTasks()
+ {
+ while (!teardowns.isEmpty())
+ {
+ teardowns.remove().run();
+ }
+ }
+}
diff --git a/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java
index bd3757bf97..344d7abf82 100644
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java
@@ -1,48 +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.server.cluster;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQBody;
-import org.apache.qpid.framing.AMQFrame;
-
-/**
- */
-public class SimpleBodySendable implements Sendable
-{
- private final AMQBody _body;
-
- public SimpleBodySendable(AMQBody body)
- {
- _body = body;
- }
-
- public void send(int channel, Member member) throws AMQException
- {
- member.send(new AMQFrame(channel, _body));
- }
-
- public String toString()
- {
- return _body.toString();
- }
-
-}
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * Defines an interface that classes which supply shutdown hooks implement. Code that creates these classes can check
+ * if they supply a shutdown hook and register these hooks when the obejct are created.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Supply a shutdown hook.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface ShutdownHookable
+{
+ /**
+ * Supplies the shutdown hook.
+ *
+ * @return The shut down hook.
+ */
+ public Thread getShutdownHook();
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java
new file mode 100644
index 0000000000..f7e350b1c7
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java
@@ -0,0 +1,81 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * SleepThrottle is a Throttle implementation that generates short pauses using the thread sleep methods. As the pauses
+ * get shorter, this technique gets more innacurate. In practice, around 100 Hz is the cap rate for accuracy.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept throttling rate in operations per second.
+ * <tr><td> Inject short pauses to fill out processing cycles to a specified rate.
+ * <tr><td> Check against a throttle speed without waiting.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class SleepThrottle extends BaseThrottle implements Throttle
+{
+ /** Holds the time of the last call to the throttle method in nano seconds. */
+ private long lastTimeNanos;
+
+ /**
+ * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this
+ * it will inject short pauses to restrict the call rate to that rate.
+ */
+ public void throttle()
+ {
+ // Get the current time in nanos.
+ long currentTimeNanos = System.nanoTime();
+
+ // Don't introduce any pause on the first call.
+ if (!firstCall)
+ {
+ // Check if there is any time left in the cycle since the last call to this method and introduce a short pause
+ // to fill that time if there is.
+ long remainingTimeNanos = cycleTimeNanos - (currentTimeNanos - lastTimeNanos);
+
+ if (remainingTimeNanos > 0)
+ {
+ long milliPause = remainingTimeNanos / 1000000;
+ int nanoPause = (int) (remainingTimeNanos % 1000000);
+
+ try
+ {
+ Thread.sleep(milliPause, nanoPause);
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the interrupted thread, in-case the caller is checking for it.
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ else
+ {
+ firstCall = false;
+ }
+
+ // Update the last time stamp.
+ lastTimeNanos = System.nanoTime();
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java
new file mode 100644
index 0000000000..c9bcf3eb66
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java
@@ -0,0 +1,625 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.listeners.TKTestListener;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Properties;
+
+/**
+ * TKTestResult extends TestResult in order to calculate test timings, to pass the variable integer parameter for
+ * parameterized test cases to those test cases and to introduce an optional delay before test starts. Interested
+ * {@link TKTestListener}s may be attached to this and will be informed of all relevant test statistics.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Calculate test timings.
+ * <tr><td> Inform timing listeners of timings.
+ * <tr><td> Inform memory listeners of memory readings.
+ * <tr><td> Inform parameters listeners of parameters.
+ * <tr><td> Pass the integer parameter to parameterized test cases.
+ * <tr><td> Provide verbose test information on test start and end.
+ * </table>
+ *
+ * @todo Move the verbose test information on test start/end into a test listener instead. It confuses the intention
+ * of this class. Could also move the delay into a listener but that seems less appropriate as it would be a
+ * side-effecting listener. Delay and timing calculation are fundamental enough to this class.
+ *
+ * @todo The need for this class to act as a place-holder for the integer parameter for parameterized test cases is
+ * because this behaviour has been factored out into a test decorator class, see {@link AsymptoticTestDecorator}.
+ * The {@link AsymptoticTestDecorator#run} method takes a TestResult as an argument and cannot easily get to the
+ * {@link AsymptoticTestCase} class other than through this class. The option of using this class as a place hold
+ * for this value was chosen. Alternatively this class could provide a method for decorators to access the
+ * underlying test case through and then leave the setting of this parameter to the decorator which is a more
+ * natural home for this behaviour. It would also provide a more general framework for decorators.
+ *
+ * @todo The memory usage may need to be moved in closer to the test method invocation so that as little code as possible
+ * exists between it and the test or the results may be obscured. In fact it certainly does as the teardown method
+ * is getting called first. Wouldn't be a bad idea to move the timing code in closer too.
+ *
+ * @todo Get rid of the delay logic. Will be replaced by throttle control.
+ *
+ * @author Rupert Smith
+ */
+public class TKTestResult extends TestResult
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(TKTestResult.class);
+
+ /** The delay between two tests. */
+ private int delay = 0;
+
+ /**
+ * This flag indicates that the #completeTest method of the timing controller has been called. Once this has
+ * been called once, the end test event for the whole test method should be ignored because tests have taken
+ * charge of outputing their own timings.
+ */
+ private boolean completeTestUsed = false;
+
+ /**
+ * Thread locals to hold test start time for non-instrumented tests. (Instrumented tests hold their own
+ * measurement data).
+ */
+ // private Hashtable threadStartTimeMap = new Hashtable();
+ private ThreadLocal<ThreadLocalSettings> threadLocals = new ThreadLocal<ThreadLocalSettings>();
+
+ /** Used to hold the current integer parameter to pass to parameterized tests. This defaults to 1. */
+ private int n = 1;
+
+ /** The timing listeners. */
+ private Collection<TKTestListener> tkListeners;
+
+ /** The test case name. */
+ private String testCaseName;
+
+ /** Used to hold the current concurrency level, set by the {@link ScaledTestDecorator}. */
+ private int concurrencyLevel = 1;
+
+ /** Flag used to indicate that this test result should attempt to complete its current tests as soon as possible. */
+ private boolean shutdownNow = false;
+
+ /** Holds the parametes that the test is run with. */
+ private Properties testParameters;
+
+ /**
+ * Creates a new TKTestResult object.
+ *
+ * @param delay A delay in milliseconds to introduce before every test start.
+ * @param testCaseName The name of the test case that this is the TestResult object for.
+ */
+ public TKTestResult(int delay, String testCaseName)
+ {
+ super();
+
+ /*log.debug("public TKTestResult(PrintStream writer, int " + delay + ", boolean " + verbose + ", String "
+ + testCaseName + "): called");*/
+
+ // Keep all the parameters that this is created with.
+ this.delay = delay;
+ this.testCaseName = testCaseName;
+ }
+
+ /**
+ * Callback method use to inform this test result that a test will be started. Waits for the configured delay time
+ * if one has been set, starts the timer, then delegates to the super class implementation.
+ *
+ * @param test The test to be started.
+ */
+ public void startTest(Test test)
+ {
+ // log.debug("public void startTest(Test test): called");
+
+ // If a delay time has been specified then wait for that length of time.
+ if (this.delay > 0)
+ {
+ try
+ {
+ Thread.sleep(delay);
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore, but restore the interrupted flag.
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ // Create the thread local settings for the test.
+ ThreadLocalSettings threadLocalSettings = new ThreadLocalSettings();
+ threadLocals.set(threadLocalSettings);
+
+ // Record the test start time against this thread for calculating the test timing. (Consider using ThreadLocal
+ // instead?)
+ Long startTime = System.nanoTime();
+ threadLocalSettings.startTime = startTime;
+ // log.debug("startTime = " + startTime);
+
+ // Check if the test is timing controller aware, in which case set up a new timing controller and hold it
+ // in the thread local settings.
+ if (test instanceof TimingControllerAware)
+ {
+ TimingControllerAware controllerAware = (TimingControllerAware) test;
+ TimingControllerImpl controller =
+ new TimingControllerImpl(this, test, startTime, Thread.currentThread().getId());
+ controllerAware.setTimingController(controller);
+
+ threadLocalSettings.timingController = controller;
+ }
+
+ // Delegate to the super method to notify test event listeners.
+ super.startTest(test);
+ }
+
+ /**
+ * Callback method use to inform this result that a test was completed. This calculates how long the test took
+ * to run, then delegates to the super class implementation.
+ *
+ * @param test The test that has ended.
+ */
+ public void endTest(Test test)
+ {
+ // log.debug("public void endTest(Test test): called");
+
+ long runTime = 0;
+
+ // Recover the thread local settings.
+ ThreadLocalSettings threadLocalSettings = threadLocals.get();
+
+ // Check if the test is an instrumented test and get the timing information from the instrumentation as this
+ // will be more accurate.
+ if (test instanceof InstrumentedTest)
+ {
+ InstrumentedTest iTest = (InstrumentedTest) test;
+
+ // Calculate the test run time.
+ runTime = iTest.getTestTime();
+ // log.debug("runTime = " + runTime);
+
+ // Calculate the test memory usage.
+ long startMem = iTest.getTestStartMemory();
+ long endMem = iTest.getTestEndMemory();
+
+ // log.debug("startMem = " + startMem);
+ // log.debug("endMem = " + endMem);
+
+ // Inform any memory listeners of the test memory.
+ if (tkListeners != null)
+ {
+ for (TKTestListener memoryListener : tkListeners)
+ {
+ memoryListener.memoryUsed(test, startMem, endMem, null);
+ }
+ }
+ }
+ else
+ {
+ // Calculate the test run time.
+ long endTime = System.nanoTime();
+ Long startTime = threadLocalSettings.startTime;
+ runTime = endTime - startTime;
+ // log.debug("runTime = " + runTime);
+
+ threadLocals.remove();
+ }
+
+ // Output end test stats. This is only done when the tests have not used the timing controller to output
+ // mutiple timings.
+ if (!completeTestUsed)
+ {
+ // Check if the test is an asymptotic test case and get its int parameter if so.
+ if (test instanceof AsymptoticTestCase)
+ {
+ AsymptoticTestCase pTest = (AsymptoticTestCase) test;
+
+ // Set the parameter.
+ int paramValue = pTest.getN();
+
+ // Inform any parameter listeners of the test parameter.
+ if (tkListeners != null)
+ {
+ for (TKTestListener parameterListener : tkListeners)
+ {
+ parameterListener.parameterValue(test, paramValue, null);
+ }
+ }
+ }
+
+ // Inform any timing listeners of the test timing and concurrency level.
+ if (tkListeners != null)
+ {
+ for (TKTestListener tkListener : tkListeners)
+ {
+ TKTestListener next = tkListener;
+
+ next.timing(test, runTime, null);
+ next.concurrencyLevel(test, concurrencyLevel, null);
+ }
+ }
+
+ // Call the super method to notify test event listeners of the end event.
+ super.endTest(test);
+ }
+ }
+
+ /**
+ * Gets the integer parameter to pass to parameterized test cases.
+ *
+ * @return The value of the integer parameter.
+ */
+ public int getN()
+ {
+ return n;
+ }
+
+ /**
+ * Sets the integer parameter to pass to parameterized test cases.
+ *
+ * @param n The new value of the integer parameter.
+ */
+ public void setN(int n)
+ {
+ // log.debug("public void setN(int " + n + "): called");
+
+ this.n = n;
+ }
+
+ /**
+ * Adds a timing listener to pass all timing events to.
+ *
+ * @param listener The timing listener to register.
+ */
+ public void addTKTestListener(TKTestListener listener)
+ {
+ // Create the collection to hold the timing listeners if it does not already exist.
+ if (tkListeners == null)
+ {
+ tkListeners = new ArrayList<TKTestListener>();
+ }
+
+ // Keep the new timing listener.
+ tkListeners.add(listener);
+ }
+
+ /**
+ * Called by the test runner to notify this that a new test batch is being begun. This method forwards this
+ * notification to all batch listeners.
+ */
+ public void notifyStartBatch()
+ {
+ if (tkListeners != null)
+ {
+ for (TKTestListener batchListener : tkListeners)
+ {
+ batchListener.startBatch();
+ }
+ }
+ }
+
+ /**
+ * Called by the test runner to notify this that the current test batch has been ended. This method forwards this
+ * notification to all batch listener.
+ */
+ public void notifyEndBatch()
+ {
+ // log.debug("public void notifyEndBatch(): called");
+
+ if (tkListeners != null)
+ {
+ for (TKTestListener batchListener : tkListeners)
+ {
+ batchListener.endBatch(testParameters);
+ }
+ }
+ }
+
+ /**
+ * Called by the test runner to notify this of the properties that the test is using.
+ *
+ * @param properties The tests set/read properties.
+ */
+ public void notifyTestProperties(Properties properties)
+ {
+ // log.debug("public void notifyTestProperties(Properties properties): called");
+
+ this.testParameters = properties;
+
+ /*
+ if (tkListeners != null)
+ {
+ for (TKTestListener batchListener : tkListeners)
+ {
+ batchListener.properties(properties);
+ }
+ }
+ */
+ }
+
+ /**
+ * Intercepts the execution of a test case to pass the variable integer parameter to a test if it is a parameterized
+ * test case.
+ *
+ * @param test The test to run.
+ */
+ protected void run(final TestCase test)
+ {
+ // log.debug("protected void run(final TestCase test): called");
+
+ // Check if the test case is a parameterized test and set its integer parameter if so.
+ if (test instanceof AsymptoticTestCase)
+ {
+ AsymptoticTestCase pTest = (AsymptoticTestCase) test;
+
+ // Set up the integer parameter.
+ pTest.setN(n);
+ }
+
+ // Delegate to the super method to run the test.
+ super.run(test);
+ }
+
+ /**
+ * Helper method that generats a String of verbose information about a test. This includes the thread name, test
+ * class name and test method name.
+ *
+ * @param test The test to generate the info string for.
+ *
+ * @return Returns a string with the thread name, test class name and test method name.
+ */
+ protected String getTestInfo(Test test)
+ {
+ // log.debug("protected String getTestInfo(Test test): called");
+
+ return "[" + Thread.currentThread().getName() + "@" + test.getClass().getName() + "."
+ + ((test instanceof TestCase) ? ((TestCase) test).getName() : "") + "]";
+ }
+
+ /**
+ * Sets the concurrency level to pass into the test result.
+ *
+ * @param concurrencyLevel The concurrency level the tests are running out.
+ */
+ public void setConcurrencyLevel(int concurrencyLevel)
+ {
+ this.concurrencyLevel = concurrencyLevel;
+ }
+
+ /**
+ * Tells this test result that it should stop running tests. Once this method has been called this test result
+ * will not start any new tests, and any tests that use the timing controller will be passed interrupted exceptions,
+ * to indicate that they should end immediately. Usually the caller of this method will introduce a short wait
+ * to allow an opporunity for running tests to complete, before forcing the shutdown of the JVM.
+ */
+ public void shutdownNow()
+ {
+ log.debug("public void shutdownNow(): called on " + this);
+
+ shutdownNow = true;
+ }
+
+ /**
+ * Prints a string summary of this class, mainly for debugging purposes.
+ *
+ * @return A string summary of this class, mainly for debugging purposes.
+ */
+ public String toString()
+ {
+ return "TKTestResult@" + Integer.toString(hashCode(), 16) + ": [ testCaseName = " + testCaseName + ", n = " + n
+ + ", tkListeners = " + tkListeners + " ]";
+ }
+
+ /**
+ * Holds things that need to be kept on a per thread basis for each test invocation, such as the test start
+ * time and its timing controller.
+ */
+ private static class ThreadLocalSettings
+ {
+ /** Holds the test start time. */
+ Long startTime;
+
+ /** Holds the test threads timing controller. */
+ TimingController timingController;
+ }
+
+ /**
+ * Provides an implementation of the {@link TimingController} interface that timing aware tests can use to call
+ * back to reset timers, and register additional test timings.
+ */
+ private static class TimingControllerImpl implements TimingController
+ {
+ /** Holds an explicit reference to the test TKTestResult that created this. */
+ TKTestResult testResult;
+
+ /** Holds a reference to the test that this is the timing controller for. */
+ Test test;
+
+ /** Holds the start time for this timing controller. This gets reset to now on each completed test. */
+ long startTime;
+
+ /**
+ * Holds the thread id of the thread that started the test, so that this controller may be called from other
+ * threads but still identify itself correctly to {@link TKTestListener}s as being associated with the
+ * thread that called the test method.
+ */
+ long threadId;
+
+ /**
+ * Creates a timing controller on a specified TKTestResult and a test.
+ *
+ * @param testResult The TKTestResult that this controller interacts with.
+ * @param test The test that this is the timing controller for.
+ * @param startTime The test start time in nanoseconds.
+ * @param threadId The thread id of the thread that is calling the test method.
+ */
+ public TimingControllerImpl(TKTestResult testResult, Test test, long startTime, long threadId)
+ {
+ this.testResult = testResult;
+ this.test = test;
+ this.startTime = startTime;
+ this.threadId = threadId;
+ }
+
+ /**
+ * Gets the timing controller associated with the current test thread. Tests that use timing controller should
+ * always get the timing controller from this method in the same thread that called the setUp, tearDown or test
+ * method. The controller returned by this method may be called from any thread because it remembers the thread
+ * id of the original test thread.
+ *
+ * @return The timing controller associated with the current test thread.
+ */
+ public TimingController getControllerForCurrentThread()
+ {
+ // Recover the thread local settings and extract the timing controller from them.
+ ThreadLocalSettings threadLocalSettings = testResult.threadLocals.get();
+
+ return threadLocalSettings.timingController;
+ }
+
+ /**
+ * Not implemented yet.
+ *
+ * @return Nothing.
+ */
+ public long suspend()
+ {
+ throw new RuntimeException("Method not implemented.");
+ }
+
+ /**
+ * Not implemented yet.
+ *
+ * @return Nothing.
+ */
+ public long resume()
+ {
+ throw new RuntimeException("Method not implemented.");
+ }
+
+ /**
+ * Resets the timer start time to now.
+ *
+ * @return The new value of the start time.
+ */
+ public long restart()
+ {
+ startTime = System.nanoTime();
+
+ return startTime;
+ }
+
+ /**
+ * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of
+ * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters.
+ *
+ * @param testPassed Whether or not this timing is for a test pass or fail.
+ *
+ * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+ * indicate to the test method that it should stop immediately.
+ */
+ public void completeTest(boolean testPassed) throws InterruptedException
+ {
+ completeTest(testPassed, 1);
+ }
+
+ /**
+ * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
+ * 'size' parmeter.
+ *
+ * @param testPassed Whether or not this timing is for a test pass or fail.
+ * @param param The test parameter size for parameterized tests.
+ *
+ * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+ * indicate to the test method that it should stop immediately.
+ */
+ public void completeTest(boolean testPassed, int param) throws InterruptedException
+ {
+ /*log.debug("public long completeTest(boolean testPassed = " + testPassed + ", int param = " + param
+ + "): called");*/
+
+ // Calculate the test run time.
+ long endTime = System.nanoTime();
+ long runTime = endTime - startTime;
+ // log.debug("runTime = " + runTime);
+
+ // Reset the test start time to now, to reset the timer for the next result.
+ startTime = endTime;
+
+ completeTest(testPassed, param, runTime);
+ }
+
+ /**
+ * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
+ * 'size' parmeter and allows the caller to sepecify the timing to log.
+ *
+ * @param testPassed Whether or not this timing is for a test pass or fail.
+ * @param param The test parameter size for parameterized tests.
+ * @param timeNanos The time in nano-seconds to log the test result with.
+ *
+ * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+ * indicate to the test method that it should stop immediately.
+ */
+ public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException
+ {
+ log.debug("public void completeTest(boolean testPassed, int param, long timeNanos): called");
+ log.debug("testResult = " + testResult);
+
+ // Tell the test result that completeTest has been used, so to not register end test events for the whole
+ // test method.
+ testResult.completeTestUsed = true;
+
+ // Inform any timing listeners of the test timings and parameters and send an end test notification using
+ // the thread id of the thread that started the test.
+ if (testResult.tkListeners != null)
+ {
+ for (TKTestListener listener : testResult.tkListeners)
+ {
+ listener.reset(test, threadId);
+ listener.timing(test, timeNanos, threadId);
+ listener.parameterValue(test, param, threadId);
+ listener.concurrencyLevel(test, testResult.concurrencyLevel, threadId);
+
+ if (!testPassed)
+ {
+ listener.addFailure(test, null, threadId);
+ }
+
+ listener.endTest(test, threadId);
+ }
+ }
+
+ // log.debug("testResult.shutdownNow = " + testResult.shutdownNow);
+
+ // Check if the test runner has been asked to shutdown and raise an interuppted exception if so.
+ if (testResult.shutdownNow)
+ {
+ // log.debug("The shutdown flag is set.");
+
+ throw new InterruptedException("Attempting clean shutdown by suspending current test.");
+ }
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java
new file mode 100644
index 0000000000..7955a2e2e9
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java
@@ -0,0 +1,694 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.listeners.CSVTestListener;
+import org.apache.qpid.junit.extensions.listeners.ConsoleTestListener;
+import org.apache.qpid.junit.extensions.listeners.XMLTestListener;
+import org.apache.qpid.junit.extensions.util.CommandLineParser;
+import org.apache.qpid.junit.extensions.util.MathUtils;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * TKTestRunner extends {@link junit.textui.TestRunner} with the ability to run tests multiple times, to execute a test
+ * simultaneously using many threads, to put a delay between test runs and adds support for tests that take integer
+ * parameters that can be 'stepped' through on multiple test runs. These features can be accessed by using this class
+ * as an entry point and passing command line arguments to specify which features to use:
+ *
+ * <pre>
+ * -w ms The number of milliseconds between invocations of test cases.
+ * -c pattern The number of tests to run concurrently.
+ * -r num The number of times to repeat each test.
+ * -d duration The length of time to run the tests for.
+ * -t name The name of the test case to execute.
+ * -s pattern The size parameter to run tests with.
+ * -o dir The name of the directory to output test timings to.
+ * --csv Output test results in CSV format.
+ * --xml Output test results in XML format.
+ * </pre>
+ *
+ * <p/>This command line may also have trailing 'name=value' parameters added to it. All of these values are added
+ * to the test context properties and passed to the test, which can access them by name.
+ *
+ * <p/>The pattern arguments are of the form [lowest(: ...)(: highest)](:sample=s)(:exp), where round brackets
+ * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided
+ * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples.
+ *
+ * <p/>The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least
+ * one of the optional values must be present.
+ *
+ * <p/>When specifying optional test parameters on the command line, in 'name=value' format, it is also possible to use
+ * the format 'name=[value1:value2:value3:...]', to specify multiple values for a parameter. All permutations of all
+ * parameters with multiple values will be created and tested. If the values are numerical, it is also possible to use
+ * the sequence generation patterns instead of fully specifying all of the values.
+ *
+ * <p/>Here are some examples:
+ *
+ * <p/><table>
+ * <tr><td><pre> -c [10:20:30:40:50] </pre><td> Runs the test with 10,20,...,50 threads.
+ * <tr><td><pre> -s [1:100]:samples=10 </pre>
+ * <td> Runs the test with ten different size parameters evenly spaced between 1 and 100.
+ * <tr><td><pre> -s [1:1000000]:samples=10:exp </pre>
+ * <td> Runs the test with ten different size parameters exponentially spaced between 1 and 1000000.
+ * <tr><td><pre> -r 10 </pre><td> Runs each test ten times.
+ * <tr><td><pre> -d 10H </pre><td> Runs the test repeatedly for 10 hours.
+ * <tr><td><pre> -d 1M, -r 10 </pre>
+ * <td> Runs the test repeatedly for 1 minute but only takes a timing sample every 10 test runs.
+ * <tr><td><pre> -r 10, -c [1:5:10:50], -s [100:1000:10000] </pre>
+ * <td> Runs 12 test cycles (4 concurrency samples * 3 size sample), with 10 repeats each. In total the test
+ * will be run 199 times (3 + 15 + 30 + 150)
+ * <tr><td><pre> cache=true </pre><td> Passes the 'cache' parameter with value 'true' to the test.
+ * <tr><td><pre> cache=[true:false] </pre><td> Runs the test with the 'cache' parameter set to 'true' and 'false'.
+ * <tr><td><pre> cacheSize=[1000:1000000],samples=4,exp </pre>
+ * <td> Runs the test with the 'cache' parameter set to a series of exponentially increasing sizes.
+ * </table>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create the test configuration specified by the command line parameters.
+ * </table>
+ *
+ * @todo Verify that the output directory exists or can be created.
+ *
+ * @todo Verify that the specific named test case to execute exists.
+ *
+ * @todo Drop the delay parameter as it is being replaced by throttling.
+ *
+ * @todo Completely replace the test ui test runner, instead of having TKTestRunner inherit from it, its just not
+ * good code to extend.
+ *
+ * @author Rupert Smith
+ */
+public class TKTestRunner extends TestRunnerImprovedErrorHandling
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TKTestRunner.class);
+
+ /** Used for displaying information on the console. */
+ // private static final Logger console = Logger.getLogger("CONSOLE." + TKTestRunner.class.getName());
+
+ /** Used for generating the timestamp when naming output files. */
+ protected static final DateFormat TIME_STAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
+
+ /** Number of times to rerun the test. */
+ protected Integer repetitions = 1;
+
+ /** The length of time to run the tests for. */
+ protected Long duration;
+
+ /** Number of threads running the tests. */
+ protected int[] threads;
+
+ /** Delay in ms to wait between two test cases. */
+ protected int delay = 0;
+
+ /** The parameter values to pass to parameterized tests. */
+ protected int[] params;
+
+ /** Name of the single test case to execute. */
+ protected String testCaseName = null;
+
+ /** Name of the test class. */
+ protected String testClassName = null;
+
+ /** Name of the test run. */
+ protected String testRunName = null;
+
+ /** Directory to output XML reports into, if specified. */
+ protected String reportDir = null;
+
+ /** Flag that indicates the CSV results listener should be used to output results. */
+ protected boolean csvResults;
+
+ /** Flag that indiciates the XML results listener should be used to output results. */
+ protected boolean xmlResults;
+
+ /**
+ * Holds the name of the class of the test currently being run. Ideally passed into the {@link #createTestResult}
+ * method, but as the signature is already fixed for this, the current value gets pushed here as a member variable.
+ */
+ protected String currentTestClassName;
+
+ /** Holds the test results object, which is reponsible for instrumenting tests/threads to record results. */
+ protected TKTestResult result;
+
+ /** Holds a list of factories for instantiating optional user specified test decorators. */
+ protected List<TestDecoratorFactory> decoratorFactories;
+
+ /**
+ * Constructs a TKTestRunner using System.out for all the output.
+ *
+ * @param repetitions The number of times to repeat the test, or test batch size.
+ * @param duration The length of time to run the tests for. -1 means no duration has been set.
+ * @param threads The concurrency levels to ramp up to.
+ * @param delay A delay in milliseconds between test runs.
+ * @param params The sets of 'size' parameters to pass to test.
+ * @param testCaseName The name of the test case to run.
+ * @param reportDir The directory to output the test results to.
+ * @param runName The name of the test run; used to name the output file.
+ * @param csvResults <tt>true</tt> if the CSV results listener should be attached.
+ * @param xmlResults <tt>true</tt> if the XML results listener should be attached.
+ * @param decoratorFactories List of factories for user specified decorators.
+ */
+ public TKTestRunner(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName,
+ String reportDir, String runName, boolean csvResults, boolean xmlResults,
+ List<TestDecoratorFactory> decoratorFactories)
+ {
+ super(new NullResultPrinter(System.out));
+
+ log.debug("public TKTestRunner(): called");
+
+ // Keep all the test parameters.
+ this.repetitions = repetitions;
+ this.duration = duration;
+ this.threads = threads;
+ this.delay = delay;
+ this.params = params;
+ this.testCaseName = testCaseName;
+ this.reportDir = reportDir;
+ this.testRunName = runName;
+ this.csvResults = csvResults;
+ this.xmlResults = xmlResults;
+ this.decoratorFactories = decoratorFactories;
+ }
+
+ /**
+ * The entry point for the toolkit test runner.
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ // Use the command line parser to evaluate the command line.
+ CommandLineParser commandLine =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "w", "The number of milliseconds between invocations of test cases.", "ms", "false" },
+ { "c", "The number of tests to run concurrently.", "num", "false", MathUtils.SEQUENCE_REGEXP },
+ { "r", "The number of times to repeat each test.", "num", "false" },
+ { "d", "The length of time to run the tests for.", "duration", "false", MathUtils.DURATION_REGEXP },
+ { "f", "The maximum rate to call the tests at.", "frequency", "false", "^([1-9][0-9]*)/([1-9][0-9]*)$" },
+ { "s", "The size parameter to run tests with.", "size", "false", MathUtils.SEQUENCE_REGEXP },
+ { "t", "The name of the test case to execute.", "name", "false" },
+ { "o", "The name of the directory to output test timings to.", "dir", "false" },
+ { "n", "A name for this test run, used to name the output file.", "name", "true" },
+ {
+ "X:decorators", "A list of additional test decorators to wrap the tests in.",
+ "\"class.name[:class.name]*\"", "false"
+ },
+ { "1", "Test class.", "class", "true" },
+ { "-csv", "Output test results in CSV format.", null, "false" },
+ { "-xml", "Output test results in XML format.", null, "false" }
+ });
+
+ // Capture the command line arguments or display errors and correct usage and then exit.
+ ParsedProperties options = null;
+
+ try
+ {
+ options = new ParsedProperties(commandLine.parseCommandLine(args));
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(FAILURE_EXIT);
+ }
+
+ // Extract the command line options.
+ Integer delay = options.getPropertyAsInteger("w");
+ String threadsString = options.getProperty("c");
+ Integer repetitions = options.getPropertyAsInteger("r");
+ String durationString = options.getProperty("d");
+ String paramsString = options.getProperty("s");
+ String testCaseName = options.getProperty("t");
+ String reportDir = options.getProperty("o");
+ String testRunName = options.getProperty("n");
+ String decorators = options.getProperty("X:decorators");
+ String testClassName = options.getProperty("1");
+ boolean csvResults = options.getPropertyAsBoolean("-csv");
+ boolean xmlResults = options.getPropertyAsBoolean("-xml");
+
+ int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString);
+ int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString);
+ Long duration = (durationString == null) ? null : MathUtils.parseDuration(durationString);
+
+ // The test run name defaults to the test class name unless a value was specified for it.
+ testRunName = (testRunName == null) ? testClassName : testRunName;
+
+ // Add all the command line options and trailing settings to test context properties. Tests may pick up
+ // overridden values from there, and these values will be logged in the test results, for analysis and
+ // to make tests repeatable.
+ commandLine.addTrailingPairsToProperties(TestContextProperties.getInstance());
+ commandLine.addOptionsToProperties(TestContextProperties.getInstance());
+
+ // Create and start the test runner.
+ try
+ {
+ // Create a list of test decorator factories for use specified decorators to be applied.
+ List<TestDecoratorFactory> decoratorFactories = parseDecorators(decorators);
+
+ TKTestRunner testRunner =
+ new TKTestRunner(repetitions, duration, threads, (delay == null) ? 0 : delay, params, testCaseName,
+ reportDir, testRunName, csvResults, xmlResults, decoratorFactories);
+
+ TestResult testResult = testRunner.start(testClassName);
+
+ if (!testResult.wasSuccessful())
+ {
+ System.exit(FAILURE_EXIT);
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ e.printStackTrace(new PrintStream(System.err));
+ System.exit(EXCEPTION_EXIT);
+ }
+ }
+
+ /**
+ * Parses a list of test decorators, in the form "class.name[:class.name]*", and creates factories for those
+ * TestDecorator classes , and returns a list of the factories. This list of factories will be in the same
+ * order as specified in the string. The factories can be used to succesively wrap tests in layers of
+ * decorators, as decorators themselves implement the 'Test' interface.
+ *
+ * <p/>If the string fails to parse, or if any of the decorators specified in it are cannot be loaded, or are not
+ * TestDecorators, a runtime exception with a suitable error message will be thrown. The factories themselves
+ * throw runtimes if the constructor method calls on the decorators fail.
+ *
+ * @param decorators The decorators list to be parsed.
+ *
+ * @return A list of instantiated decorators.
+ */
+ protected static List<TestDecoratorFactory> parseDecorators(String decorators)
+ {
+ List<TestDecoratorFactory> result = new LinkedList<TestDecoratorFactory>();
+ String toParse = decorators;
+
+ // Check that the decorators string is not null or empty, returning an empty list of decorator factories it
+ // it is.
+ if ((decorators == null) || "".equals(decorators))
+ {
+ return result;
+ }
+
+ // Strip any leading and trailing quotes from the string.
+ if (toParse.charAt(0) == '\"')
+ {
+ toParse = toParse.substring(1, toParse.length() - 1);
+ }
+
+ if (toParse.charAt(toParse.length() - 1) == '\"')
+ {
+ toParse = toParse.substring(0, toParse.length() - 2);
+ }
+
+ // Instantiate all decorators.
+ for (String decoratorClassName : toParse.split(":"))
+ {
+ try
+ {
+ Class decoratorClass = Class.forName(decoratorClassName);
+ final Constructor decoratorConstructor = decoratorClass.getConstructor(WrappedSuiteTestDecorator.class);
+
+ // Check that the decorator is an instance of WrappedSuiteTestDecorator.
+ if (!WrappedSuiteTestDecorator.class.isAssignableFrom(decoratorClass))
+ {
+ throw new RuntimeException("The decorator class " + decoratorClassName
+ + " is not a sub-class of WrappedSuiteTestDecorator, which it needs to be.");
+ }
+
+ result.add(new TestDecoratorFactory()
+ {
+ public WrappedSuiteTestDecorator decorateTest(Test test)
+ {
+ try
+ {
+ return (WrappedSuiteTestDecorator) decoratorConstructor.newInstance(test);
+ }
+ catch (InstantiationException e)
+ {
+ throw new RuntimeException(
+ "The decorator class " + decoratorConstructor.getDeclaringClass().getName()
+ + " cannot be instantiated.", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new RuntimeException(
+ "The decorator class " + decoratorConstructor.getDeclaringClass().getName()
+ + " does not have a publicly accessable constructor.", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new RuntimeException(
+ "The decorator class " + decoratorConstructor.getDeclaringClass().getName()
+ + " cannot be invoked.", e);
+ }
+ }
+ });
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new RuntimeException("The decorator class " + decoratorClassName + " could not be found.", e);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new RuntimeException("The decorator class " + decoratorClassName
+ + " does not have a constructor that accepts a single 'WrappedSuiteTestDecorator' argument.", e);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * TestDecoratorFactory is a factory for creating test decorators from tests.
+ */
+ protected interface TestDecoratorFactory
+ {
+ /**
+ * Decorates the specified test with a new decorator.
+ *
+ * @param test The test to decorate.
+ *
+ * @return The decorated test.
+ */
+ public WrappedSuiteTestDecorator decorateTest(Test test);
+ }
+
+ /**
+ * Runs a test or suite of tests, using the super class implemenation. This method wraps the test to be run
+ * in any test decorators needed to add in the configured toolkits enhanced junit functionality.
+ *
+ * @param test The test to run.
+ * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for.
+ *
+ * @return The results of the test run.
+ */
+ public TestResult doRun(Test test, boolean wait)
+ {
+ log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called");
+
+ // Wrap the tests in decorators for duration, scaling, repetition, parameterization etc.
+ WrappedSuiteTestDecorator targetTest = decorateTests(test);
+
+ // Delegate to the super method to run the decorated tests.
+ log.debug("About to call super.doRun");
+
+ TestResult result = super.doRun(targetTest, wait);
+ log.debug("super.doRun returned.");
+
+ /*if (result instanceof TKTestResult)
+ {
+ TKTestResult tkResult = (TKTestResult) result;
+
+ tkResult.notifyEndBatch();
+ }*/
+
+ return result;
+ }
+
+ /**
+ * Applies test decorators to the tests for parameterization, duration, scaling and repetition.
+ *
+ * @param test The test to decorat.
+ *
+ * @return The decorated test.
+ */
+ protected WrappedSuiteTestDecorator decorateTests(Test test)
+ {
+ log.debug("params = " + ((params == null) ? null : MathUtils.printArray(params)));
+ log.debug("repetitions = " + repetitions);
+ log.debug("threads = " + ((threads == null) ? null : MathUtils.printArray(threads)));
+ log.debug("duration = " + duration);
+
+ // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling,
+ // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it.
+ WrappedSuiteTestDecorator targetTest = null;
+
+ if (test instanceof TestSuite)
+ {
+ log.debug("targetTest is a TestSuite");
+
+ TestSuite suite = (TestSuite) test;
+
+ int numTests = suite.countTestCases();
+ log.debug("There are " + numTests + " in the suite.");
+
+ for (int i = 0; i < numTests; i++)
+ {
+ Test nextTest = suite.testAt(i);
+ log.debug("suite.testAt(" + i + ") = " + nextTest);
+
+ if (nextTest instanceof TimingControllerAware)
+ {
+ log.debug("nextTest is TimingControllerAware");
+ }
+
+ if (nextTest instanceof TestThreadAware)
+ {
+ log.debug("nextTest is TestThreadAware");
+ }
+ }
+
+ targetTest = new WrappedSuiteTestDecorator(suite);
+ log.debug("Wrapped with a WrappedSuiteTestDecorator.");
+ }
+ // If the test has already been wrapped, no need to do it again.
+ else if (test instanceof WrappedSuiteTestDecorator)
+ {
+ targetTest = (WrappedSuiteTestDecorator) test;
+ }
+
+ // If size parameter values have been set, then wrap the test in an asymptotic test decorator.
+ if (params != null)
+ {
+ targetTest = new AsymptoticTestDecorator(targetTest, params, (repetitions == null) ? 1 : repetitions);
+ log.debug("Wrapped with asymptotic test decorator.");
+ log.debug("targetTest = " + targetTest);
+ }
+
+ // If no size parameters are set but the repitions parameter is, then wrap the test in an asymptotic test decorator.
+ else if ((repetitions != null) && (repetitions > 1))
+ {
+ targetTest = new AsymptoticTestDecorator(targetTest, new int[] { 1 }, repetitions);
+ log.debug("Wrapped with asymptotic test decorator.");
+ log.debug("targetTest = " + targetTest);
+ }
+
+ // Apply any optional user specified decorators.
+ targetTest = applyOptionalUserDecorators(targetTest);
+
+ // If a test run duration has been set then wrap the test in a duration test decorator. This will wrap on
+ // top of size, repeat or concurrency wrappings already applied.
+ if (duration != null)
+ {
+ DurationTestDecorator durationTest = new DurationTestDecorator(targetTest, duration);
+ targetTest = durationTest;
+
+ log.debug("Wrapped with duration test decorator.");
+ log.debug("targetTest = " + targetTest);
+
+ registerShutdownHook(durationTest);
+ }
+
+ // ParameterVariationTestDecorator...
+
+ // If a test thread concurrency level is set then wrap the test in a scaled test decorator. This will wrap on
+ // top of size scaling or repetition wrappings.
+ ScaledTestDecorator scaledDecorator;
+
+ if ((threads != null) && ((threads.length > 1) || (MathUtils.maxInArray(threads) > 1)))
+ {
+ scaledDecorator = new ScaledTestDecorator(targetTest, threads);
+ targetTest = scaledDecorator;
+ log.debug("Wrapped with scaled test decorator.");
+ log.debug("targetTest = " + targetTest);
+ }
+ else
+ {
+ scaledDecorator = new ScaledTestDecorator(targetTest, new int[] { 1 });
+ targetTest = scaledDecorator;
+ log.debug("Wrapped with scaled test decorator with default of 1 thread.");
+ log.debug("targetTest = " + targetTest);
+ }
+
+ // Register the scaled test decorators shutdown hook.
+ registerShutdownHook(scaledDecorator);
+
+ return targetTest;
+ }
+
+ /**
+ * If there were any user specified test decorators on the command line, this method instantiates them and wraps
+ * the test in them, from inner-most to outer-most in the order in which the decorators were supplied on the
+ * command line.
+ *
+ * @param targetTest The test to wrap.
+ *
+ * @return A wrapped test.
+ */
+ protected WrappedSuiteTestDecorator applyOptionalUserDecorators(WrappedSuiteTestDecorator targetTest)
+ {
+ // If there are user defined test decorators apply them in order now.
+ for (TestDecoratorFactory factory : decoratorFactories)
+ {
+ targetTest = factory.decorateTest(targetTest);
+ }
+
+ return targetTest;
+ }
+
+ /**
+ * Creates the TestResult object to be used for test runs. See {@link TKTestResult} for more information and the
+ * enhanced test result class that this uses.
+ *
+ * @return An instance of the enhanced test result object, {@link TKTestResult}.
+ */
+ protected TestResult createTestResult()
+ {
+ log.debug("protected TestResult createTestResult(): called");
+
+ TKTestResult result = new TKTestResult(delay, testCaseName);
+
+ // Check if a directory to output reports to has been specified and attach test listeners if so.
+ if (reportDir != null)
+ {
+ // Create the report directory if it does not already exist.
+ File reportDirFile = new File(reportDir);
+
+ if (!reportDirFile.exists())
+ {
+ reportDirFile.mkdir();
+ }
+
+ // Create the results file (make the name of this configurable as a command line parameter).
+ Writer timingsWriter;
+
+ // Always set up a console feedback listener.
+ ConsoleTestListener feedbackListener = new ConsoleTestListener();
+ result.addListener(feedbackListener);
+ result.addTKTestListener(feedbackListener);
+
+ // Set up an XML results listener to output the timings to the results file, if requested on the command line.
+ if (xmlResults)
+ {
+ try
+ {
+ File timingsFile = new File(reportDirFile, "TEST-" + currentTestClassName + ".xml");
+ timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to create the log file to write test results to: " + e, e);
+ }
+
+ XMLTestListener listener = new XMLTestListener(timingsWriter, currentTestClassName);
+ result.addListener(listener);
+ result.addTKTestListener(listener);
+
+ registerShutdownHook(listener);
+ }
+
+ // Set up an CSV results listener to output the timings to the results file, if requested on the command line.
+ if (csvResults)
+ {
+ try
+ {
+ File timingsFile =
+ new File(reportDirFile, testRunName + "-" + TIME_STAMP_FORMAT.format(new Date()) + "-timings.csv");
+ timingsWriter = new BufferedWriter(new FileWriter(timingsFile), 20000);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to create the log file to write test results to: " + e, e);
+ }
+
+ CSVTestListener listener = new CSVTestListener(timingsWriter);
+ result.addListener(listener);
+ result.addTKTestListener(listener);
+
+ // Register the results listeners shutdown hook to flush its data if the test framework is shutdown
+ // prematurely.
+ registerShutdownHook(listener);
+ }
+
+ // Register the results listeners shutdown hook to flush its data if the test framework is shutdown
+ // prematurely.
+ // registerShutdownHook(listener);
+
+ // Record the start time of the batch.
+ // result.notifyStartBatch();
+
+ // At this point in time the test class has been instantiated, giving it an opportunity to read its parameters.
+ // Inform any test listers of the test properties.
+ result.notifyTestProperties(TestContextProperties.getAccessedProps());
+ }
+
+ return result;
+ }
+
+ /**
+ * Registers the shutdown hook of a {@link ShutdownHookable}.
+ *
+ * @param hookable The hookable to register.
+ */
+ protected void registerShutdownHook(ShutdownHookable hookable)
+ {
+ Runtime.getRuntime().addShutdownHook(hookable.getShutdownHook());
+ }
+
+ /**
+ * Initializes the test runner with the provided command line arguments and and starts the test run.
+ *
+ * @param testClassName The fully qualified name of the test class to run.
+ *
+ * @return The test results.
+ *
+ * @throws Exception Any exceptions from running the tests are allowed to fall through.
+ */
+ protected TestResult start(String testClassName) throws Exception
+ {
+ // Record the current test class, so that the test results can be output to a file incorporating this name.
+ this.currentTestClassName = testClassName;
+
+ // Delegate to the super method to run the tests.
+ return super.start(new String[] { testClassName });
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java
new file mode 100644
index 0000000000..9b4a8707db
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java
@@ -0,0 +1,131 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import junit.runner.Version;
+
+import junit.textui.ResultPrinter;
+import junit.textui.TestRunner;
+
+import org.apache.log4j.Logger;
+
+import java.io.PrintStream;
+
+/**
+ * The {@link junit.textui.TestRunner} does not provide very good error handling. It does not wrap exceptions and
+ * does not print out stack traces, losing valuable error tracing information. This class overrides methods in it
+ * in order to improve their error handling. The {@link TKTestRunner} is then built on top of this.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class TestRunnerImprovedErrorHandling extends TestRunner
+{
+ /** Used for logging. */
+ Logger log = Logger.getLogger(TestRunnerImprovedErrorHandling.class);
+
+ /**
+ * Delegates to the super constructor.
+ */
+ public TestRunnerImprovedErrorHandling()
+ {
+ super();
+ }
+
+ /**
+ * Delegates to the super constructor.
+ *
+ * @param printStream The location to write test results to.
+ */
+ public TestRunnerImprovedErrorHandling(PrintStream printStream)
+ {
+ super(printStream);
+ }
+
+ /**
+ * Delegates to the super constructor.
+ *
+ * @param resultPrinter The location to write test results to.
+ */
+ public TestRunnerImprovedErrorHandling(ResultPrinter resultPrinter)
+ {
+ super(resultPrinter);
+ }
+
+ /**
+ * Starts a test run. Analyzes the command line arguments
+ * and runs the given test suite.
+ *
+ * @param args The command line arguments.
+ *
+ * @return The test results.
+ *
+ * @throws Exception Any exceptions falling through the tests are wrapped in Exception and rethrown.
+ */
+ protected TestResult start(String[] args) throws Exception
+ {
+ String testCase = "";
+ boolean wait = false;
+
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i].equals("-wait"))
+ {
+ wait = true;
+ }
+ else if (args[i].equals("-c"))
+ {
+ testCase = extractClassName(args[++i]);
+ }
+ else if (args[i].equals("-v"))
+ {
+ System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
+ }
+ else
+ {
+ testCase = args[i];
+ }
+ }
+
+ if (testCase.equals(""))
+ {
+ throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+ }
+
+ try
+ {
+ Test suite = getTest(testCase);
+
+ return doRun(suite, wait);
+ }
+ catch (Exception e)
+ {
+ log.warn("Got exception whilst creating and running test suite.", e);
+ throw new Exception("Could not create and run the test suite.", e);
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java
new file mode 100644
index 0000000000..aaa773260d
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.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.junit.extensions;
+
+/**
+ * This interface can be implemented by tests that want to know if they are being run concurrently. It provides
+ * lifecycle notification events to tell the test implementation when test threads are being created and destroyed.
+ * This can assist tests in creating and destroying resources that exist over the life of a test thread. A single
+ * test thread can excute the same test many times, and often it is convenient to keep resources, for example network
+ * connections, open over many test calls.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Set up per thread test fixtures.
+ * <tr><td> Clean up per thread test fixtures.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TestThreadAware
+{
+ /**
+ * Called when a test thread is created.
+ */
+ public void threadSetUp();
+
+ /**
+ * Called when a test thread is destroyed.
+ */
+ public void threadTearDown();
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java
new file mode 100644
index 0000000000..955e47c25b
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java
@@ -0,0 +1,73 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * Throttle is an interface that supplies a {@link #throttle} method, that can only be called at the rate specified
+ * in a call to the {@link #setRate} method. This can be used to restict processing to run at a certain number
+ * of operations per second.
+ *
+ * <p/>Throttle also supplies a method to check the throttle rate, without waiting. This could be used to update a user
+ * interface every time an event occurs, but only up to a maximum rate. For example, as elements are added to a list,
+ * a count of elements is updated for the user to see, but only up to a maximum rate of ten updates a second, as updating
+ * faster than that slows the processing of element-by-element additions to the list unnecessarily.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Accept throttling rate in operations per second.
+ * <tr><td> Inject short pauses to fill-out processing cycles to a specified rate.
+ * <tr><td> Check against a throttle speed without waiting.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface Throttle
+{
+ /**
+ * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse
+ * of which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer.
+ * The value must also be larger than zero.
+ *
+ * @param hertz The throttling rate in cycles per second.
+ */
+ public void setRate(float hertz);
+
+ /**
+ * This method can only be called at the rate set by the {@link #setRate} method, if it is called faster than this
+ * it will inject short pauses to restrict the call rate to that rate.
+ *
+ * <p/>If the thread executing this method is interrupted, it must ensure that the threads interrupt thread
+ * remains set upon exit from the method. This method does not expose InterruptedException, to indicate interruption
+ * of the throttle during a timed wait. It may be changed so that it does.
+ */
+ public void throttle();
+
+ /**
+ * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater
+ * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>true</tt>
+ *
+ * @return <tt>true</tt> if a length of time greater than that equal to the inverse of the throttling rate has
+ * passed since this method was last called and returned <tt>true</tt>, <tt>false</tt> otherwise. The very
+ * first time this method is called on a throttle, it returns <tt>true</tt> as the base case to the above
+ * self-referential definition.
+ */
+ public boolean checkThrottle();
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java
new file mode 100644
index 0000000000..7b5763f1de
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java
@@ -0,0 +1,175 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * A TimingController is a interface that a test that is aware of the fact that it is being timed can use to manage
+ * the timer. Using this interface tests can suspend and resume test timers. This is usefull if you want to exclude
+ * some expensive preparation from being timed as part of a test. In general when timing tests to measure the
+ * performance of code, you should try to set up data in the #setUp where possible, or as static members in the test
+ * class. This is not always convenient, this interface gives you a way to suspend and resume, or event completely
+ * restart test timers, to get accurate measurements.
+ *
+ * <p/>The interface can also be used to register multiple test pass/fails and timings from a single test method.
+ * In some cases it is easier to write tests in this way. For example a concurrent and asynchronous test may make
+ * many asynchronous requests and then wait for replies to all its requests. Writing such a test with one send/reply
+ * per test method and trying to scale up using many threads will quickly run into limitations if more than about
+ * 100 asynchronous calls need to be made at once. A better way to write such a test is as a single method that sends
+ * many (perhaps thousands or millions) and waits for replies in two threads, one for send, one for replies. It can
+ * then log pass/fails and timings on each individual reply as they come back in, even though the test has been written
+ * to send thousands of requests per test method in order to do volume testing.
+ *
+ * <p/>If when the {@link #completeTest(boolean)} is called, the test runner decides that testing should stop (perhaps
+ * because a duration test has expired), it throws an InterruptedException to indicate that the test method should stop
+ * immediately. The test method can do this by allowing this exception to fall through, if no other clean-up handling
+ * is necessary, or it can simply return as soon as it possibly can. The test runner will still call the tearDown
+ * method in the usual way when this happens.
+ *
+ * <p/>Below are some examples of how this can be used. Not how checking that the timing controller is really available
+ * rather than assuming it is, means that the test can run as an ordinary JUnit test under the default test runners. In
+ * general code should be written to take advantage of the extended capabilities of junit toolkit, without assuming they
+ * are going to be run under its test runner.
+ *
+ * <pre>
+ * public class MyTest extends TestCase implements TimingControllerAware {
+ * ...
+ *
+ * timingUtils = this.getTimingController();
+ *
+ * // Do expensive data preparation here...
+ *
+ * if (timingUtils != null)
+ * timingUtils.restart();
+ * </pre>
+ *
+ * <pre>
+ * public class MyTest extends TestCase implements TimingControllerAware {
+ * ...
+ *
+ * public void myVolumeTest(int size) {
+ *
+ * timingUtils = this.getTimingController();
+ *
+ * boolean stopNow = false;
+ *
+ * // In Sender thread.
+ * for(int i = 0; !stopNow && i < size; i++)
+ * // Send request i.
+ * ...
+ *
+ * // In Receiver thread.
+ * onReceive(Object o) {
+ * try {
+ * // Check o is as expected.
+ * if (....)
+ * {
+ * if (timingUtils != null)
+ * timingUtils.completeTest(true);
+ * }
+ * else
+ * {
+ * if (timingUtils != null)
+ * timingUtils.completeTest(false);
+ * }
+ * } catch (InterruptedException e) {
+ * stopNow = true;
+ * return;
+ * }
+ * }
+ * </pre>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Allow test timers to be suspended, restarted or reset.
+ * <tr><td> Allow tests to register multiple pass/fails and timings.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TimingController
+{
+ /**
+ * Gets the timing controller associated with the current test thread. Tests that use timing controller should
+ * always get the timing controller from this method in the same thread that called the setUp, tearDown or test
+ * method. The controller returned by this method may be called from any thread because it remembers the thread
+ * id of the original test thread.
+ *
+ * @return The timing controller associated with the current test thread.
+ */
+ public TimingController getControllerForCurrentThread();
+
+ /**
+ * Suspends the test timer.
+ *
+ * @return The current time in nanoseconds.
+ */
+ public long suspend();
+
+ /**
+ * Allows the test timer to continue running after a suspend.
+ *
+ * @return The current time in nanoseconds.
+ */
+ public long resume();
+
+ /**
+ * Completely restarts the test timer from zero.
+ *
+ * @return The current time in nanoseconds.
+ */
+ public long restart();
+
+ /**
+ * Register an additional pass/fail for the current test. The test result is assumed to apply to a test of
+ * 'size' parmeter 1. Use the {@link #completeTest(boolean, int)} method to register timings with parameters.
+ *
+ * @param testPassed Whether or not this timing is for a test pass or fail.
+ *
+ * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+ * indicate to the test method that it should stop immediately.
+ */
+ public void completeTest(boolean testPassed) throws InterruptedException;
+
+ /**
+ * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
+ * 'size' parmeter.
+ *
+ * @param testPassed Whether or not this timing is for a test pass or fail.
+ * @param param The test parameter size for parameterized tests.
+ *
+ * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+ * indicate to the test method that it should stop immediately.
+ */
+ public void completeTest(boolean testPassed, int param) throws InterruptedException;
+
+ /**
+ * Register an additional pass/fail for the current test. The test result is applies to a test of the specified
+ * 'size' parmeter and allows the caller to sepecify the timing to log.
+ *
+ * @param testPassed Whether or not this timing is for a test pass or fail.
+ * @param param The test parameter size for parameterized tests.
+ * @param timeNanos The time in nano seconds to log the test result with.
+ *
+ * @throws InterruptedException If the test runner decides that testing should stop it throws this exception to
+ * indicate to the test method that it should stop immediately.
+ */
+ public void completeTest(boolean testPassed, int param, long timeNanos) throws InterruptedException;
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java
new file mode 100644
index 0000000000..1ccdc7dbad
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+/**
+ * TimingControllerAware is an interface that tests that manipulate the timing controller should implement. It enables
+ * the TK test runner to set the test up with a handle on the timing controller which the test can use to call back
+ * to the test runner to manage the timers.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide timing controller insertion point for tests.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TimingControllerAware
+{
+ /**
+ * Used by test runners that can supply a {@link TimingController} to set the controller on an aware test.
+ *
+ * @param controller The timing controller.
+ */
+ public void setTimingController(TimingController controller);
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java
new file mode 100644
index 0000000000..7a1e537b1c
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java
@@ -0,0 +1,134 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions;
+
+import junit.extensions.TestDecorator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * WrappedSuiteTestDecorator is a test decorator that wraps a test suite, or another wrapped suite, but provides the
+ * same functionality for the {@link junit.extensions.TestDecorator#countTestCases()} and {@link TestSuite#testAt(int)}
+ * methods as the underlying suite. It returns the values that these methods provide, to enable classes using decorated
+ * tests to drill down to the underlying tests in the suite. That is to say that it indexes and reports the number of
+ * distinct tests in the suite, not the number of test runs that would result from, for example, wrapping the suite in a
+ * repeating decorator.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide access to the underlying tests in a suite.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class WrappedSuiteTestDecorator extends TestDecorator
+{
+ /** Used for logging. */
+ private static Logger log = Logger.getLogger(WrappedSuiteTestDecorator.class);
+
+ /** Holds the test suite that this supplies access to. */
+ protected Test suite;
+
+ /**
+ * Creates a wrappred suite test decorator from a test suite.
+ *
+ * @param suite The test suite.
+ */
+ public WrappedSuiteTestDecorator(TestSuite suite)
+ {
+ super(suite);
+ this.suite = suite;
+ }
+
+ /**
+ * Creates a wrapped suite test decorator from another one.
+ *
+ * @param suite The test suite.
+ */
+ public WrappedSuiteTestDecorator(WrappedSuiteTestDecorator suite)
+ {
+ super(suite);
+ this.suite = suite;
+ }
+
+ /**
+ * Returns the test count of the wrapped suite.
+ *
+ * @return The test count of the wrapped suite.
+ */
+ public int countTestCases()
+ {
+ return suite.countTestCases();
+ }
+
+ /**
+ * Gets the ith test from the test suite.
+ *
+ * @param i The index of the test within the suite to get.
+ *
+ * @return The test with the specified index.
+ */
+ public Test testAt(int i)
+ {
+ log.debug("public Test testAt(int i = " + i + "): called");
+
+ if (suite instanceof WrappedSuiteTestDecorator)
+ {
+ return ((WrappedSuiteTestDecorator) suite).testAt(i);
+ }
+ else if (suite instanceof TestSuite)
+ {
+ return ((TestSuite) suite).testAt(i);
+ }
+
+ // This should never happen.
+ return null;
+ }
+
+ /**
+ * Gets all the tests from the underlying test suite.
+ *
+ * @return All the tests from the underlying test suite.
+ */
+ public Collection<Test> getAllUnderlyingTests()
+ {
+ log.debug("public Collection<Test> getAllUnderlyingTests(): called");
+
+ List<Test> tests = new ArrayList<Test>();
+
+ int numTests = countTestCases();
+ log.debug("numTests = " + numTests);
+
+ for (int i = 0; i < numTests; i++)
+ {
+ tests.add(testAt(i));
+ }
+
+ return tests;
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java
new file mode 100644
index 0000000000..a771e08cf7
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java
@@ -0,0 +1,532 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.ShutdownHookable;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * CSVTestListener is both a test listener, a timings listener, a memory listener and a parameter listener. It listens for test completion events and
+ * then writes out all the data that it has listened to into a '.csv' (comma seperated values) file.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Listen to test events; start, end, fail, error.
+ * <tr><td> Listen to test timings.
+ * <tr><td> Listen to test memory usage.
+ * <tr><td> Listen to parameterized test parameters.
+ * <tr><td> Output all test data to a CSV file.
+ * </table>
+ *
+ * @author Rupert Smith
+ *
+ * @todo Write an XML output class. Write a transform to convert it into an HTML page with timings as graphs.
+ */
+public class CSVTestListener implements TestListener, TKTestListener, ShutdownHookable
+{
+ /** Used for logging. */
+ private static final Logger log = Logger.getLogger(CSVTestListener.class);
+
+ /** The timings file writer. */
+ private Writer timingsWriter;
+
+ /**
+ * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an
+ * explicit thread id must be used, where notifications come from different threads than the ones that called
+ * the test method.
+ */
+ Map<Long, TestResult> threadLocalResults = Collections.synchronizedMap(new HashMap<Long, TestResult>());
+
+ /** Used to record the start time of a complete test run, for outputing statistics at the end of the test run. */
+ private long batchStartTime;
+
+ /** Used to record the number of errors accross a complete test run. */
+ private int numError;
+
+ /** Used to record the number of failures accross a complete test run. */
+ private int numFailed;
+
+ /** Used to record the number of passes accross a complete test run. */
+ private int numPassed;
+
+ /** Used to record the total tests run accross a complete test run. Always equal to passes + errors + fails. */
+ private int totalTests;
+
+ /** Used to recrod the current concurrency level for the test batch. */
+ private int concurrencyLevel;
+
+ /**
+ * Used to record the total 'size' of the tests run, this is the number run times the average value of the test
+ * size parameters.
+ */
+ private int totalSize;
+
+ /**
+ * Used to record the summation of all of the individual test timgings. Note that total time and summed time
+ * are unlikely to be in agreement, exception for a single threaded test (with no setup time). Total time is
+ * the time taken to run all the tests, summed time is the added up time that each individual test took. So if
+ * two tests run in parallel and take one second each, total time will be one seconds, summed time will be two
+ * seconds.
+ */
+ private long summedTime;
+
+ /** Flag to indicate when batch has been started but not ended to ensure end batch stats are output only once. */
+ private boolean batchStarted = false;
+
+ /**
+ * Creates a new CSVTestListener object.
+ *
+ * @param writer A writer where this CSV listener should write out its output to.
+ */
+ public CSVTestListener(Writer writer)
+ {
+ // log.debug("public CSVTestListener(Writer writer): called");
+
+ // Keep the writer.
+ this.timingsWriter = writer;
+ }
+
+ /**
+ * Resets the test results to the default state of time zero, memory usage zero, test passed.
+ *
+ * @param test The test to resest any results for.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void reset(Test test, Long threadId)
+ {
+ // log.debug("public void reset(Test test = \"" + test + "\", Long threadId = " + threadId + "): called");
+
+ TestResult r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ r.testTime = 0L;
+ r.testStartMem = 0L;
+ r.testEndMem = 0L;
+ r.testState = "Pass";
+ r.testParam = 0;
+ }
+
+ /**
+ * Called when a test results in an error.
+ *
+ * @param test The test which is in error.
+ * @param t Any Throwable raised by the test in error.
+ */
+ public void addError(Test test, Throwable t)
+ {
+ // log.debug("public void addError(Test test, Throwable t): called");
+
+ TestResult r = threadLocalResults.get(Thread.currentThread().getId());
+ r.testState = "Error";
+ }
+
+ /**
+ * Called when a test results in a failure.
+ *
+ * @param test The test which failed.
+ * @param t The AssertionFailedError that encapsulates the test failure.
+ */
+ public void addFailure(Test test, AssertionFailedError t)
+ {
+ // log.debug("public void addFailure(Test \"" + test + "\", AssertionFailedError t): called");
+
+ TestResult r = threadLocalResults.get(Thread.currentThread().getId());
+ r.testState = "Failure";
+ }
+
+ /**
+ * Called when a test completes to mark it as a test fail. This method should be used when registering a
+ * failure from a different thread than the one that started the test.
+ *
+ * @param test The test which failed.
+ * @param e The assertion that failed the test.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void addFailure(Test test, AssertionFailedError e, Long threadId)
+ {
+ // log.debug("public void addFailure(Test test = \"" + test + "\", AssertionFailedError e, Long threadId = " + threadId
+ // + "): called");
+
+ TestResult r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ r.testState = "Failure";
+ }
+
+ /**
+ * Called when a test completes. Success, failure and errors.
+ *
+ * @param test The test which completed.
+ */
+ public void endTest(Test test)
+ {
+ // log.debug("public void endTest(Test \"" + test + "\"): called");
+
+ TestResult r = threadLocalResults.get(Thread.currentThread().getId());
+
+ writeTestResults(r, test);
+
+ // Clear all the test results for the thread.
+ threadLocalResults.remove(Thread.currentThread().getId());
+ }
+
+ /**
+ * Called when a test starts.
+ *
+ * @param test The test wich has started.
+ */
+ public void startTest(Test test)
+ {
+ // log.debug("public void startTest(Test \"" + test + "\"): called");
+
+ // Initialize the thread local test results.
+ threadLocalResults.put(Thread.currentThread().getId(), new TestResult());
+ }
+
+ /**
+ * Should be called every time a test completes with the run time of that test.
+ *
+ * @param test The name of the test.
+ * @param nanos The run time of the test in nanoseconds.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void timing(Test test, long nanos, Long threadId)
+ {
+ // log.debug("public void timing(String \"" + test + "\", long " + nanos + "): called");
+
+ TestResult r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ r.testTime = nanos;
+ summedTime += nanos;
+ }
+
+ /**
+ * Should be called every time a test completed with the amount of memory used before and after the test was run.
+ *
+ * @param test The test which memory was measured for.
+ * @param memStart The total JVM memory used before the test was run.
+ * @param memEnd The total JVM memory used after the test was run.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
+ {
+ // log.debug("public void memoryUsed(Test \"" + test + "\", long " + memStart + ", long " + memEnd + ", Long "
+ // + threadId + "): called");
+
+ TestResult r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ r.testStartMem = memStart;
+ r.testEndMem = memEnd;
+ }
+
+ /**
+ * Should be called every time a parameterized test completed with the int value of its test parameter.
+ *
+ * @param test The test which memory was measured for.
+ * @param parameter The int parameter value.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void parameterValue(Test test, int parameter, Long threadId)
+ {
+ // log.debug("public void parameterValue(Test test = \"" + test + "\", int parameter = " + parameter + "): called");
+
+ TestResult r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ r.testParam = parameter;
+ totalSize += parameter;
+ }
+
+ /**
+ * Should be called every time a test completes with the current number of test threads running. This should not
+ * change within a test batch, therefore it is safe to take this as a batch level property value too.
+ *
+ * @param test The test for which the measurement is being generated.
+ * @param threads The number of tests being run concurrently.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void concurrencyLevel(Test test, int threads, Long threadId)
+ {
+ // log.debug("public void concurrencyLevel(Test test = \"" + test + "\", int threads = " + threads + "): called");
+
+ TestResult r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ r.testConcurrency = threads;
+ concurrencyLevel = threads;
+
+ }
+
+ /**
+ * Called when a test completes. Success, failure and errors. This method should be used when registering an
+ * end test from a different thread than the one that started the test.
+ *
+ * @param test The test which completed.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void endTest(Test test, Long threadId)
+ {
+ // log.debug("public void endTest(Test test = \"" + test + "\", Long threadId " + threadId + "): called");
+
+ TestResult r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ writeTestResults(r, test);
+ }
+
+ /**
+ * Takes a time stamp for the beginning of the batch and resets stats counted for the batch.
+ */
+ public synchronized void startBatch()
+ {
+ numError = 0;
+ numFailed = 0;
+ numPassed = 0;
+ totalTests = 0;
+ totalSize = 0;
+ batchStartTime = System.nanoTime();
+ summedTime = 0;
+ batchStarted = true;
+
+ // Write out the column headers for the batch.
+ writeColumnHeaders();
+ }
+
+ /**
+ * Takes a time stamp for the end of the batch to calculate the total run time.
+ * Write this and other stats out to the tail of the csv file.
+ *
+ * @param parameters The optional test parameters, may be null.
+ */
+ public synchronized void endBatch(Properties parameters)
+ {
+ boolean noParams = (parameters == null) || (parameters.size() == 0);
+
+ // Check that a batch has been started but not ended.
+ if (batchStarted)
+ {
+ long batchEndTime = System.nanoTime();
+ float totalTimeMillis = ((float) (batchEndTime - batchStartTime)) / 1000000f;
+ float summedTimeMillis = ((float) summedTime) / 1000000f;
+
+ // Write the stats for the batch out.
+ try
+ {
+ synchronized (this.getClass())
+ {
+ timingsWriter.write("Total Tests:, " + totalTests + ", ");
+ timingsWriter.write("Total Passed:, " + numPassed + ", ");
+ timingsWriter.write("Total Failed:, " + numFailed + ", ");
+ timingsWriter.write("Total Error:, " + numError + ", ");
+ timingsWriter.write("Total Size:, " + totalSize + ", ");
+ timingsWriter.write("Summed Time:, " + summedTimeMillis + ", ");
+ timingsWriter.write("Concurrency Level:, " + concurrencyLevel + ", ");
+ timingsWriter.write("Total Time:, " + totalTimeMillis + ", ");
+ timingsWriter.write("Test Throughput:, " + (((float) totalTests) / totalTimeMillis) + ", ");
+ timingsWriter.write("Test * Size Throughput:, " + (((float) totalSize) / totalTimeMillis)
+ + (noParams ? "\n\n" : ", "));
+
+ // Write out the test parameters if there are any specified.
+ if (!noParams)
+ {
+ properties(parameters);
+ }
+
+ timingsWriter.flush();
+ }
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to write out end batch statistics: " + e, e);
+ }
+ }
+
+ // Reset the batch started flag to ensure stats are only output once.
+ batchStarted = false;
+ }
+
+ /**
+ * Notifies listeners of the tests read/set properties.
+ *
+ * @param properties The tests read/set properties.
+ */
+ public void properties(Properties properties)
+ {
+ // log.debug("public void properties(Properties properties): called");
+
+ // Write the properties out to the results file.
+ try
+ {
+ synchronized (this.getClass())
+ {
+ Set keySet = new TreeSet(properties.keySet());
+
+ // timingsWriter.write("\n");
+
+ for (Object key : keySet)
+ {
+ timingsWriter.write(key + " = , " + properties.getProperty((String) key) + ", ");
+ }
+
+ timingsWriter.write("\n\n");
+ // timingsWriter.flush();
+ }
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to write out test parameters: " + e, e);
+ }
+
+ // Write out the column headers after the properties.
+ // writeColumnHeaders();
+ }
+
+ /**
+ * Writes out and flushes the column headers for raw test data.
+ */
+ private void writeColumnHeaders()
+ {
+ // Write the column headers for the CSV file. Any IO exceptions are ignored.
+ try
+ {
+ timingsWriter.write("Class, ");
+ timingsWriter.write("Method, ");
+ timingsWriter.write("Thread, ");
+ timingsWriter.write("Test Outcome, ");
+ timingsWriter.write("Time (milliseconds), ");
+ timingsWriter.write("Memory Used (bytes), ");
+ timingsWriter.write("Concurrency level, ");
+ timingsWriter.write("Test Size\n");
+
+ timingsWriter.flush();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to write out column headers: " + e, e);
+ }
+ }
+
+ /**
+ * Writes out the test results for the specified test. This outputs a single line of results to the csv file.
+ *
+ * @param r The test results to write out.
+ * @param test The test to write them out for.
+ */
+ private void writeTestResults(TestResult r, Test test)
+ {
+ // Update the running stats for this batch.
+ if ("Error".equals(r.testState))
+ {
+ numError++;
+ }
+ else if ("Failure".equals(r.testState))
+ {
+ numFailed++;
+ }
+ else if ("Pass".equals(r.testState))
+ {
+ numPassed++;
+ }
+
+ totalTests++;
+
+ // Write the test name and thread information plus all instrumenation a line of the CSV ouput. Any IO
+ // exceptions are ignored.
+ try
+ {
+ synchronized (this.getClass())
+ {
+ timingsWriter.write(test.getClass().getName() + ", ");
+ timingsWriter.write(((test instanceof TestCase) ? ((TestCase) test).getName() : "") + ", ");
+ timingsWriter.write(Thread.currentThread().getName() + ", ");
+ timingsWriter.write(r.testState + ", ");
+ timingsWriter.write((((float) r.testTime) / 1000000f) + ", ");
+ timingsWriter.write((r.testEndMem - r.testStartMem) + ", ");
+ timingsWriter.write(r.testConcurrency + ", ");
+ timingsWriter.write(r.testParam + "\n");
+ }
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to write out test results: " + e, e);
+ }
+ }
+
+ /**
+ * Supplies the shutdown hook. This attempts to flush the results in the event of the test runner being prematurely
+ * suspended before the end of the current test batch.
+ *
+ * @return The shut down hook.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ log.debug("CSVTestListener::ShutdownHook: called");
+
+ // Complete the current test batch stats.
+ endBatch(TestContextProperties.getInstance());
+ }
+ });
+ }
+
+ /** Captures test results packaged into a single object, so that it can be set up as a thread local. */
+ private static class TestResult
+ {
+ /** Used to hold the test timing. */
+ public long testTime;
+
+ /** Used to hold the test start memory usage. */
+ public long testStartMem;
+
+ /** Used to hold the test end memory usage. */
+ public long testEndMem;
+
+ /** Used to hold the test pass/fail/error state. */
+ public String testState = "Pass";
+
+ /** Used to hold the test parameter value. */
+ public int testParam;
+
+ /** Used to hold the concurrency level under which the test was run. */
+ public int testConcurrency;
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java
new file mode 100644
index 0000000000..5c328a8814
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java
@@ -0,0 +1,264 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+import org.apache.qpid.junit.extensions.SleepThrottle;
+import org.apache.qpid.junit.extensions.Throttle;
+
+import java.util.Properties;
+
+/**
+ * ConsoleTestListener provides feedback to the console, as test timings are taken, by drawing a '.', or an 'E', or an
+ * 'F', for each test that passes, is in error or fails. It does this for every test result registered with the framework,
+ * not just on the completion of each test method as the JUnit one does. It also uses a throttle to cap the rate of
+ * dot drawing, as exessively high rates can degrade test performance without providing much usefull feedback to the user.
+ * Unlike the JUnit dot drawing feedback, this one will correctly wrap lines when tests are run concurrently (the
+ * rate capping ensures that this does not become a hot-spot for thread contention).
+ *
+ * <p/>Where rate capping causes the conflation of multiple requested dots into a single dot, the dot that is actually
+ * drawn will be the worst result within the conflation period, that is, error is worse than fail which is worse than pass.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Draw dots as each test result completes, at a capped rate.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ConsoleTestListener implements TestListener, TKTestListener
+{
+ /** Used to indicate a test pass. */
+ private static final int PASS = 1;
+
+ /** Used to indicate a test failure. */
+ private static final int FAIL = 2;
+
+ /** Used to indicate a test error. */
+ private static final int ERROR = 3;
+
+ /** Defines the maximum number of columns of dots to print. */
+ private static final int MAX_COLUMNS = 80;
+
+ /** Used to throttle the dot writing rate. */
+ Throttle throttle;
+
+ /** Tracks the worst test result so far, when the throttled print method must conflate results due to throttling. */
+ private int conflatedResult = 0;
+
+ /** Tracks the column count as dots are printed, so that newlines can be inserted at the right margin. */
+ private int columnCount = 0;
+
+ /** Used as a monitor on the print method criticial section, to ensure that line ends always happen in the right place. */
+ private final Object printMonitor = new Object();
+
+ /**
+ * Creates a dot drawing feedback test listener, set by default to 80 columns at 80 dots per second capped rate.
+ */
+ public ConsoleTestListener()
+ {
+ throttle = new SleepThrottle();
+ throttle.setRate(80f);
+ }
+
+ /**
+ * Prints dots at a capped rate, conflating the requested type of dot to draw if this method is called at a rate
+ * higher than the capped rate. The conflation works by always printing the worst result that occurs within the
+ * conflation period, that is, error is worse than fail which is worse than a pass.
+ *
+ * @param result The type of dot to draw, {@link #PASS}, {@link #FAIL} or {@link #ERROR}.
+ */
+ private void throttledPrint(int result)
+ {
+ conflatedResult = (result > conflatedResult) ? result : conflatedResult;
+
+ if (throttle.checkThrottle())
+ {
+ synchronized (printMonitor)
+ {
+ switch (conflatedResult)
+ {
+ default:
+ case PASS:
+ System.out.print('.');
+ break;
+
+ case FAIL:
+ System.out.print('F');
+ break;
+
+ case ERROR:
+ System.out.print('E');
+ break;
+ }
+
+ columnCount = (columnCount >= MAX_COLUMNS) ? 0 : (columnCount + 1);
+
+ if (columnCount == 0)
+ {
+ System.out.print('\n');
+ }
+
+ conflatedResult = 0;
+ }
+ }
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param test The test in error. Ignored.
+ * @param t The error that the test threw. Ignored.
+ */
+ public void addError(Test test, Throwable t)
+ {
+ throttledPrint(ERROR);
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param test The test that failed. Ignored.
+ * @param t The assertion failure that the test threw. Ignored.
+ */
+ public void addFailure(Test test, AssertionFailedError t)
+ {
+ throttledPrint(FAIL);
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param test The test that ended. Ignored.
+ */
+ public void endTest(Test test)
+ {
+ throttledPrint(PASS);
+ }
+
+ /**
+ * A test started.
+ *
+ * @param test The test that started. Ignored.
+ */
+ public void startTest(Test test)
+ { }
+
+ /**
+ * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
+ *
+ * @param test The test to resest any results for.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void reset(Test test, Long threadId)
+ { }
+
+ /**
+ * Should be called every time a test completes with the run time of that test.
+ *
+ * @param test The name of the test.
+ * @param nanos The run time of the test in nanoseconds.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void timing(Test test, long nanos, Long threadId)
+ { }
+
+ /**
+ * Should be called every time a test completed with the amount of memory used before and after the test was run.
+ *
+ * @param test The test which memory was measured for.
+ * @param memStart The total JVM memory used before the test was run.
+ * @param memEnd The total JVM memory used after the test was run.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
+ { }
+
+ /**
+ * Should be called every time a parameterized test completed with the int value of its test parameter.
+ *
+ * @param test The test which memory was measured for.
+ * @param parameter The int parameter value.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void parameterValue(Test test, int parameter, Long threadId)
+ { }
+
+ /**
+ * Should be called every time a test completes with the current number of test threads running.
+ *
+ * @param test The test for which the measurement is being generated.
+ * @param threads The number of tests being run concurrently.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void concurrencyLevel(Test test, int threads, Long threadId)
+ { }
+
+ /**
+ * Called when a test completes. Success, failure and errors. This method should be used when registering an
+ * end test from a different thread than the one that started the test.
+ *
+ * @param test The test which completed.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void endTest(Test test, Long threadId)
+ {
+ throttledPrint(PASS);
+ }
+
+ /**
+ * Called when a test completes to mark it as a test fail. This method should be used when registering a
+ * failure from a different thread than the one that started the test.
+ *
+ * @param test The test which failed.
+ * @param e The assertion that failed the test.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void addFailure(Test test, AssertionFailedError e, Long threadId)
+ {
+ throttledPrint(FAIL);
+ }
+
+ /**
+ * Notifies listeners of the start of a complete run of tests.
+ */
+ public void startBatch()
+ { }
+
+ /**
+ * Notifies listeners of the end of a complete run of tests.
+ *
+ * @param parameters The optional test parameters to log out with the batch results.
+ */
+ public void endBatch(Properties parameters)
+ { }
+
+ /**
+ * Notifies listeners of the tests read/set properties.
+ *
+ * @param properties The tests read/set properties.
+ */
+ public void properties(Properties properties)
+ { }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java
new file mode 100644
index 0000000000..4f08e8bf2d
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.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.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+import java.util.Properties;
+
+/**
+ * TKTestListener is a listener interface for listeners that want to be informed of the run times of tests, the memory
+ * usage of tests, the 'size' parameters of parameterized tests and the begin and end events of complete test runs.
+ * {@link org.apache.qpid.junit.extensions.TKTestResult} is an example of a test result class that listeners
+ * interested in these events can be attached to.
+ *
+ * The {@link #timing(junit.framework.Test, long, Long)}, {@link #memoryUsed(junit.framework.Test, long, long, Long)},
+ * {@link #parameterValue(junit.framework.Test, int, Long)} and {@link #endTest(junit.framework.Test, Long)} methods
+ * all accept on optional thread id parameter.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Listen to test timings.
+ * <tr><td> Listen to test memory usages.
+ * <tr><td> Listen to parameterized test parameters.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public interface TKTestListener extends TestListener
+{
+ /**
+ * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
+ *
+ * @param test The test to resest any results for.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void reset(Test test, Long threadId);
+
+ /**
+ * Should be called every time a test completes with the run time of that test.
+ *
+ * @param test The name of the test.
+ * @param nanos The run time of the test in nanoseconds.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void timing(Test test, long nanos, Long threadId);
+
+ /**
+ * Should be called every time a test completed with the amount of memory used before and after the test was run.
+ *
+ * @param test The test which memory was measured for.
+ * @param memStart The total JVM memory used before the test was run.
+ * @param memEnd The total JVM memory used after the test was run.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void memoryUsed(Test test, long memStart, long memEnd, Long threadId);
+
+ /**
+ * Should be called every time a parameterized test completed with the int value of its test parameter.
+ *
+ * @param test The test which memory was measured for.
+ * @param parameter The int parameter value.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void parameterValue(Test test, int parameter, Long threadId);
+
+ /**
+ * Should be called every time a test completes with the current number of test threads running.
+ *
+ * @param test The test for which the measurement is being generated.
+ * @param threads The number of tests being run concurrently.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void concurrencyLevel(Test test, int threads, Long threadId);
+
+ /**
+ * Called when a test completes. Success, failure and errors. This method should be used when registering an
+ * end test from a different thread than the one that started the test.
+ *
+ * @param test The test which completed.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void endTest(Test test, Long threadId);
+
+ /**
+ * Called when a test completes to mark it as a test fail. This method should be used when registering a
+ * failure from a different thread than the one that started the test.
+ *
+ * @param test The test which failed.
+ * @param e The assertion that failed the test.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void addFailure(Test test, AssertionFailedError e, Long threadId);
+
+ /**
+ * Notifies listeners of the start of a complete run of tests.
+ */
+ public void startBatch();
+
+ /**
+ * Notifies listeners of the end of a complete run of tests.
+ *
+ * @param parameters The optional test parameters to log out with the batch results.
+ */
+ public void endBatch(Properties parameters);
+
+ /**
+ * Notifies listeners of the tests read/set properties.
+ *
+ * @param properties The tests read/set properties.
+ */
+ public void properties(Properties properties);
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java
new file mode 100644
index 0000000000..a88837e323
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java
@@ -0,0 +1,400 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.junit.extensions.ShutdownHookable;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.*;
+
+/**
+ * Listens for test results for a named test and outputs these in the standard JUnit XML format to the specified
+ * writer.
+ *
+ * <p/>The API for this listener accepts notifications about different aspects of a tests results through different
+ * methods, so some assumption needs to be made as to which test result a notification refers to. For example
+ * {@link #startTest} will be called, then possibly {@link #timing} will be called, even though the test instance is
+ * passed in both cases, it is not enough to distinguish a particular run of the test, as the test case instance may
+ * be being shared between multiple threads, or being run a repeated number of times, and can therfore be re-used
+ * between calls. The listeners make the assumption that, for every test, a unique thread will call {@link #startTest}
+ * and {@link #endTest} to delimit each test. All calls to set test parameters, timings, state and so on, will occur
+ * between the start and end and will be given with the same thread id as the start and end, so the thread id provides
+ * a unqiue value to identify a particular test run against.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Listen to test lifecycle notifications.
+ * <tr><td> Listen to test errors and failures.
+ * <tr><td> Listen to test timings.
+ * <tr><td> Listen to test memory usages.
+ * <tr><td> Listen to parameterized test parameters.
+ * <tr><th> Responsibilities
+ * </table>
+ *
+ * @todo Merge this class with CSV test listener, making the collection of results common to both, and only factoring
+ * out the results printing code into sub-classes. Provide a simple XML results formatter with the same format as
+ * the ant XML formatter, and a more structured one for outputing results with timings and summaries from
+ * performance tests.
+ *
+ * @author Rupert Smith
+ */
+public class XMLTestListener implements TKTestListener, ShutdownHookable
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(XMLTestListener.class);
+
+ /** The results file writer. */
+ protected Writer writer;
+
+ /** Holds the results for individual tests. */
+ // protected Map<Result, Result> results = new LinkedHashMap<Result, Result>();
+ // protected List<Result> results = new ArrayList<Result>();
+
+ /**
+ * Map for holding results on a per thread basis as they come in. A ThreadLocal is not used as sometimes an
+ * explicit thread id must be used, where notifications come from different threads than the ones that called
+ * the test method.
+ */
+ Map<Long, Result> threadLocalResults = Collections.synchronizedMap(new LinkedHashMap<Long, Result>());
+
+ /**
+ * Holds results for tests that have ended. Transferring these results here from the per-thread results map, means
+ * that the thread id is freed for the thread to generate more results.
+ */
+ List<Result> results = new ArrayList<Result>();
+
+ /** Holds the overall error count. */
+ protected int errors = 0;
+
+ /** Holds the overall failure count. */
+ protected int failures = 0;
+
+ /** Holds the overall tests run count. */
+ protected int runs = 0;
+
+ /** Holds the name of the class that tests are being run for. */
+ String testClassName;
+
+ /**
+ * Creates a new XML results output listener that writes to the specified location.
+ *
+ * @param writer The location to write results to.
+ * @param testClassName The name of the test class to include in the test results.
+ */
+ public XMLTestListener(Writer writer, String testClassName)
+ {
+ log.debug("public XMLTestListener(Writer writer, String testClassName = " + testClassName + "): called");
+
+ this.writer = writer;
+ this.testClassName = testClassName;
+ }
+
+ /**
+ * Resets the test results to the default state of time zero, memory usage zero, parameter zero, test passed.
+ *
+ * @param test The test to resest any results for.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void reset(Test test, Long threadId)
+ {
+ log.debug("public void reset(Test test = " + test + ", Long threadId = " + threadId + "): called");
+
+ XMLTestListener.Result r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+
+ r.error = null;
+ r.failure = null;
+
+ }
+
+ /**
+ * Notification that a test started.
+ *
+ * @param test The test that started.
+ */
+ public void startTest(Test test)
+ {
+ log.debug("public void startTest(Test test = " + test + "): called");
+
+ Result newResult = new Result(test.getClass().getName(), ((TestCase) test).getName());
+
+ // Initialize the thread local test results.
+ threadLocalResults.put(Thread.currentThread().getId(), newResult);
+ runs++;
+ }
+
+ /**
+ * Should be called every time a test completes with the run time of that test.
+ *
+ * @param test The name of the test.
+ * @param nanos The run time of the test in nanoseconds.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void timing(Test test, long nanos, Long threadId)
+ { }
+
+ /**
+ * Should be called every time a test completed with the amount of memory used before and after the test was run.
+ *
+ * @param test The test which memory was measured for.
+ * @param memStart The total JVM memory used before the test was run.
+ * @param memEnd The total JVM memory used after the test was run.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void memoryUsed(Test test, long memStart, long memEnd, Long threadId)
+ { }
+
+ /**
+ * Should be called every time a parameterized test completed with the int value of its test parameter.
+ *
+ * @param test The test which memory was measured for.
+ * @param parameter The int parameter value.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void parameterValue(Test test, int parameter, Long threadId)
+ { }
+
+ /**
+ * Should be called every time a test completes with the current number of test threads running.
+ *
+ * @param test The test for which the measurement is being generated.
+ * @param threads The number of tests being run concurrently.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void concurrencyLevel(Test test, int threads, Long threadId)
+ { }
+
+ /**
+ * Notifies listeners of the tests read/set properties.
+ *
+ * @param properties The tests read/set properties.
+ */
+ public void properties(Properties properties)
+ { }
+
+ /**
+ * Notification that a test ended.
+ *
+ * @param test The test that ended.
+ */
+ public void endTest(Test test)
+ {
+ log.debug("public void endTest(Test test = " + test + "): called");
+
+ // Move complete test results into the completed tests list.
+ Result r = threadLocalResults.get(Thread.currentThread().getId());
+ results.add(r);
+
+ // Clear all the test results for the thread.
+ threadLocalResults.remove(Thread.currentThread().getId());
+ }
+
+ /**
+ * Called when a test completes. Success, failure and errors. This method should be used when registering an
+ * end test from a different thread than the one that started the test.
+ *
+ * @param test The test which completed.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void endTest(Test test, Long threadId)
+ {
+ log.debug("public void endTest(Test test = " + test + ", Long threadId = " + threadId + "): called");
+
+ // Move complete test results into the completed tests list.
+ Result r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+ results.add(r);
+
+ // Clear all the test results for the thread.
+ threadLocalResults.remove(Thread.currentThread().getId());
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param test The test in which the error occurred.
+ * @param t The throwable that resulted from the error.
+ */
+ public void addError(Test test, Throwable t)
+ {
+ log.debug("public void addError(Test test = " + test + ", Throwable t = " + t + "): called");
+
+ Result r = threadLocalResults.get(Thread.currentThread().getId());
+ r.error = t;
+ errors++;
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param test The test in which the failure occurred.
+ * @param t The JUnit assertions that led to the failure.
+ */
+ public void addFailure(Test test, AssertionFailedError t)
+ {
+ log.debug("public void addFailure(Test test = " + test + ", AssertionFailedError t = " + t + "): called");
+
+ Result r = threadLocalResults.get(Thread.currentThread().getId());
+ r.failure = t;
+ failures++;
+ }
+
+ /**
+ * Called when a test completes to mark it as a test fail. This method should be used when registering a
+ * failure from a different thread than the one that started the test.
+ *
+ * @param test The test which failed.
+ * @param e The assertion that failed the test.
+ * @param threadId Optional thread id if not calling from thread that started the test method. May be null.
+ */
+ public void addFailure(Test test, AssertionFailedError e, Long threadId)
+ {
+ log.debug("public void addFailure(Test test, AssertionFailedError e, Long threadId): called");
+
+ Result r =
+ (threadId == null) ? threadLocalResults.get(Thread.currentThread().getId()) : threadLocalResults.get(threadId);
+ r.failure = e;
+ failures++;
+ }
+
+ /**
+ * Notifies listeners of the start of a complete run of tests.
+ */
+ public void startBatch()
+ {
+ log.debug("public void startBatch(): called");
+
+ // Reset all results counts.
+ threadLocalResults = Collections.synchronizedMap(new HashMap<Long, Result>());
+ errors = 0;
+ failures = 0;
+ runs = 0;
+
+ // Write out the file header.
+ try
+ {
+ writer.write("<?xml version=\"1.0\" ?>\n");
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to write the test results.", e);
+ }
+ }
+
+ /**
+ * Notifies listeners of the end of a complete run of tests.
+ *
+ * @param parameters The optional test parameters to log out with the batch results.
+ */
+ public void endBatch(Properties parameters)
+ {
+ log.debug("public void endBatch(Properties parameters = " + parameters + "): called");
+
+ // Write out the results.
+ try
+ {
+ // writer.write("<?xml version=\"1.0\" ?>\n");
+ writer.write("<testsuite errors=\"" + errors + "\" failures=\"" + failures + "\" tests=\"" + runs + "\" name=\""
+ + testClassName + "\">\n");
+
+ for (Result result : results)
+ {
+ writer.write(" <testcase classname=\"" + result.testClass + "\" name=\"" + result.testName + "\">\n");
+
+ if (result.error != null)
+ {
+ writer.write(" <error type=\"" + result.error.getClass() + "\">");
+ result.error.printStackTrace(new PrintWriter(writer));
+ writer.write(" </error>");
+ }
+ else if (result.failure != null)
+ {
+ writer.write(" <failure type=\"" + result.failure.getClass() + "\">");
+ result.failure.printStackTrace(new PrintWriter(writer));
+ writer.write(" </failure>");
+ }
+
+ writer.write(" </testcase>\n");
+ }
+
+ writer.write("</testsuite>\n");
+ writer.flush();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Unable to write the test results.", e);
+ }
+ }
+
+ /**
+ * Supplies the shutdown hook.
+ *
+ * @return The shut down hook.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ log.debug("XMLTestListener::ShutdownHook: called");
+ }
+ });
+ }
+
+ /**
+ * Used to capture the results of a particular test run.
+ */
+ protected static class Result
+ {
+ /** Holds the name of the test class. */
+ public String testClass;
+
+ /** Holds the name of the test method. */
+ public String testName;
+
+ /** Holds the exception that caused error in this test. */
+ public Throwable error;
+
+ /** Holds the assertion exception that caused failure in this test. */
+ public AssertionFailedError failure;
+
+ /**
+ * Creates a placeholder for the results of a test.
+ *
+ * @param testClass The test class.
+ * @param testName The name of the test that was run.
+ */
+ public Result(String testClass, String testName)
+ {
+ this.testClass = testClass;
+ this.testName = testName;
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html
new file mode 100644
index 0000000000..326d6e176e
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+Listners for test statistics are defined in this package. At the moment there is only one listener which writes all test
+statistics out to a CSV (comma seperated values) file which can be loaded by most spread sheets.
+</body>
+</html> \ No newline at end of file
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html
new file mode 100644
index 0000000000..091dcce08e
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html
@@ -0,0 +1,12 @@
+<html>
+<body>
+Basic JUnit is enahanced with test runners to run tests repeatedly, simultaneously in many threads and with increasing
+test sizes for asymptotic performance measurements. There are features to measure the time and amount of memory that
+tests use as well as to record the asymptotic test size parameters. There are some utilities to write these test
+statistics to various file formats too and these can be found in the listeners package.
+
+</p>The main test runner class is TKTestRunner which can be called with command line parameters to specify how tests
+should be run.
+
+</body>
+</html> \ No newline at end of file
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java
new file mode 100644
index 0000000000..61c58bf3ba
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java
@@ -0,0 +1,787 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.util;
+
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure
+ * that they fit their specified format. A command line is made up of flags and options, both may be refered to as
+ * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not
+ * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so
+ * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify
+ * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified.
+ *
+ * <p/>Some examples command line are:
+ *
+ * <ul>
+ * <li>This one has two options that expect arguments:
+ * <pre>
+ * cruisecontrol -configfile cruisecontrol.xml -port 9000
+ * </pre>
+ * <li>This has one no-arg flag and two 'free' arguments:
+ * <pre>
+ * zip -r project.zip project/*
+ * </pre>
+ * <li>This one concatenates multiple flags into a single block with only one '-':
+ * <pre>
+ * jar -tvf mytar.tar
+ * </pre>
+ *
+ * <p/>The parsing rules are:
+ *
+ * <ol>
+ * <li>Flags may be combined after a single '-' because they never take arguments. Normally such flags are single letter
+ * flags but this is only a convention and not enforced. Flags of more than one letter are usually specified on their own.
+ * <li>Options expecting arguments must always be on their own.
+ * <li>The argument to an option may be seperated from it by whitespace or appended directly onto the option.
+ * <li>The argument to an option may never begin with a '-' character.
+ * <li>All other arguments not beginning with a '-' character are free arguments that do not belong to any option.
+ * <li>The second or later of a set of duplicate or repeated flags override earlier ones.
+ * <li>Options are matched up to the shortest matching option. This is because of the possibility of having no space
+ * between an option and its argument. This rules out the possibility of using two options where one is an opening
+ * substring of the other. For example, the options "foo" and "foobar" cannot be used on the same command line because
+ * it is not possible to distinguish the argument "-foobar" from being the "foobar" option or the "foo" option with
+ * the "bar" argument.
+ * </ol>
+ *
+ * <p/>By default, unknown options are simply ignored if specified on the command line. This behaviour may be changed
+ * so that the parser reports all unknowns as errors by using the {@link #setErrorsOnUnknowns} method.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept a command line specification.
+ * <tr><td> Parse a command line into properties, validating it against its specification.
+ * <tr><td> Report all errors between a command line and its specification.
+ * <tr><td> Provide a formatted usage string for a command line.
+ * <tr><td> Provide a formatted options in force string for a command line.
+ * <tr><td> Allow errors on unknowns behaviour to be turned on or off.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class CommandLineParser
+{
+ /**
+ * Holds a mapping from command line option names to detailed information about those options.
+ * Use of a tree map ensures that the options are easy to print in alphabetical order as a usage string.
+ * An alternative might be to use a LinkedHashMap to print them in the order they are specified.
+ */
+ private Map<String, CommandLineOption> optionMap = new TreeMap<String, CommandLineOption>();
+
+ /** Holds a list of parsing errors. */
+ private List<String> parsingErrors = new ArrayList<String>();
+
+ /** Holds the regular head matcher to match command line options with. */
+ private Matcher optionMatcher = null;
+
+ /** Holds the parsed command line properties after parsing. */
+ private Properties parsedProperties = null;
+
+ /** Holds any trailing name=value pairs specified in the free arguments. */
+ private Properties trailingProperties = null;
+
+ /** Flag used to indicate that errors should be created for unknown options. False by default. */
+ private boolean errorsOnUnknowns = false;
+
+ /**
+ * Creates a command line options parser from a command line specification. This is passed to this constructor
+ * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static
+ * array may therefore easily be used to configure the command line parser in a single method call with an easily
+ * readable format.
+ *
+ * <p/>Each array of strings must be 2, 3, 4 or 5 elements long. If any of the last three elements are missing they
+ * are assumed to be null. The elements specify the following parameters:
+ * <ol>
+ * <li>The name of the option without the leading '-'. For example, "file". To specify the format of the 'free'
+ * arguments use the option names "1", "2", ... and so on.
+ * <li>The option comment. A line of text describing the usage of the option. For example, "The file to be processed."
+ * <li>The options argument. This is a very short description of the argument to the option, often a single word
+ * or a reminder as to the arguments format. When this element is null the option is a flag and does not
+ * accept any arguments. For example, "filename" or "(unix | windows)" or null. The actual text specified
+ * is only used to print in the usage message to remind the user of the usage of the option.
+ * <li>The mandatory flag. When set to "true" an option must always be specified. Any other value, including null,
+ * means that the option is mandatory. Flags are always mandatory (see class javadoc for explanation of why) so
+ * this is ignored for flags.
+ * <li>A regular head describing the format that the argument must take. Ignored if null.
+ * </ol>
+ * <p/>An example call to this constructor is:
+ *
+ * <pre>
+ * CommandLineParser commandLine = new CommandLineParser(
+ * new String[][] {{"file", "The file to be processed. ", "filename", "true"},
+ * {"dir", "Directory to store results in. Current dir used if not set.", "out dir"},
+ * {"os", "Operating system EOL format to use.", "(windows | unix)", null, "windows\|unix"},
+ * {"v", "Verbose mode. Prints information about the processing as it goes."},
+ * {"1", "The processing command to run.", "command", "true", "add\|remove\|list"}});
+ * </pre>
+ *
+ * @param config The configuration as an array of arrays of strings.
+ */
+ public CommandLineParser(String[][] config)
+ {
+ // Loop through all the command line option specifications creating details for each in the options map.
+ for (String[] nextOptionSpec : config)
+ {
+ addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null,
+ (nextOptionSpec.length > 3) && ("true".equals(nextOptionSpec[3])),
+ (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null);
+ }
+ }
+
+ /**
+ * Extracts all name=value pairs from the command line, sets them all as system properties and also returns
+ * a map of properties containing them.
+ *
+ * @param args The command line.
+ * @param commandLine The command line parser.
+ * @param properties The properties object to inject all parsed properties into (optional may be <tt>null</tt>).
+ *
+ * @return A set of properties containing all name=value pairs from the command line.
+ */
+ public static Properties processCommandLine(String[] args, CommandLineParser commandLine, Properties properties)
+ {
+ // Capture the command line arguments or display errors and correct usage and then exit.
+ Properties options = null;
+
+ try
+ {
+ options = commandLine.parseCommandLine(args);
+
+ // Add all the command line options and trailing settings to properties if the optional properties object
+ // to copy them into has been set.
+ if (properties != null)
+ {
+ commandLine.addTrailingPairsToProperties(properties);
+ commandLine.addOptionsToProperties(properties);
+ }
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(1);
+ }
+
+ return options;
+ }
+
+ /**
+ * Lists all the parsing errors from the most recent parsing in a string.
+ *
+ * @return All the parsing errors from the most recent parsing.
+ */
+ public String getErrors()
+ {
+ // Return the empty string if there are no errors.
+ if (parsingErrors.isEmpty())
+ {
+ return "";
+ }
+
+ // Concatenate all the parsing errors together.
+ String result = "";
+
+ for (String s : parsingErrors)
+ {
+ result += s;
+ }
+
+ return result;
+ }
+
+ /**
+ * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ *
+ * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ */
+ public String getOptionsInForce()
+ {
+ // Check if there are no properties to report and return and empty string if so.
+ if (parsedProperties == null)
+ {
+ return "";
+ }
+
+ // List all the properties.
+ String result = "Options in force:\n";
+
+ for (Map.Entry<Object, Object> property : parsedProperties.entrySet())
+ {
+ result += property.getKey() + " = " + property.getValue() + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Generates a usage string consisting of the name of each option and each options argument description and
+ * comment.
+ *
+ * @return A usage string for all the options.
+ */
+ public String getUsage()
+ {
+ String result = "Options:\n";
+
+ int optionWidth = 0;
+ int argumentWidth = 0;
+
+ // Calculate the column widths required for aligned layout.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ int oWidth = optionInfo.option.length();
+ int aWidth = (optionInfo.argument != null) ? (optionInfo.argument.length()) : 0;
+
+ optionWidth = (oWidth > optionWidth) ? oWidth : optionWidth;
+ argumentWidth = (aWidth > argumentWidth) ? aWidth : argumentWidth;
+ }
+
+ // Print usage on each of the command line options.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ String argString = ((optionInfo.argument != null) ? (optionInfo.argument) : "");
+ String optionString = optionInfo.option;
+
+ argString = rightPad(argString, " ", argumentWidth);
+ optionString = rightPad(optionString, " ", optionWidth);
+
+ result += "-" + optionString + " " + argString + " " + optionInfo.comment + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Right pads a string with a given string to a given size. This method will repeat the padder string as many
+ * times as is necessary until the exact specified size is reached. If the specified size is less than the size
+ * of the original string then the original string is returned unchanged.
+ *
+ * <pre>
+ * Example1 - original string "cat", padder string "white", size 8 gives "catwhite".
+ * Example2 - original string "cat", padder string "white", size 15 gives "catwhitewhitewh".
+ * Example3 - original string "cat", padder string "white", size 2 gives "cat".
+ * </pre>
+ *
+ * @param stringToPad The original string.
+ * @param padder The string to pad onto the original string.
+ * @param size The required size of the new string.
+ *
+ * @return The newly padded string.
+ */
+ public static String rightPad(String stringToPad, String padder, int size)
+ {
+ if (padder.length() == 0)
+ {
+ return stringToPad;
+ }
+
+ StringBuffer strb = new StringBuffer(stringToPad);
+ StringCharacterIterator sci = new StringCharacterIterator(padder);
+
+ while (strb.length() < size)
+ {
+ for (char ch = sci.first(); ch != CharacterIterator.DONE; ch = sci.next())
+ {
+ if (strb.length() < size)
+ {
+ strb.append(String.valueOf(ch));
+ }
+ }
+ }
+
+ return strb.toString();
+ }
+
+ /**
+ * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options
+ * as errors. When turned off, all unknowns are simply ignored.
+ *
+ * @param errors The setting of the errors on unkown flag. True to turn it on.
+ */
+ public void setErrorsOnUnknowns(boolean errors)
+ {
+ errorsOnUnknowns = errors;
+ }
+
+ /**
+ * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments
+ * are keyed by integers as strings starting at "1" and then "2", ... and so on.
+ *
+ * <p/>See the class level comment for a description of the parsing rules.
+ *
+ * @param args The command line arguments.
+ *
+ * @return The arguments as a set of properties.
+ *
+ * @throws IllegalArgumentException If the command line cannot be parsed against its specification. If this exception
+ * is thrown a call to {@link #getErrors} will provide a diagnostic of the command
+ * line errors.
+ */
+ public Properties parseCommandLine(String[] args) throws IllegalArgumentException
+ {
+ Properties options = new Properties();
+
+ // Used to keep count of the current 'free' argument.
+ int free = 1;
+
+ // Used to indicate that the most recently parsed option is expecting arguments.
+ boolean expectingArgs = false;
+
+ // The option that is expecting arguments from the next element of the command line.
+ String optionExpectingArgs = null;
+
+ // Used to indicate that the most recently parsed option is a duplicate and should be ignored.
+ // boolean ignore = false;
+
+ // Create the regular head matcher for the command line options.
+ String regexp = "^(";
+ int optionsAdded = 0;
+
+ for (Iterator<String> i = optionMap.keySet().iterator(); i.hasNext();)
+ {
+ String nextOption = i.next();
+
+ // Check that the option is not a free argument definition.
+ boolean notFree = false;
+
+ try
+ {
+ Integer.parseInt(nextOption);
+ }
+ catch (NumberFormatException e)
+ {
+ notFree = true;
+ }
+
+ // Add the option to the regular head matcher if it is not a free argument definition.
+ if (notFree)
+ {
+ regexp += nextOption + (i.hasNext() ? "|" : "");
+ optionsAdded++;
+ }
+ }
+
+ // There has to be more that one option in the regular head or else the compiler complains that the close
+ // cannot be nullable if the '?' token is used to make the matched option string optional.
+ regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)";
+ Pattern pattern = Pattern.compile(regexp);
+
+ // Loop through all the command line arguments.
+ for (String arg1 : args)
+ {
+ // Check if the next command line argument begins with a '-' character and is therefore the start of
+ // an option.
+ if (arg1.startsWith("-"))
+ {
+ // Extract the value of the option without the leading '-'.
+ String arg = arg1.substring(1);
+
+ // Match up to the longest matching option.
+ optionMatcher = pattern.matcher(arg);
+ optionMatcher.matches();
+
+ String matchedOption = optionMatcher.group(1);
+
+ // Match any argument directly appended onto the longest matching option.
+ String matchedArg = optionMatcher.group(2);
+
+ // Check that a known option was matched.
+ if ((matchedOption != null) && !"".equals(matchedOption))
+ {
+ // Get the command line option information for the matched option.
+ CommandLineOption optionInfo = optionMap.get(matchedOption);
+
+ // Check if this option is expecting arguments.
+ if (optionInfo.expectsArgs)
+ {
+ // The option is expecting arguments so swallow the next command line argument as an
+ // argument to this option.
+ expectingArgs = true;
+ optionExpectingArgs = matchedOption;
+
+ // In the mean time set this options argument to the empty string in case no argument is ever
+ // supplied.
+ // options.put(matchedOption, "");
+ }
+
+ // Check if the option was matched on its own and is a flag in which case set that flag.
+ if ("".equals(matchedArg) && !optionInfo.expectsArgs)
+ {
+ options.put(matchedOption, "true");
+ }
+ // The option was matched as a substring with its argument appended to it or is a flag that is
+ // condensed together with other flags.
+ else if (!"".equals(matchedArg))
+ {
+ // Check if the option is a flag and therefore is allowed to be condensed together
+ // with other flags.
+ if (!optionInfo.expectsArgs)
+ {
+ // Set the first matched flag.
+ options.put(matchedOption, "true");
+
+ // Repeat the longest matching process on the remainder but ensure that the remainder
+ // consists only of flags as only flags may be condensed together in this fashion.
+ do
+ {
+ // Match the remainder against the options.
+ optionMatcher = pattern.matcher(matchedArg);
+ optionMatcher.matches();
+
+ matchedOption = optionMatcher.group(1);
+ matchedArg = optionMatcher.group(2);
+
+ // Check that an option was matched.
+ if (matchedOption != null)
+ {
+ // Get the command line option information for the next matched option.
+ optionInfo = optionMap.get(matchedOption);
+
+ // Ensure that the next option is a flag or raise an error if not.
+ if (optionInfo.expectsArgs)
+ {
+ parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n");
+ }
+
+ options.put(matchedOption, "true");
+ }
+ // The remainder could not be matched against a flag it is either an unknown flag
+ // or an illegal argument to a flag.
+ else
+ {
+ parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n");
+
+ break;
+ }
+ }
+ // Continue until the remainder of the argument has all been matched with flags.
+ while (!"".equals(matchedArg));
+ }
+ // The option is expecting an argument, so store the unmatched portion against it
+ // as its argument.
+ else
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, matchedArg);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(matchedOption, matchedArg);
+
+ // The argument to this flag has already been supplied to it. Do not swallow the
+ // next command line argument as an argument to this flag.
+ expectingArgs = false;
+ }
+ }
+ }
+ else // No matching option was found.
+ {
+ // Add this to the list of parsing errors if errors on unkowns is being used.
+ if (errorsOnUnknowns)
+ {
+ parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n");
+ }
+ }
+ }
+ // The command line argument did not being with a '-' so it is an argument to the previous flag or it
+ // is a free argument.
+ else
+ {
+ // Check if a previous flag is expecting to swallow this next argument as its argument.
+ if (expectingArgs)
+ {
+ // Get the option info for the option waiting for arguments.
+ CommandLineOption optionInfo = optionMap.get(optionExpectingArgs);
+
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, arg1);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(optionExpectingArgs, arg1);
+
+ // Clear the expecting args flag now that the argument has been swallowed.
+ expectingArgs = false;
+ optionExpectingArgs = null;
+ }
+ // This command line option is not an argument to any option. Add it to the set of 'free' options.
+ else
+ {
+ // Get the option info for the free option, if there is any.
+ CommandLineOption optionInfo = optionMap.get(Integer.toString(free));
+
+ if (optionInfo != null)
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, arg1);
+ }
+
+ // Add to the list of free options.
+ options.put(Integer.toString(free), arg1);
+
+ // Move on to the next free argument.
+ free++;
+ }
+ }
+ }
+
+ // Scan through all the specified options to check that all mandatory options have been set and that all flags
+ // that were not set are set to false in the set of properties.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ // Check if this is a flag.
+ if (!optionInfo.expectsArgs)
+ {
+ // Check if the flag is not set in the properties and set it to false if so.
+ if (!options.containsKey(optionInfo.option))
+ {
+ options.put(optionInfo.option, "false");
+ }
+ }
+ // Check if this is a mandatory option and was not set.
+ else if (optionInfo.mandatory && !options.containsKey(optionInfo.option))
+ {
+ // Create an error for the missing option.
+ parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n");
+ }
+ }
+
+ // Check if there were any errors.
+ if (!parsingErrors.isEmpty())
+ {
+ // Throw an illegal argument exception to signify that there were parsing errors.
+ throw new IllegalArgumentException();
+ }
+
+ // Convert any name/value pairs in the free arguments into properties in the parsed options.
+ trailingProperties = takeFreeArgsAsProperties(options, 1);
+
+ parsedProperties = options;
+
+ return options;
+ }
+
+ /**
+ * If a command line has been parsed, calling this method sets all of its free arguments that were name=value pairs
+ * on the specified properties.
+ *
+ * @param properties The property set to add the name=value pairs to.
+ */
+ public void addTrailingPairsToProperties(Properties properties)
+ {
+ if (trailingProperties != null)
+ {
+ for (Object propKey : trailingProperties.keySet())
+ {
+ String name = (String) propKey;
+ String value = trailingProperties.getProperty(name);
+
+ properties.setProperty(name, value);
+ }
+ }
+ }
+
+ /**
+ * If a command line has been parsed, calling this method sets all of its options that were set to the specified
+ * properties.
+ *
+ * @param properties The property set to the options to.
+ */
+ public void addOptionsToProperties(Properties properties)
+ {
+ if (parsedProperties != null)
+ {
+ for (Object propKey : parsedProperties.keySet())
+ {
+ String name = (String) propKey;
+ String value = parsedProperties.getProperty(name);
+
+ // This filters out all trailing items.
+ if (!name.matches("^[0-9]+$"))
+ {
+ properties.setProperty(name, value);
+ }
+ }
+ }
+ }
+
+ /**
+ * Resets this command line parser after it has been used to parse a command line. This method will only need
+ * to be called to use this parser a second time which is not likely seeing as a command line is usually only
+ * specified once. However, it is exposed as a public method for the rare case where this may be done.
+ *
+ * <p/>Cleans the internal state of this parser, removing all stored errors and information about the options in
+ * force.
+ */
+ public void reset()
+ {
+ parsingErrors = new ArrayList<String>();
+ parsedProperties = null;
+ }
+
+ /**
+ * Adds the option to list of available command line options.
+ *
+ * @param option The option to add as an available command line option.
+ * @param comment A comment for the option.
+ * @param argument The text that appears after the option in the usage string.
+ * @param mandatory When true, indicates that this option is mandatory.
+ * @param formatRegexp The format that the argument must take, defined as a regular head.
+ */
+ protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp)
+ {
+ // Check if usage text has been set in which case this option is expecting arguments.
+ boolean expectsArgs = (!((argument == null) || argument.equals("")));
+
+ // Add the option to the map of command line options.
+ CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp);
+ optionMap.put(option, opt);
+ }
+
+ /**
+ * Converts the free arguments into property declarations. After parsing the command line the free arguments
+ * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method
+ * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value
+ * 'value'.
+ *
+ * <p/>For example the comand line:
+ * <pre>
+ * ... debug=true
+ * </pre>
+ *
+ * <p/>After parsing has properties:
+ * <pre>[[1, debug=true]]</pre>
+ *
+ * <p/>After applying this method the properties are:
+ * <pre>[[1, debug=true], [debug, true]]</pre>
+ *
+ * @param properties The parsed command line properties.
+ * @param from The free argument index to convert to properties from.
+ *
+ * @return The parsed command line properties, with free argument name value pairs too.
+ */
+ private Properties takeFreeArgsAsProperties(Properties properties, int from)
+ {
+ Properties result = new Properties();
+
+ for (int i = from; true; i++)
+ {
+ String nextFreeArg = properties.getProperty(Integer.toString(i));
+
+ // Terminate the loop once all free arguments have been consumed.
+ if (nextFreeArg == null)
+ {
+ break;
+ }
+
+ // Split it on the =, strip any whitespace and set it as a system property.
+ String[] nameValuePair = nextFreeArg.split("=");
+
+ if (nameValuePair.length == 2)
+ {
+ result.setProperty(nameValuePair[0], nameValuePair[1]);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks the format of an argument to an option against its specified regular head format if one has
+ * been set. Any errors are added to the list of parsing errors.
+ *
+ * @param optionInfo The command line option information for the option which is havings its argument checked.
+ * @param matchedArg The string argument to the option.
+ */
+ private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg)
+ {
+ // Check if this option enforces a format for its argument.
+ if (optionInfo.argumentFormatRegexp != null)
+ {
+ Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp);
+ Matcher argumentMatcher = pattern.matcher(matchedArg);
+
+ // Check if the argument does not meet its required format.
+ if (!argumentMatcher.matches())
+ {
+ // Create an error for this badly formed argument.
+ parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n");
+ }
+ }
+ }
+
+ /**
+ * Holds information about a command line options. This includes what its name is, whether or not it is a flag,
+ * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its
+ * regular head format is.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Hold details of a command line option.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+ protected class CommandLineOption
+ {
+ /** Holds the text for the flag to match this argument with. */
+ public String option = null;
+
+ /** Holds a string describing how to use this command line argument. */
+ public String argument = null;
+
+ /** Flag that determines whether or not this command line argument can take arguments. */
+ public boolean expectsArgs = false;
+
+ /** Holds a short comment describing what this command line argument is for. */
+ public String comment = null;
+
+ /** Flag that determines whether or not this is an mandatory command line argument. */
+ public boolean mandatory = false;
+
+ /** A regular head describing what format the argument to this option muist have. */
+ public String argumentFormatRegexp = null;
+
+ /**
+ * Create a command line option object that holds specific information about a command line option.
+ *
+ * @param option The text that matches the option.
+ * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false.
+ * @param comment A comment explaining how to use this option.
+ * @param argument A short reminder of the format of the argument to this option/
+ * @param mandatory Set to true if this option is mandatory.
+ * @param formatRegexp The regular head that the argument to this option must meet to be valid.
+ */
+ public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory,
+ String formatRegexp)
+ {
+ this.option = option;
+ this.expectsArgs = expectsArgs;
+ this.comment = comment;
+ this.argument = argument;
+ this.mandatory = mandatory;
+ this.argumentFormatRegexp = formatRegexp;
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java
new file mode 100644
index 0000000000..cabbf7869a
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java
@@ -0,0 +1,494 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * ContextualProperties is an extension of {@link java.util.Properties} that automatically selects properties based on an
+ * environment parameter (defined by the system property {@link #ENV_SYS_PROPERTY}), the name of a class, plus a modifier
+ * (which can be used to name a method of a class) and a property key. It also supports the definition of arrays of
+ * property values using indexes. The properties are searched in the following order until a match is found:
+ *
+ * <ol>
+ * <li>environment + class name with package name + modifier + key
+ * <li>environment + class name with package name + key
+ * <li>environment + key
+ * <li>class name with package name + modifier + key
+ * <li>class name with package name + key
+ * <li>key
+ * </ol>
+ *
+ * <p>To create arrays of property values add index numbers onto the end of the property keys. An array of string values
+ * will be created with the elements of the array set to the value of the property at the matching index. Ideally the
+ * index values will be contiguous, starting at 0. This does not need to be the case however. If an array definition
+ * begins at index n, and ends at index m, Then an array big enough to hold m + 1 elements will be created and populated
+ * with values from n to m. Values before n and any missing values between n and m will be null in the array.
+ *
+ * <p>To give an example, suppose you have two different environments 'DEVELOPMENT' and 'PRODUCTION' and they each need
+ * the same properties but set to different values for each environment and some properties the same in both, you could
+ * create a properties file like:
+ *
+ * <p><code>
+ * # Project configuration properties file.<br/>
+ * <br/>
+ * # These properties are environment specific.<br/>
+ * DEVELOPMENT.debug=true<br/>
+ * PRODUCTION.debug=false<br/>
+ * <br/>
+ * # Always debug MyClass in all environments but not the myMethod method.<br/>
+ * MyClass.debug=true<br/>
+ * MyClass.myMethod.debug=false<br/>
+ * <br/>
+ * # Set up an array of my ten favourite animals. Leave elements 3 to 8 as null as I haven't decided on them yet.<br/>
+ * animals.0=cat<br/>
+ * animals.1=dog<br/>
+ * animals.2=elephant<br/>
+ * animals.9=lion<br/>
+ * <br/>
+ * # This is a default value that will be used when the environment is not known.<br/>
+ * debug=false<br/>
+ * </code>
+ *
+ * <p>The most specific definition of a property is searched for first moving out to the most general. This allows
+ * general property defaults to be set and then overiden for specific uses by some classes and modifiers.
+ *
+ * <p>A ContextualProperties object can be loaded in the same way as a java.utils.Properties. A recommended way to do
+ * this that does not assume that the properties file is a file (it could be in a jar) is to load the properties from the
+ * url for the resource lookup up on the classpath:
+ *
+ * <p><code>
+ * Properties configProperties = new ContextualProperties();<br/>
+ * configProperties.load(this.getClass().getClassLoader().getResourceAsStream("config.properties"));<br/>
+ * </code>
+ *
+ * <p>EnvironmentProperties will load the 'DEVELOPMENT.debug' property or 'PROUCTION.debug' property based on the setting
+ * of the system environment property. If a matching property for the environment cannot be found then the simple property
+ * name without the environment pre-pended onto it will be used instead. This 'use of default environments' behaviour is
+ * turned on initially but it can be disabled by calling the {@link #useDefaultEnvironments} method.
+ *
+ * <p>When a property matching a key cannot be found then the property accessor methods will always return null. If a
+ * default value for a property exists but the 'use of default environments' behavious prevents it being used then the
+ * accessor methods will return null.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Automatically select properties dependant on environment, class name and modifier as well as property key.
+ * <tr><td> Convert indexed properties into arrays.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ContextualProperties extends ParsedProperties
+{
+ /** The name of the system property that is used to define the environment. */
+ public static final String ENV_SYS_PROPERTY = "environment";
+
+ /**
+ * <p>Holds the iteration count down order.
+ *
+ * <p>If e = 4, b = 2, m = 1 then the iteration order or i is 7,6,4 and then if using environment defaults 3,2,0
+ * where the accessor key is:
+ * (i & e != 0 ? environment : "") + (i & b != 0 ? base : "") + (1 + m != 0 ? modifier : "") + key
+ *
+ * <p>In other words the presence or otherwise of the three least significant bits when counting down from 7
+ * specifies which of the environment, base and modifier are to be included in the key where the environment, base
+ * and modifier stand for the bits in positions 2, 1 and 0. The numbers 5 and 1 are missed out of the count because
+ * they stand for the case where the modifier is used without the base which is not done.
+ */
+ private static final int[] ORDER = new int[] { 7, 6, 4, 3, 2, 0 };
+
+ /**
+ * Defines the point in the iteration count order below which the 'use environment defaults' feature is being used.
+ */
+ private static final int ENVIRONMENT_DEFAULTS_CUTOFF = 4;
+
+ /** Defines the bit representation for the environment in the key ordering. See {@link #ORDER}. */
+ private static final int E = 4;
+
+ /** Defines the bit representation for the base in the key ordering. See {@link #ORDER}. */
+ private static final int B = 2;
+
+ /** Defines the bit representation for the modifier in the key ordering. See {@link #ORDER}. */
+ private static final int M = 1;
+
+ /** Used to hold the value of the environment system property. */
+ private String environment;
+
+ /** Used to indicate that the 'use of defaults' behaviour should be used. */
+ private boolean useDefaults = true;
+
+ /** Used to hold all the array properties. This is a mapping from property names to ArrayLists of Strings. */
+ protected Map arrayProperties = new HashMap();
+
+ /**
+ * Default constructor that builds a ContextualProperties that uses environment defaults.
+ */
+ public ContextualProperties()
+ {
+ super();
+
+ // Keep the value of the system environment property.
+ environment = System.getProperty(ENV_SYS_PROPERTY);
+ }
+
+ /**
+ * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties.
+ *
+ * @param props The properties to initialize this with.
+ */
+ public ContextualProperties(Properties props)
+ {
+ super(props);
+
+ // Keep the value of the system environment property.
+ environment = System.getProperty(ENV_SYS_PROPERTY);
+
+ // Extract any array properties as arrays.
+ createArrayProperties();
+ }
+
+ /**
+ * Parses an input stream as properties.
+ *
+ * @param inStream The input stream to read the properties from.
+ *
+ * @exception IOException If there is an IO error during reading from the input stream.
+ */
+ public void load(InputStream inStream) throws IOException
+ {
+ super.load(inStream);
+
+ // Extract any array properties as arrays.
+ createArrayProperties();
+ }
+
+ /**
+ * Tells this environment aware properties object whether it should use default environment properties without a
+ * pre-pended environment when a property for the current environment cannot be found.
+ *
+ * @param flag True to use defaults, false to not use defaults.
+ */
+ public void useDefaultEnvironments(boolean flag)
+ {
+ useDefaults = flag;
+ }
+
+ /**
+ * Looks up a property value relative to the environment, callers class and method. The default environment will be
+ * checked for a matching property if defaults are being used. In order to work out the callers class and method this
+ * method throws an exception and then searches one level up its stack frames.
+ *
+ * @param key The property key.
+ *
+ * @return The value of this property searching from the most specific definition (environment, class, method, key)
+ * to the most general (key only), unless use of default environments is turned off in which case the most general
+ * proeprty searched is (environment, key).
+ */
+ public String getProperty(String key)
+ {
+ // Try to get the callers class name and method name by examing the stack.
+ String className = null;
+ String methodName = null;
+
+ // Java 1.4 onwards only.
+ /*try
+ {
+ throw new Exception();
+ }
+ catch (Exception e)
+ {
+ StackTraceElement[] stack = e.getStackTrace();
+
+ // Check that the stack trace contains at least two elements, one for this method and one for the caller.
+ if (stack.length >= 2)
+ {
+ className = stack[1].getClassName();
+ methodName = stack[1].getMethodName();
+ }
+ }*/
+
+ // Java 1.5 onwards only.
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+
+ // Check that the stack trace contains at least two elements, one for this method and one for the caller.
+ if (stack.length >= 2)
+ {
+ className = stack[1].getClassName();
+ methodName = stack[1].getMethodName();
+ }
+
+ // Java 1.3 and before? Not sure, some horrible thing that parses the text spat out by printStackTrace?
+
+ return getProperty(className, methodName, key);
+ }
+
+ /**
+ * Looks up a property value relative to the environment, base class and modifier. The default environment will be
+ * checked for a matching property if defaults are being used.
+ *
+ * @param base An object of the class to retrieve properties relative to.
+ * @param modifier The modifier (which may stand for a method of the class).
+ * @param key The property key.
+ *
+ * @return The value of this property searching from the most specific definition (environment, class, modifier, key)
+ * to the most general (key only), unless use of default environments is turned off in which case the most general
+ * property searched is (environment, key).
+ */
+ public String getProperty(Object base, String modifier, String key)
+ {
+ return getProperty(base.getClass().getName(), modifier, key);
+ }
+
+ /**
+ * Looks up a property value relative to the environment, base class and modifier. The default environment will be
+ * checked for a matching property if defaults are being used.
+ *
+ * @param base The name of the class to retrieve properties relative to.
+ * @param modifier The modifier (which may stand for a method of the class).
+ * @param key The property key.
+ *
+ * @return The value of this property searching from the most specific definition (environment, class, modifier, key)
+ * to the most general (key only), unless use of default environments is turned off in which case the most general
+ * property searched is (environment, key).
+ */
+ public String getProperty(String base, String modifier, String key)
+ {
+ String result = null;
+
+ // Loop over the key orderings, from the most specific to the most general, until a matching value is found.
+ for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();)
+ {
+ String nextKey = (String) i.next();
+
+ result = super.getProperty(nextKey);
+
+ if (result != null)
+ {
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Looks up an array property value relative to the environment, callers class and method. The default environment
+ * will be checked for a matching array property if defaults are being used. In order to work out the callers class
+ * and method this method throws an exception and then searches one level up its stack frames.
+ *
+ * @param key The property key.
+ *
+ * @return The array value of this indexed property searching from the most specific definition (environment, class,
+ * method, key) to the most general (key only), unless use of default environments is turned off in which
+ * case the most general proeprty searched is (environment, key).
+ */
+ public String[] getProperties(String key)
+ {
+ // Try to get the callers class name and method name by throwing an exception an searching the stack frames.
+ String className = null;
+ String methodName = null;
+
+ /* Java 1.4 onwards only.
+ try {
+ throw new Exception();
+ } catch (Exception e) {
+ StackTraceElement[] stack = e.getStackTrace();
+ // Check that the stack trace contains at least two elements, one for this method and one for the caller.
+ if (stack.length >= 2) {
+ className = stack[1].getClassName();
+ methodName = stack[1].getMethodName();
+ }
+ }*/
+ return getProperties(className, methodName, key);
+ }
+
+ /**
+ * Looks up an array property value relative to the environment, base class and modifier. The default environment will
+ * be checked for a matching array property if defaults are being used.
+ *
+ * @param base An object of the class to retrieve properties relative to.
+ * @param modifier The modifier (which may stand for a method of the class).
+ * @param key The property key.
+ *
+ * @return The array value of this indexed property searching from the most specific definition (environment, class,
+ * modifier, key) to the most general (key only), unless use of default environments is turned off in which
+ * case the most general proeprty searched is (environment, key).
+ */
+ public String[] getProperties(Object base, String modifier, String key)
+ {
+ return getProperties(base.getClass().getName(), modifier, key);
+ }
+
+ /**
+ * Looks up an array property value relative to the environment, base class and modifier. The default environment will
+ * be checked for a matching array property if defaults are being used.
+ *
+ * @param base The name of the class to retrieve properties relative to.
+ * @param modifier The modifier (which may stand for a method of the class).
+ * @param key The property key.
+ *
+ * @return The array value of this indexed property searching from the most specific definition (environment, class,
+ * modifier, key) to the most general (key only), unless use of default environments is turned off in which
+ * case the most general property searched is (environment, key).
+ */
+ public String[] getProperties(String base, String modifier, String key)
+ {
+ String[] result = null;
+
+ // Loop over the key orderings, from the most specific to the most general, until a matching value is found.
+ for (Iterator i = getKeyIterator(base, modifier, key); i.hasNext();)
+ {
+ String nextKey = (String) i.next();
+ ArrayList arrayList = (ArrayList) arrayProperties.get(nextKey);
+
+ if (arrayList != null)
+ {
+ result = (String[]) arrayList.toArray(new String[] {});
+
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * For a given environment, base, modifier and key and setting of the use of default environments feature this
+ * generates an iterator that walks over the order in which to try and access properties.
+ *
+ * <p>See the {@link #ORDER} constant for an explanation of how the key ordering is generated.
+ *
+ * @param base The name of the class to retrieve properties relative to.
+ * @param modifier The modifier (which may stand for a method of the class).
+ * @param key The property key.
+ *
+ * @return An Iterator over String keys defining the order in which properties should be accessed.
+ */
+ protected Iterator getKeyIterator(final String base, final String modifier, final String key)
+ {
+ return new Iterator()
+ {
+ // The key ordering count always begins at the start of the ORDER array.
+ private int i = 0;
+
+ public boolean hasNext()
+ {
+ return (useDefaults ? ((i < ORDER.length) && (ORDER[i] > ENVIRONMENT_DEFAULTS_CUTOFF))
+ : (i < ORDER.length));
+ }
+
+ public Object next()
+ {
+ // Check that there is a next element and return null if not.
+ if (!hasNext())
+ {
+ return null;
+ }
+
+ // Get the next ordering count.
+ int o = ORDER[i];
+
+ // Do bit matching on the count to choose which elements to include in the key.
+ String result =
+ (((o & E) != 0) ? (environment + ".") : "") + (((o & B) != 0) ? (base + ".") : "")
+ + (((o & M) != 0) ? (modifier + ".") : "") + key;
+
+ // Increment the iterator to get the next key on the next call.
+ i++;
+
+ return result;
+ }
+
+ public void remove()
+ {
+ // This method is not supported.
+ throw new UnsupportedOperationException("remove() is not supported on this key order iterator as "
+ + "the ordering cannot be changed");
+ }
+ };
+ }
+
+ /**
+ * Scans all the properties in the parent Properties object and creates arrays for any array property definitions.
+ *
+ * <p>Array properties are defined with indexes. For example:
+ *
+ * <p><code>
+ * property.1=one<br/>
+ * property.2=two<br/>
+ * property.3=three<br/>
+ * </code>
+ *
+ * <p>Note that these properties will be stored as the 'empty string' or "" property array.
+ *
+ * <p><code>
+ * .1=one<br/>
+ * 2=two<br/>
+ * </code>
+ */
+ protected void createArrayProperties()
+ {
+ // Scan through all defined properties.
+ for (Object o : keySet())
+ {
+ String key = (String) o;
+ String value = super.getProperty(key);
+
+ // Split the property key into everything before the last '.' and after it.
+ int lastDotIndex = key.lastIndexOf('.');
+ String keyEnding = key.substring(lastDotIndex + 1, key.length());
+ String keyStart = key.substring(0, (lastDotIndex == -1) ? 0 : lastDotIndex);
+
+ // Check if the property key ends in an integer, in which case it is an array property.
+ int index = 0;
+
+ try
+ {
+ index = Integer.parseInt(keyEnding);
+ }
+ // The ending is not an integer so its not an array.
+ catch (NumberFormatException e)
+ {
+ // Scan the next property.
+ continue;
+ }
+
+ // Check if an array property already exists for this base name and create one if not.
+ ArrayList propArray = (ArrayList) arrayProperties.get(keyStart);
+
+ if (propArray == null)
+ {
+ propArray = new ArrayList();
+ arrayProperties.put(keyStart, propArray);
+ }
+
+ // Add the new property value to the array property for the index.
+ propArray.set(index, value);
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java
new file mode 100644
index 0000000000..7a45632643
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java
@@ -0,0 +1,428 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.util;
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Mathematical support methods for the toolkit. Caculating averages, variances, min/max for test latencies and
+ * generating linear/exponential sequences for test size/concurrency ramping up.
+ *
+ * <p/>The sequence specifications are of the form [lowest(, ...)(, highest)](,sample=s)(,exp), where round brackets
+ * enclose optional values. Using this pattern form it is possible to specify a single value, a range of values divided
+ * into s samples, a range of values divided into s samples but distributed exponentially, or a fixed set of samples.
+ *
+ * <p/>The duration arguments are of the form (dD)(hH)(mM)(sS), where round brackets enclose optional values. At least
+ * one of the optional values must be present.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Generate a sequene of integers from a sequence specification.
+ * <tr><td> Parse an encoded duration into milliseconds.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class MathUtils
+{
+ /** Used for debugging. */
+ // private static final Logger log = Logger.getLogger(MathUtils.class);
+
+ /** The sequence defintion matching regular expression. */
+ public static final String SEQUENCE_REGEXP = "^(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$";
+
+ /** The regular expression that matches sequence definitions. */
+ private static final Pattern SEQUENCE_PATTERN = Pattern.compile(SEQUENCE_REGEXP);
+
+ /** The duration definition matching regular expression. */
+ public static final String DURATION_REGEXP = "^(\\d+D)?(\\d+H)?(\\d+M)?(\\d+S)?$";
+
+ /** The regular expression that matches the duration expression. */
+ public static final Pattern DURATION_PATTERN = Pattern.compile(DURATION_REGEXP);
+
+ /** For matching name=value pairs. */
+ public static final String NAME_VALUE_REGEXP = "^\\w+=\\w+$";
+
+ /** For matching name=[value1: value2: ...] variations. */
+ public static final String NAME_VALUE_VARIATION_REGEXP = "^\\w+=\\[[\\w:]+\\]$";
+
+ /** For matching name=[n: ... :m](:sample=s)(:exp) sequences. */
+ public static final String NAME_VALUE_SEQUENCE_REGEXP = "^\\w+=(\\[[0-9:]+\\])(:samples=[0-9]+)?(:exp)?$";
+
+ /** The regular expression that matches name=value pairs and variations. */
+ public static final Pattern NAME_VALUE_PATTERN =
+ Pattern.compile("(" + NAME_VALUE_REGEXP + ")|(" + NAME_VALUE_VARIATION_REGEXP + ")|(" + NAME_VALUE_SEQUENCE_REGEXP
+ + ")");
+
+ /**
+ * Runs a quick test of the sequence generation methods to confirm that they work as expected.
+ *
+ * @param args The command line parameters.
+ */
+ public static void main(String[] args)
+ {
+ // Use the command line parser to evaluate the command line.
+ CommandLineParser commandLine =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "s", "The sequence definition.", "[m:...:n](:sample=s)(:exp)", "true", MathUtils.SEQUENCE_REGEXP },
+ { "d", "The duration definition.", "dDhHmMsS", "false", MathUtils.DURATION_REGEXP }
+ });
+
+ // Capture the command line arguments or display errors and correct usage and then exit.
+ ParsedProperties options = null;
+
+ try
+ {
+ options = new ParsedProperties(commandLine.parseCommandLine(args));
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(-1);
+ }
+
+ // Extract the command line options.
+ String sequence = options.getProperty("s");
+ String durationString = options.getProperty("d");
+
+ System.out.println("Sequence is: " + printArray(parseSequence(sequence)));
+
+ if (durationString != null)
+ {
+ System.out.println("Duration is: " + parseDuration(durationString));
+ }
+ }
+
+ /**
+ * Given a start and end and a number of steps this method generates a sequence of evenly spaced integer
+ * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number
+ * of values in the sequence. The sequence returned may contain less than the specified number where the integer
+ * range between start and end is too small to contain that many.
+ *
+ * <p/>As the results are integers, they will not be perfectly evenly spaced but a best-fit.
+ *
+ * @param start The sequence start.
+ * @param end The sequence end.
+ * @param steps The number of steps.
+ *
+ * @return The sequence.
+ */
+ public static int[] generateSequence(int start, int end, int steps)
+ {
+ // Check that there are at least two steps.
+ if (steps < 2)
+ {
+ throw new IllegalArgumentException("There must be at least 2 steps.");
+ }
+
+ ArrayList<Integer> result = new ArrayList<Integer>();
+
+ // Calculate the sequence using floating point, then round into the results.
+ double fStart = start;
+ double fEnd = end;
+ double fCurrent = start;
+
+ for (int i = 0; i < steps; i++)
+ {
+ fCurrent = (((fEnd - fStart) / (steps - 1)) * i) + fStart;
+
+ roundAndAdd(result, fCurrent);
+ }
+
+ // Return the results after converting to a primitive array.
+ return intListToPrimitiveArray(result);
+ }
+
+ /**
+ * Given a start and end and a number of steps this method generates a sequence of expontentially spaced integer
+ * values, starting at the start (inclusive) and finishing at the end (inclusive) with the specified number
+ * of values in the sequence. An exponentially spaced sequence is one where the ratio between any two consecutive
+ * numbers in the sequence remains constant. The sequence returned may contain less than the specified number where
+ * the difference between two consecutive values is too small (this is more likely at the start of the sequence,
+ * where the values are closer together).
+ *
+ * <p/>As the results are integers, they will not be perfectly exponentially spaced but a best-fit.
+ *
+ * @param start The sequence start.
+ * @param end The sequence end.
+ * @param steps The number of steps.
+ *
+ * @return The sequence.
+ */
+ public static int[] generateExpSequence(int start, int end, int steps)
+ {
+ // Check that there are at least two steps.
+ if (steps < 2)
+ {
+ throw new IllegalArgumentException("There must be at least 2 steps.");
+ }
+
+ ArrayList<Integer> result = new ArrayList<Integer>();
+
+ // Calculate the sequence using floating point, then round into the results.
+ double fStart = start;
+ double fEnd = end;
+ // float fCurrent = start;
+ double diff = fEnd - fStart;
+ double factor = java.lang.Math.pow(diff, (1.0f / (steps - 1)));
+
+ for (int i = 0; i < steps; i++)
+ {
+ // This is a cheat to get the end exactly on and lose the accumulated rounding error.
+ if (i == (steps - 1))
+ {
+ result.add(end);
+ }
+ else
+ {
+ roundAndAdd(result, fStart - 1.0f + java.lang.Math.pow(factor, i));
+ }
+ }
+
+ // Return the results after converting to a primitive array.
+ return intListToPrimitiveArray(result);
+ }
+
+ /**
+ * Parses a string defintion of a sequence into an int array containing the sequence. The definition will conform
+ * to the regular expression: "^(\[[0-9,]+\])(,samples=[0-9]+)?(,exp)?$". This splits it into three parts,
+ * an array of integers, the optional sample count and the optional exponential flag.
+ *
+ * @param sequenceDef The sequence definition.
+ *
+ * @return The sequence as a fully expanded int array.
+ */
+ public static int[] parseSequence(String sequenceDef)
+ {
+ // Match the sequence definition against the regular expression for sequences.
+ Matcher matcher = SEQUENCE_PATTERN.matcher(sequenceDef);
+
+ // Check that the argument is of the right format accepted by this method.
+ if (!matcher.matches())
+ {
+ throw new IllegalArgumentException("The sequence definition is not in the correct format.");
+ }
+
+ // Get the total number of matching groups to see if either of the optional samples or exponential flag
+ // goups were set.
+ int numGroups = matcher.groupCount();
+
+ // Split the array of integers on commas.
+ String intArrayString = matcher.group(1);
+
+ String[] intSplits = intArrayString.split("[:\\[\\]]");
+
+ int[] sequence = new int[intSplits.length - 1];
+
+ for (int i = 1; i < intSplits.length; i++)
+ {
+ sequence[i - 1] = Integer.parseInt(intSplits[i]);
+ }
+
+ // Check for the optional samples count.
+ int samples = 0;
+
+ if ((numGroups > 1) && (matcher.group(2) != null))
+ {
+ String samplesGroup = matcher.group(2);
+
+ String samplesString = samplesGroup.substring(",samples=".length());
+ samples = Integer.parseInt(samplesString);
+ }
+
+ // Check for the optional exponential flag.
+ boolean expFlag = false;
+
+ if ((numGroups > 2) && (matcher.group(3) != null))
+ {
+ expFlag = true;
+ }
+
+ // If there is a sample count and 2 or more sequence values defined, then generate the sequence from the first
+ // and last sequence values.
+ if ((samples != 0) && (sequence.length >= 2))
+ {
+ int start = sequence[0];
+ int end = sequence[sequence.length - 1];
+
+ if (!expFlag)
+ {
+ sequence = generateSequence(start, end, samples);
+ }
+ else
+ {
+ sequence = generateExpSequence(start, end, samples);
+ }
+ }
+
+ return sequence;
+ }
+
+ /**
+ * Parses a duration defined as a string, giving a duration in days, hours, minutes and seconds into a number
+ * of milliseconds equal to that duration.
+ *
+ * @param duration The duration definition string.
+ *
+ * @return The duration in millliseconds.
+ */
+ public static long parseDuration(String duration)
+ {
+ // Match the duration against the regular expression.
+ Matcher matcher = DURATION_PATTERN.matcher(duration);
+
+ // Check that the argument is of the right format accepted by this method.
+ if (!matcher.matches())
+ {
+ throw new IllegalArgumentException("The duration definition is not in the correct format.");
+ }
+
+ // This accumulates the duration.
+ long result = 0;
+
+ int numGroups = matcher.groupCount();
+
+ // Extract the days.
+ if (numGroups >= 1)
+ {
+ String daysString = matcher.group(1);
+ result +=
+ (daysString == null)
+ ? 0 : (Long.parseLong(daysString.substring(0, daysString.length() - 1)) * 24 * 60 * 60 * 1000);
+ }
+
+ // Extract the hours.
+ if (numGroups >= 2)
+ {
+ String hoursString = matcher.group(2);
+ result +=
+ (hoursString == null) ? 0
+ : (Long.parseLong(hoursString.substring(0, hoursString.length() - 1)) * 60 * 60 * 1000);
+ }
+
+ // Extract the minutes.
+ if (numGroups >= 3)
+ {
+ String minutesString = matcher.group(3);
+ result +=
+ (minutesString == null)
+ ? 0 : (Long.parseLong(minutesString.substring(0, minutesString.length() - 1)) * 60 * 1000);
+ }
+
+ // Extract the seconds.
+ if (numGroups >= 4)
+ {
+ String secondsString = matcher.group(4);
+ result +=
+ (secondsString == null) ? 0 : (Long.parseLong(secondsString.substring(0, secondsString.length() - 1)) * 1000);
+ }
+
+ return result;
+ }
+
+ /**
+ * Pretty prints an array of ints as a string.
+ *
+ * @param array The array to pretty print.
+ *
+ * @return The pretty printed string.
+ */
+ public static String printArray(int[] array)
+ {
+ String result = "[";
+ for (int i = 0; i < array.length; i++)
+ {
+ result += array[i];
+ result += (i < (array.length - 1)) ? ", " : "";
+ }
+
+ result += "]";
+
+ return result;
+ }
+
+ /**
+ * Returns the maximum value in an array of integers.
+ *
+ * @param values The array to find the amx in.
+ *
+ * @return The max value.
+ */
+ public static int maxInArray(int[] values)
+ {
+ if ((values == null) || (values.length == 0))
+ {
+ throw new IllegalArgumentException("Cannot find the max of a null or empty array.");
+ }
+
+ int max = values[0];
+
+ for (int value : values)
+ {
+ max = (max < value) ? value : max;
+ }
+
+ return max;
+ }
+
+ /**
+ * The #toArray methods of collections cannot be used with primitive arrays. This loops over and array list
+ * of Integers and outputs and array of int.
+ *
+ * @param result The array of Integers to convert.
+ *
+ * @return An array of int.
+ */
+ private static int[] intListToPrimitiveArray(ArrayList<Integer> result)
+ {
+ int[] resultArray = new int[result.size()];
+ int index = 0;
+ for (int r : result)
+ {
+ resultArray[index] = result.get(index);
+ index++;
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * Rounds the specified floating point value to the nearest integer and adds it to the specified list of
+ * integers, provided it is not already in the list.
+ *
+ * @param result The list of integers to add to.
+ * @param value The new candidate to round and add to the list.
+ */
+ private static void roundAndAdd(ArrayList<Integer> result, double value)
+ {
+ int roundedValue = (int) Math.round(value);
+
+ if (!result.contains(roundedValue))
+ {
+ result.add(roundedValue);
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java
new file mode 100644
index 0000000000..59c8cfbd3a
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java
@@ -0,0 +1,390 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.util;
+
+import java.util.Properties;
+
+/**
+ * ParsedProperties extends the basic Properties class with methods to extract properties, not as strings but as strings
+ * parsed into basic types.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class ParsedProperties extends Properties
+{
+ /**
+ * Creates an empty ParsedProperties.
+ */
+ public ParsedProperties()
+ {
+ super();
+ }
+
+ /**
+ * Creates a ParsedProperties initialized with the specified properties.
+ *
+ * @param props The properties to initialize this with.
+ */
+ public ParsedProperties(Properties props)
+ {
+ super(props);
+ }
+
+ /**
+ * Helper method for setting system properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public static boolean setSysPropertyIfNull(String propname, boolean value)
+ {
+ return Boolean.parseBoolean(setSysPropertyIfNull(propname, Boolean.toString(value)));
+ }
+
+ /**
+ * Helper method for setting system properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public static short setSysPropertyIfNull(String propname, short value)
+ {
+ return Short.parseShort(setSysPropertyIfNull(propname, Short.toString(value)));
+ }
+
+ /**
+ * Helper method for setting system properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public static int setSysPropertyIfNull(String propname, int value)
+ {
+ return Integer.parseInt(setSysPropertyIfNull(propname, Integer.toString(value)));
+ }
+
+ /**
+ * Helper method for setting system properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public static long setSysPropertyIfNull(String propname, long value)
+ {
+ return Long.parseLong(setSysPropertyIfNull(propname, Long.toString(value)));
+ }
+
+ /**
+ * Helper method for setting system properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public static float setSysPropertyIfNull(String propname, float value)
+ {
+ return Float.parseFloat(setSysPropertyIfNull(propname, Float.toString(value)));
+ }
+
+ /**
+ * Helper method for setting system properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public static double setSysPropertyIfNull(String propname, double value)
+ {
+ return Double.parseDouble(setSysPropertyIfNull(propname, Double.toString(value)));
+ }
+
+ /**
+ * Helper method for setting system properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the system property after this method call.
+ */
+ public static String setSysPropertyIfNull(String propname, String value)
+ {
+ String property = System.getProperty(propname);
+
+ if (property == null)
+ {
+ System.setProperty(propname, value);
+
+ return value;
+ }
+ else
+ {
+ return property;
+ }
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public boolean setPropertyIfNull(String propname, boolean value)
+ {
+ return Boolean.parseBoolean(setPropertyIfNull(propname, Boolean.toString(value)));
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public short setPropertyIfNull(String propname, short value)
+ {
+ return Short.parseShort(setPropertyIfNull(propname, Short.toString(value)));
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public int setPropertyIfNull(String propname, int value)
+ {
+ return Integer.parseInt(setPropertyIfNull(propname, Integer.toString(value)));
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public long setPropertyIfNull(String propname, long value)
+ {
+ return Long.parseLong(setPropertyIfNull(propname, Long.toString(value)));
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public float setPropertyIfNull(String propname, float value)
+ {
+ return Float.parseFloat(setPropertyIfNull(propname, Float.toString(value)));
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public double setPropertyIfNull(String propname, double value)
+ {
+ return Double.parseDouble(setPropertyIfNull(propname, Double.toString(value)));
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public String setPropertyIfNull(String propname, String value)
+ {
+ String property = super.getProperty(propname);
+
+ if (property == null)
+ {
+ super.setProperty(propname, value);
+
+ return value;
+ }
+ else
+ {
+ return property;
+ }
+ }
+
+ /**
+ * Helper method for setting properties.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public boolean setProperty(String propname, boolean value)
+ {
+ setProperty(propname, Boolean.toString(value));
+
+ return value;
+ }
+
+ /**
+ * Helper method for setting properties.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public short setProperty(String propname, short value)
+ {
+ setProperty(propname, Short.toString(value));
+
+ return value;
+ }
+
+ /**
+ * Helper method for setting properties.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public int setProperty(String propname, int value)
+ {
+ setProperty(propname, Integer.toString(value));
+
+ return value;
+ }
+
+ /**
+ * Helper method for setting properties.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public long setProperty(String propname, long value)
+ {
+ setProperty(propname, Long.toString(value));
+
+ return value;
+ }
+
+ /**
+ * Helper method for setting properties.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public float setProperty(String propname, float value)
+ {
+ setProperty(propname, Float.toString(value));
+
+ return value;
+ }
+
+ /**
+ * Helper method for setting properties.
+ *
+ * @param propname The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property after this method call.
+ */
+ public double setProperty(String propname, double value)
+ {
+ setProperty(propname, Double.toString(value));
+
+ return value;
+ }
+
+ /**
+ * Parses a property as a boolean.
+ *
+ * @param propName The property.
+ *
+ * @return The property as a boolean, or false if it does not exist.
+ */
+ public boolean getPropertyAsBoolean(String propName)
+ {
+ String prop = getProperty(propName);
+
+ return (prop != null) && Boolean.parseBoolean(prop);
+ }
+
+ /**
+ * Parses a property as an integer.
+ *
+ * @param propName The property.
+ *
+ * @return The property as a integer, or null if it does not exist.
+ */
+ public Integer getPropertyAsInteger(String propName)
+ {
+ String prop = getProperty(propName);
+
+ return (prop != null) ? new Integer(prop) : null;
+ }
+
+ /**
+ * Parses a property as a long.
+ *
+ * @param propName The property.
+ *
+ * @return The property as a long, or null if it does not exist.
+ */
+ public Long getPropertyAsLong(String propName)
+ {
+ String prop = getProperty(propName);
+
+ return (prop != null) ? new Long(prop) : null;
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java
new file mode 100644
index 0000000000..5f3ebb4545
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java
@@ -0,0 +1,94 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.util;
+
+/**
+ * SizeOf provides a static method that does its best to return an accurate measure of the total amount of memory used by
+ * the virtual machine. This is calculated as the total memory available to the VM minus the actual amount used by it.
+ * Before this measurement is taken the garbage collector is run many times until the used memory calculation stabilizes.
+ * Generally, this trick works quite well to provide an accurate reading, however, it cannot be relied upon to be totally
+ * accurate. It is also quite slow.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Calculate total memory used.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class SizeOf
+{
+ /** Holds a reference to the runtime object. */
+ private static final Runtime RUNTIME = Runtime.getRuntime();
+
+ /**
+ * Makes 4 calls the {@link #runGCTillStable} method.
+ */
+ public static void runGCTillStableSeveralTimes()
+ {
+ // It helps to call Runtime.gc() using several method calls.
+ for (int r = 0; r < 4; ++r)
+ {
+ runGCTillStable();
+ }
+ }
+
+ /**
+ * Runs the garbage collector until the used memory reading stabilizes. It may run the garbage collector up
+ * to 500 times.
+ */
+ public static void runGCTillStable()
+ {
+ long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE;
+
+ for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++i)
+ {
+ RUNTIME.runFinalization();
+ RUNTIME.gc();
+ Thread.currentThread().yield();
+
+ usedMem2 = usedMem1;
+ usedMem1 = usedMemory();
+ }
+ }
+
+ /**
+ * Runs the garbage collector until the used memory stabilizes and then measures it.
+ *
+ * @return The amount of memory used by the virtual machine.
+ */
+ public static long getUsedMemory()
+ {
+ runGCTillStableSeveralTimes();
+
+ return usedMemory();
+ }
+
+ /**
+ * Returns the amount of memory used by subtracting the free memory from the total available memory.
+ *
+ * @return The amount of memory used.
+ */
+ private static long usedMemory()
+ {
+ return RUNTIME.totalMemory() - RUNTIME.freeMemory();
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java
new file mode 100644
index 0000000000..9078c0e247
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java
@@ -0,0 +1,131 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.util;
+
+import java.util.EmptyStackException;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Stack;
+
+/**
+ * The Stack class in java.util (most unhelpfully) does not implement the Queue interface. This is an adaption of that
+ * class as a queue.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Turn a stack into a queue.
+ * </table>
+ *
+ * @todo Need to override the add method, and iterator and consider other methods too. They work like LIFO but
+ * really wany FIFO behaviour accross the whole data structure.
+ *
+ * @author Rupert Smith
+ */
+public class StackQueue<E> extends Stack<E> implements Queue<E>
+{
+ /**
+ * Retrieves, but does not remove, the head of this queue.
+ *
+ * @return The element at the top of the stack.
+ */
+ public E element()
+ {
+ try
+ {
+ return super.peek();
+ }
+ catch (EmptyStackException e)
+ {
+ NoSuchElementException t = new NoSuchElementException();
+ t.initCause(e);
+ throw t;
+ }
+ }
+
+ /**
+ * Inserts the specified element into this queue, if possible.
+ *
+ * @param o The data element to push onto the stack.
+ *
+ * @return True if it was added to the stack, false otherwise (this implementation always returns true).
+ */
+ public boolean offer(E o)
+ {
+ push(o);
+
+ return true;
+ }
+
+ /**
+ * Retrieves, but does not remove, the head of this queue, returning null if this queue is empty.
+ *
+ * @return The top element from the stack, or null if the stack is empty.
+ */
+ public E peek()
+ {
+ try
+ {
+ return super.peek();
+ }
+ catch (EmptyStackException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, or null if this queue is empty.
+ *
+ * @return The top element from the stack, or null if the stack is empty.
+ */
+ public E poll()
+ {
+ try
+ {
+ return super.pop();
+ }
+ catch (EmptyStackException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves and removes the head of this queue.
+ *
+ * @return The top element from the stack, or null if the stack is empty.
+ *
+ * @throws NoSuchElementException If the stack is empty so no element can be removed from it.
+ */
+ public E remove()
+ {
+ try
+ {
+ return super.pop();
+ }
+ catch (EmptyStackException e)
+ {
+ NoSuchElementException t = new NoSuchElementException();
+ t.initCause(e);
+ throw t;
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java
new file mode 100644
index 0000000000..edb7b6d73a
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java
@@ -0,0 +1,202 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.junit.extensions.util;
+
+import java.util.Properties;
+
+/**
+ * TestContextProperties is an extension of {@link ParsedProperties} that keeps track of property key/value pairs
+ * that are used by tests being run under the {@link org.apache.qpid.junit.extensions.TKTestRunner}. To keep the
+ * test runner notified of configurable test parameters, tests should establish their required property values by
+ * initiliazing fields or statics or in the constructor, through this class. The tk test runner automatically places
+ * any additional properties specified on the command line into the this class, and these are held statically.
+ *
+ * <p/>Here is an example:
+ *
+ * <pre>
+ * public class MyTestClass extends TestCase {
+ * ParsedProperties testProps = TestContextProperties.getInstance();
+ * private int testParam = testProps.setPropertyIfNull("testParam", 1);
+ * ...
+ * </pre>
+ *
+ * <p/>This has the effect of setting up the field testParam with the default value of 1, unless it is overridden
+ * by values passed to the tk test runner. It also notifies the tk test runner of the name and value of the test
+ * parameter actually used for the test, so that this can be logged in the test output file.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Log all name/value pairs read or written.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class TestContextProperties extends ParsedProperties
+{
+ /** Used for debugging. */
+ // Logger log = Logger.getLogger(TestContextProperties.class);
+
+ /** Holds all properties set or read through this property extension class. */
+ private Properties accessedProps = new Properties();
+
+ /** The singleton instance of the test context properties. */
+ private static TestContextProperties singleton = null;
+
+ /**
+ * Default constructor that builds a ContextualProperties that uses environment defaults.
+ */
+ private TestContextProperties()
+ {
+ super();
+ }
+
+ /**
+ * Gets the singleton instance of the test context properties.
+ *
+ * @return The singleton instance of the test context properties.
+ */
+ public static synchronized ParsedProperties getInstance()
+ {
+ if (singleton == null)
+ {
+ singleton = new TestContextProperties();
+ }
+
+ return singleton;
+ }
+
+ /**
+ * Gets the singleton instance of the test context properties, applying a specified set of default properties to
+ * it, if they are not already set.
+ *
+ * @param defaults The defaults to apply for properties not already set.
+ *
+ * @return The singleton instance of the test context properties.
+ */
+ public static synchronized ParsedProperties getInstance(Properties defaults)
+ {
+ ParsedProperties props = getInstance();
+
+ for (Object key : defaults.keySet())
+ {
+ String stringKey = (String) key;
+ String value = defaults.getProperty(stringKey);
+
+ props.setPropertyIfNull(stringKey, value);
+ }
+
+ return props;
+ }
+
+ /*
+ * Creates a ContextualProperties that uses environment defaults and is initialized with the specified properties.
+ *
+ * @param props The properties to initialize this with.
+ */
+ /*public TestContextProperties(Properties props)
+ {
+ super();
+ }*/
+
+ /**
+ * Gets all of the properties (with their most recent values) that have been set or read through this class.
+ *
+ * @return All of the properties accessed through this class.
+ */
+ public static Properties getAccessedProps()
+ {
+ return (singleton == null) ? new Properties() : singleton;
+ // return accessedProps;
+ }
+
+ /**
+ * Looks up a property value relative to the environment, callers class and method. The default environment will be
+ * checked for a matching property if defaults are being used. The property key/value pair is remembered and made
+ * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}.
+ *
+ * @param key The property key.
+ *
+ * @return The value of this property searching from the most specific definition (environment, class, method, key)
+ * to the most general (key only), unless use of default environments is turned off in which case the most general
+ * proeprty searched is (environment, key).
+ */
+ public String getProperty(String key)
+ {
+ // log.debug("public String getProperty(String key = " + key + "): called");
+
+ String value = super.getProperty(key);
+
+ if (value != null)
+ {
+ accessedProps.setProperty(key, value);
+ }
+
+ // log.debug("value = " + value);
+
+ return value;
+ }
+
+ /**
+ * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for parallelism with the <tt>getProperty</tt>
+ * method. Enforces use of strings for property keys and values. The value returned is the result of the
+ * <tt>Hashtable</tt> call to <code>put</code>. The property key/value pair is remembered and made
+ * available to {@link org.apache.qpid.junit.extensions.TKTestRunner}.
+ *
+ * @param key The key to be placed into this property list.
+ * @param value The value corresponding to <tt>key</tt>.
+ *
+ * @return The previous value of the specified key in this property list, or <code>null</code> if it did not have one.
+ */
+ public synchronized Object setProperty(String key, String value)
+ {
+ // log.debug("public synchronized Object setProperty(String key = " + key + ", String value = " + value + "): called");
+
+ Object result = super.setProperty(key, value);
+ accessedProps.setProperty(key, value);
+
+ return result;
+ }
+
+ /**
+ * Helper method for setting properties to defaults when they are not already set. The property key/value pair is
+ * remembered and made available to {@link org.apache.qpid.junit.extensions.TKTestRunner}.
+ *
+ * @param key The name of the system property to set.
+ * @param value The value to set it to.
+ *
+ * @return The value of the property, which will be the value passed in if it was null, or the existing value otherwise.
+ */
+ public String setPropertyIfNull(String key, String value)
+ {
+ // log.debug("public String setPropertyIfNull(String key = " + key + ", String value = " + value + "): called");
+
+ String result = super.setPropertyIfNull(key, value);
+
+ if (value != null)
+ {
+ accessedProps.setProperty(key, result);
+ }
+
+ // log.debug("result = " + result);
+
+ return result;
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestUtils.java b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestUtils.java
new file mode 100644
index 0000000000..553a41ecae
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestUtils.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.junit.extensions.util;
+
+/**
+ * Provides commonly used functions that aid testing.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a short pause.
+ * </table>
+ *
+ * @author Rupert Smith
+ */
+public class TestUtils
+{
+ /**
+ * Injects a short pause. The pause may not complete its full length, if the thread is interrupted when waiting.
+ * In most cases, this will not happen and this method is a vry adequate pause implementation, without the
+ * need to handle interrupted exceptions.
+ *
+ * @param millis The length of the pause in milliseconds.
+ */
+ public static void pause(long millis)
+ {
+ try
+ {
+ Thread.sleep(millis);
+ }
+ catch (InterruptedException e)
+ {
+ // Clear the flag and ignore.
+ Thread.interrupted();
+ }
+ }
+}
diff --git a/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/package.html b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/package.html
new file mode 100644
index 0000000000..cbf45fe295
--- /dev/null
+++ b/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/package.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+Provides some helper classes. ContextualProperties allows a hierarchy of properties to be used in properties file with
+default overrides. SizeOf takes memeory measurements by stabilizing the garbage collector.
+</body>
+</html> \ No newline at end of file
diff --git a/java/perftests/distribution/pom.xml b/java/perftests/distribution/pom.xml
index 52b275dc02..739170ca75 100644
--- a/java/perftests/distribution/pom.xml
+++ b/java/perftests/distribution/pom.xml
@@ -54,12 +54,12 @@
<version>${pom.version}</version>
</dependency>
<dependency>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit-maven-plugin</artifactId>
<scope>runtime</scope>
</dependency>
diff --git a/java/perftests/etc/scripts/PT-Qpid-13.sh b/java/perftests/etc/scripts/PT-Qpid-13.sh
index c2c6d6fd81..df35f718b9 100755
--- a/java/perftests/etc/scripts/PT-Qpid-13.sh
+++ b/java/perftests/etc/scripts/PT-Qpid-13.sh
@@ -28,15 +28,15 @@ for arg in "$@"; do
done
echo "Starting 6 parallel tests"
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-13.1 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd1 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-13.1 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd1 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-13.2 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd2 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-13.2 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd2 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-13.3 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd3 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-13.3 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd3 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-13.4 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinatioNname=newd4 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-13.4 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinatioNname=newd4 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-13.5 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd5 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-13.5 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd5 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-13.6 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd6 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS}
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-13.6 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=newd6 uniqueDests=true batchSize=250 transacted=true commitBatchSize=50 -o $QPID_WORK/results ${ARGS}
diff --git a/java/perftests/etc/scripts/PT-Qpid-14.sh b/java/perftests/etc/scripts/PT-Qpid-14.sh
index f0adaa1c30..ff5b8a76f9 100755
--- a/java/perftests/etc/scripts/PT-Qpid-14.sh
+++ b/java/perftests/etc/scripts/PT-Qpid-14.sh
@@ -28,14 +28,14 @@ for arg in "$@"; do
done
echo "Starting 6 parallel tests"
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping1 batchSize=250 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping1 batchSize=250 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping2 batchSize=250 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping2 batchSize=250 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping3 batchSize=250 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping3 batchSize=250 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256destinationname=ping4 batchSize=250 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[200] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256destinationname=ping4 batchSize=250 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping5 batchSize=250 -o $QPID_WORK/results ${ARGS} &
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping5 batchSize=250 -o $QPID_WORK/results ${ARGS} &
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping6 batchSize=250 -o $QPID_WORK/results ${ARGS}
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx3072m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.junit.extensions.TKTestRunner -n PT-Qpid-14 -s [250] -c[100] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true messageSize=256 destinationName=ping6 batchSize=250 -o $QPID_WORK/results ${ARGS}
diff --git a/java/perftests/etc/scripts/Test-ActiveMQ.sh b/java/perftests/etc/scripts/Test-ActiveMQ.sh
index 925c93ab09..55813f3b4d 100644
--- a/java/perftests/etc/scripts/Test-ActiveMQ.sh
+++ b/java/perftests/etc/scripts/Test-ActiveMQ.sh
@@ -9,6 +9,6 @@ for arg in "$@"; do
fi
done
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx1024m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp "qpid-perftests-1.0-incubating-M2.1-SNAPSHOT.jar;activemq-jars/apache-activemq-4.1.1.jar" uk.co.thebadgerset.junit.extensions.TKTestRunner -n Test-ActiveMQ -s[1] -r 1 -t testAsyncPingOk -o . org.apache.qpid.ping.PingAsyncTestPerf properties=activemq.properties factoryName=ConnectionFactory ${ARGS}
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx1024m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp "qpid-perftests-1.0-incubating-M2.1-SNAPSHOT.jar;activemq-jars/apache-activemq-4.1.1.jar" org.apache.qpid.junit.extensions.TKTestRunner -n Test-ActiveMQ -s[1] -r 1 -t testAsyncPingOk -o . org.apache.qpid.ping.PingAsyncTestPerf properties=activemq.properties factoryName=ConnectionFactory ${ARGS}
# queueNamePostfix=@router1 overrideClientId=true \ No newline at end of file
diff --git a/java/perftests/etc/scripts/Test-SwiftMQ.sh b/java/perftests/etc/scripts/Test-SwiftMQ.sh
index f10c3cdc3f..d0ab446df6 100644
--- a/java/perftests/etc/scripts/Test-SwiftMQ.sh
+++ b/java/perftests/etc/scripts/Test-SwiftMQ.sh
@@ -9,4 +9,4 @@ for arg in "$@"; do
fi
done
-java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx1024m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp "qpid-perftests-1.0-incubating-M2.1-SNAPSHOT.jar;swiftmqjars/swiftmq.jar" uk.co.thebadgerset.junit.extensions.TKTestRunner -n Test-SwiftMQ -s[1] -r 1 -t testAsyncPingOk -o . org.apache.qpid.ping.PingAsyncTestPerf properties=swiftmq.properties factoryName=ConnectionFactory queueNamePostfix=@router1 overrideClientId=true ${ARGS} \ No newline at end of file
+java -Xms256m -Dlog4j.configuration=perftests.log4j -Xmx1024m -Dbadger.level=warn -Damqj.test.logging.level=info -Damqj.logging.level=warn ${JAVA_OPTS} -cp "qpid-perftests-1.0-incubating-M2.1-SNAPSHOT.jar;swiftmqjars/swiftmq.jar" org.apache.qpid.junit.extensions.TKTestRunner -n Test-SwiftMQ -s[1] -r 1 -t testAsyncPingOk -o . org.apache.qpid.ping.PingAsyncTestPerf properties=swiftmq.properties factoryName=ConnectionFactory queueNamePostfix=@router1 overrideClientId=true ${ARGS} \ No newline at end of file
diff --git a/java/perftests/pom.xml b/java/perftests/pom.xml
index 69deaa383e..49c6353a05 100644
--- a/java/perftests/pom.xml
+++ b/java/perftests/pom.xml
@@ -82,7 +82,7 @@
</dependency>
<dependency>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit</artifactId>
</dependency>
@@ -119,16 +119,16 @@
To run from within maven:
- mvn uk.co.thebadgerset:junit-toolkit-maven-plugin:tktest
+ mvn org.apache.qpid:junit-toolkit-maven-plugin:tktest
To run from the command line (after doing assembly:assembly goal):
- java -cp target/test_jar-jar-with-dependencies.jar uk.co.thebadgerset.junit.extensions.TKTestRunner -s 1 -r 100000
+ java -cp target/test_jar-jar-with-dependencies.jar org.apache.qpid.junit.extensions.TKTestRunner -s 1 -r 100000
-o target org.apache.qpid.requestreply.PingPongTestPerf
To generate the scripts do:
- mvn uk.co.thebadgerset:junit-toolkit-maven-plugin:tkscriptgen
+ mvn org.apache.qpid:junit-toolkit-maven-plugin:tkscriptgen
Then to run the scripts, in the target directory do (after doing assembly:assembly goal):
@@ -137,7 +137,7 @@
These scripts can find everything in the 'all test dependencies' jar created by the assembly:assembly goal.
-->
<plugin>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit-maven-plugin</artifactId>
<configuration>
diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java
index 06081e6ebf..89fc805a34 100644
--- a/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java
+++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java
@@ -27,8 +27,8 @@ import org.apache.log4j.Logger;
import org.apache.qpid.requestreply.PingPongProducer;
-import uk.co.thebadgerset.junit.extensions.TimingController;
-import uk.co.thebadgerset.junit.extensions.TimingControllerAware;
+import org.apache.qpid.junit.extensions.TimingController;
+import org.apache.qpid.junit.extensions.TimingControllerAware;
import javax.jms.JMSException;
import javax.jms.Message;
diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java
index db6f384914..0c8c19243a 100644
--- a/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java
+++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java
@@ -25,8 +25,8 @@ import org.apache.log4j.Logger;
import org.apache.qpid.requestreply.PingPongProducer;
import org.apache.qpid.util.CommandLineParser;
-import uk.co.thebadgerset.junit.extensions.util.MathUtils;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.MathUtils;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.*;
diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java
index 16a6e9c501..215dbcefa3 100644
--- a/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java
+++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java
@@ -26,17 +26,14 @@ import junit.framework.TestSuite;
import org.apache.log4j.Logger;
import org.apache.qpid.client.AMQSession;
-import org.apache.qpid.client.message.AMQMessage;
-import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.requestreply.PingPongProducer;
-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.JMSException;
import javax.jms.Message;
-import javax.jms.ObjectMessage;
import java.util.Collections;
import java.util.HashMap;
diff --git a/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java
index 0c2aa80a09..fb071a87fe 100644
--- a/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java
+++ b/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java
@@ -28,10 +28,10 @@ import org.apache.log4j.Logger;
import org.apache.qpid.requestreply.PingPongProducer;
-import uk.co.thebadgerset.junit.extensions.AsymptoticTestCase;
-import uk.co.thebadgerset.junit.extensions.TestThreadAware;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+import org.apache.qpid.junit.extensions.AsymptoticTestCase;
+import org.apache.qpid.junit.extensions.TestThreadAware;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
import javax.jms.*;
diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java
index 82b36bf233..0712557383 100644
--- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java
+++ b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java
@@ -340,7 +340,7 @@ public class PingPongBouncer implements MessageListener
{
if (_failBeforeCommit)
{
- _logger.trace("Failing Before Commit");
+ _logger.debug("Failing Before Commit");
doFailover();
}
@@ -348,11 +348,11 @@ public class PingPongBouncer implements MessageListener
if (_failAfterCommit)
{
- _logger.trace("Failing After Commit");
+ _logger.debug("Failing After Commit");
doFailover();
}
- _logger.trace("Session Commited.");
+ _logger.debug("Session Commited.");
}
catch (JMSException e)
{
diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java
index c3689151d2..f328675488 100644
--- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java
+++ b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java
@@ -25,10 +25,10 @@ import org.apache.log4j.NDC;
import org.apache.qpid.test.framework.TestUtils;
-import uk.co.thebadgerset.junit.extensions.BatchedThrottle;
-import uk.co.thebadgerset.junit.extensions.Throttle;
-import uk.co.thebadgerset.junit.extensions.util.CommandLineParser;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.BatchedThrottle;
+import org.apache.qpid.junit.extensions.Throttle;
+import org.apache.qpid.junit.extensions.util.CommandLineParser;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.*;
import javax.naming.Context;
diff --git a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java
index e52deeecf1..2610b32220 100644
--- a/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java
+++ b/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java
@@ -26,9 +26,9 @@ import junit.framework.TestSuite;
import org.apache.log4j.Logger;
-import uk.co.thebadgerset.junit.extensions.AsymptoticTestCase;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+import org.apache.qpid.junit.extensions.AsymptoticTestCase;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
import javax.jms.*;
@@ -39,7 +39,7 @@ import javax.jms.*;
*
* <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of the number
* of pings specified by the test size and time how long it takes for all of these to complete. This test may be scaled
- * up using a suitable JUnit test runner. See {@link uk.co.thebadgerset.junit.extensions.TKTestRunner} for more
+ * up using a suitable JUnit test runner. See {@link org.apache.qpid.junit.extensions.TKTestRunner} for more
* information on how to do this.
*
* <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a
diff --git a/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java b/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java
index 663bc11717..f699295b06 100644
--- a/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java
+++ b/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java
@@ -32,11 +32,11 @@ import org.apache.qpid.test.framework.FrameworkBaseCase;
import org.apache.qpid.test.framework.MessagingTestConfigProperties;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-import uk.co.thebadgerset.junit.extensions.TestThreadAware;
-import uk.co.thebadgerset.junit.extensions.TimingController;
-import uk.co.thebadgerset.junit.extensions.TimingControllerAware;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+import org.apache.qpid.junit.extensions.TestThreadAware;
+import org.apache.qpid.junit.extensions.TimingController;
+import org.apache.qpid.junit.extensions.TimingControllerAware;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
import java.util.LinkedList;
@@ -107,7 +107,7 @@ public class MessageThroughputPerf extends FrameworkBaseCase implements TimingCo
}
/**
- * Used by test runners that can supply a {@link uk.co.thebadgerset.junit.extensions.TimingController} to set the
+ * Used by test runners that can supply a {@link org.apache.qpid.junit.extensions.TimingController} to set the
* controller on an aware test.
*
* @param controller The timing controller.
diff --git a/java/plugins/pom.xml b/java/plugins/pom.xml
index 51d22ab01e..54a98947f5 100644
--- a/java/plugins/pom.xml
+++ b/java/plugins/pom.xml
@@ -50,9 +50,9 @@
<version>1.0-incubating-M2.1-SNAPSHOT</version>
</dependency>
<dependency>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit</artifactId>
- <version>0.6.1</version>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
</dependency>
</dependencies>
diff --git a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
index e7d899983e..77cc12df7c 100644
--- a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
+++ b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
@@ -20,7 +20,6 @@
*/
package org.apache.qpid.extras.exchanges.diagnostic;
-import java.lang.instrument.Instrumentation;
import java.util.List;
import java.util.Map;
@@ -38,7 +37,7 @@ import org.apache.qpid.server.management.MBeanDescription;
import org.apache.qpid.server.queue.AMQMessage;
import org.apache.qpid.server.queue.AMQQueue;
-import uk.co.thebadgerset.junit.extensions.util.SizeOf;
+import org.apache.qpid.junit.extensions.util.SizeOf;
/**
*
diff --git a/java/pom.xml b/java/pom.xml
index becea34848..06031ca01e 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -163,9 +163,10 @@ under the License.
<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>
@@ -401,14 +402,13 @@ under the License.
</executions>
</plugin>
+ <!--
<plugin>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit-maven-plugin</artifactId>
<version>0.7.1-SNAPSHOT</version>
</plugin>
-
-
-
+ -->
</plugins>
</pluginManagement>
@@ -549,12 +549,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.7.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
+ -->
<!-- Qpid Version Dependencies -->
<dependency>
@@ -592,6 +594,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>
@@ -697,6 +709,7 @@ under the License.
</releases>
</repository>
+ <!--
<repository>
<id>junittoolkit.snapshots</id>
<url>http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/</url>
@@ -704,6 +717,8 @@ under the License.
<enabled>true</enabled>
</snapshots>
</repository>
+ -->
+
</repositories>
<pluginRepositories>
@@ -725,6 +740,7 @@ under the License.
</snapshots>
</pluginRepository>
+ <!--
<pluginRepository>
<id>junittoolkit.plugin.snapshots</id>
<url>http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/</url>
@@ -732,6 +748,7 @@ under the License.
<enabled>true</enabled>
</snapshots>
</pluginRepository>
+ -->
</pluginRepositories>
diff --git a/java/skimtests/README.txt b/java/skimtests/README.txt
new file mode 100644
index 0000000000..d49ae08e14
--- /dev/null
+++ b/java/skimtests/README.txt
@@ -0,0 +1,10 @@
+This module does not contain any code, but uses the other modules and the extended junit test runner, to run some
+benchmark tests. The idea is that it will do short run of the benchmark tests on every build, in order monitor
+performance changes as changes are applied to the code, and to check that tests will run against an external broker
+(as opposed to an in-vm one, which many of the unit tests use). These are called skim tests because they are a shorter
+run of what would typically be run against the broker when doing a full performance evaluation; each benchmark is
+only run for 1 minute.
+
+One reason this is in a seperate module, is so that in the top-level pom, a profile can exist that must be switched
+on in order to run these skim tests. It is easiest to include/exclude this module in its entirety rather than to
+mess around with maven lifecycle stages. \ No newline at end of file
diff --git a/java/skimtests/etc/jar-with-dependencies.xml b/java/skimtests/etc/jar-with-dependencies.xml
new file mode 100644
index 0000000000..bbbbd3788e
--- /dev/null
+++ b/java/skimtests/etc/jar-with-dependencies.xml
@@ -0,0 +1,91 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+ -
+ http://www.apache.org/licenses/LICENSE-2.0
+ -
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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 is an assembly descriptor that produces a distribution that contains all the
+ dependencies, with a manifest only jar that references them, required to run the
+ tests of a maven project.
+-->
+<assembly>
+ <id>all-test-deps</id>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>/</outputDirectory>
+ <unpack>false</unpack>
+ <scope>test</scope>
+ </dependencySet>
+ </dependencySets>
+ <fileSets>
+ <fileSet>
+ <directory>target/classes</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>target/test-classes</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+
+ <!-- Include all the test scripts, both generated and hand-written. -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.sh</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>etc/scripts</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.sh</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include all jndi configurations needed to run the tests. -->
+ <fileSet>
+ <directory>etc/jndi</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.properties</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include the build artifact. -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>qpid-perftests-1.0-incubating-M2-SNAPSHOT.jar</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include the manifest with classpath jar. -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>qpid-perftests-${qpid.version}.jar</include>
+ </includes>
+ </fileSet>
+
+ </fileSets>
+</assembly>
diff --git a/java/skimtests/pom.xml b/java/skimtests/pom.xml
new file mode 100644
index 0000000000..f0ebc3bc6f
--- /dev/null
+++ b/java/skimtests/pom.xml
@@ -0,0 +1,212 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-skimtests</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <name>Qpid Skim Tests</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>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <log4j.perftests>perftests.log4j</log4j.perftests>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-perftests</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <version>1.0-incubating-M2.1-SNAPSHOT</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-systests</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>junit-toolkit-maven-plugin</artifactId>
+
+ <configuration>
+ <scriptOutDirectory>target</scriptOutDirectory>
+ <testJar>${project.build.finalName}.jar</testJar>
+ <systemproperties>
+ <property>
+ <name>-Xms</name>
+ <value>256m</value>
+ </property>
+ <property>
+ <name>-Xmx</name>
+ <value>1024m</value>
+ </property>
+ <property>
+ <name>log4j.configuration</name>
+ <value>${log4j.perftests}</value>
+ </property>
+ <property>
+ <name>amqj.logging.level</name>
+ <value>warn</value>
+ </property>
+ <property>
+ <name>badger.level</name>
+ <value>warn</value>
+ </property>
+ <property>
+ <name>amqj.test.logging.level</name>
+ <value>info</value>
+ </property>
+ </systemproperties>
+
+ <commands>
+ <!-- Benchmarking tests for throughput. -->
+ <TQBT-TX-Qpid-01>-n TQBT-TX-Qpid-01 -d1M -s[1000] -c[16] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-TX-Qpid-01>
+ <TQBT-AA-Qpid-01>-n TQBT-AA-Qpid-01 -d1M -s[1000] -c[16] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-AA-Qpid-01>
+ <TQBT-NA-Qpid-01>-n TQBT-NA-Qpid-01 -d1M -s[1000] -c[16] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-NA-Qpid-01>
+ <TTBT-TX-Qpid-01>-n TTBT-TX-Qpid-01 -d1M -s[1000] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=8 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TTBT-TX-Qpid-01>
+ <TTBT-AA-Qpid-01>-n TTBT-AA-Qpid-01 -d1M -s[1000] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=8 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TTBT-AA-Qpid-01>
+ <TTBT-NA-Qpid-01>-n TTBT-NA-Qpid-01 -d1M -s[1000] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=8 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TTBT-NA-Qpid-01>
+
+ <!-- Leave the persistent ones out for the moment, need to include the file store and decide where to put it too.
+ <PQBT-TX-Qpid-01>-n PQBT-TX-Qpid-01 -d1M -s[1000] -c[16] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-TX-Qpid-01>
+ <PQBT-AA-Qpid-01>-n PQBT-AA-Qpid-01 -d1M -s[1000] -c[16] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-AA-Qpid-01>
+ <PTBT-TX-Qpid-01>-n PTBT-TX-Qpid-01 -d1M -s[1000] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=8 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PTBT-TX-Qpid-01>
+ <PTBT-AA-Qpid-01>-n PTBT-AA-Qpid-01 -d1M -s[1000] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=8 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PTBT-AA-Qpid-01>
+ -->
+ </commands>
+ </configuration>
+
+ <executions>
+ <execution>
+ <phase>test</phase>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>${antrun.version}</version>
+ <dependencies>
+ <dependency>
+ <groupId>ant</groupId>
+ <artifactId>ant-nodeps</artifactId>
+ <version>1.6.5</version>
+ </dependency>
+ </dependencies>
+
+ <executions>
+ <execution>
+ <id>skim_test</id>
+ <phase>test</phase>
+ <configuration>
+ <tasks>
+ <mkdir dir="target/skim-test-results"/>
+
+ <property name="command"
+ value="find target/ -name '*Qpid-01.sh' -exec -o target/skim-test-results {} \;"/>
+
+ <java classname="org.apache.qpid.server.RunBrokerWithCommand" fork="true" failonerror="true">
+ <arg value="${command}"/>
+ <arg value="-p"/>
+ <arg value="2110"/>
+ <arg value="-m"/>
+ <arg value="2111"/>
+
+ <classpath refid="maven.test.classpath"/>
+ <sysproperty key="QPID_HOME" value="../broker"/>
+ <sysproperty key="QPID_WORK" value="target/"/>
+ </java>
+
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ </build>
+
+</project>
diff --git a/java/systests/etc/bin/fail.py b/java/systests/etc/bin/fail.py
new file mode 100755
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 d3ecfa91a2..e6c1fa092e 100644
--- a/java/systests/pom.xml
+++ b/java/systests/pom.xml
@@ -6,9 +6,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
@@ -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,18 +82,26 @@
<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/RollbackTest.class</exclude>
<exclude>**/testcases/TTLTest.class</exclude>
<exclude>**/testcases/FailoverTest.class</exclude>
</excludes>
@@ -102,7 +110,7 @@
<!-- Runs the framework based tests against an in-vm broker. -->
<plugin>
- <groupId>uk.co.thebadgerset</groupId>
+ <groupId>org.apache.qpid</groupId>
<artifactId>junit-toolkit-maven-plugin</artifactId>
<configuration>
@@ -112,23 +120,23 @@
<value>${log4j.configuration}</value>
</property>
</systemproperties>
-
- <testrunner>uk.co.thebadgerset.junit.extensions.TKTestRunner</testrunner>
-
+
+ <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>
@@ -147,22 +155,22 @@
<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>
+ <!--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>
+ <executions>
<execution>
<id>framework_tests</id>
- <phase>test</phase>
+ <phase>test</phase>
<goals>
<goal>tktest</goal>
- </goals>
+ </goals>
</execution>
</executions>
</plugin>
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 10189a8017..42d9cccb4f 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
@@ -117,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;
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
index d3f79f84b6..9b2a3a6e28 100644
--- 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
@@ -1,19 +1,4 @@
-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;/*
+/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -33,26 +18,51 @@ import java.util.concurrent.CountDownLatch;/*
* 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 static final Logger _logger = LoggerFactory.getLogger(FailoverMethodTest.class);
private CountDownLatch _failoverComplete = new CountDownLatch(1);
public void setUp() throws AMQVMBrokerCreationException
{
+ TransportConnection.createVMBroker(1);
}
public void tearDown() throws AMQVMBrokerCreationException
{
+ TransportConnection.killAllVMBrokers();
}
- public void testFailoverRoundRobinDelay() throws URLSyntaxException, AMQVMBrokerCreationException, InterruptedException, JMSException
+ /**
+ * 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);
- TransportConnection.createVMBroker(1);
try
{
@@ -64,9 +74,15 @@ public class FailoverMethodTest extends TestCase implements ExceptionListener
TransportConnection.killAllVMBrokers();
_failoverComplete.await();
+
long end = System.currentTimeMillis();
- assertTrue("Failover took at over 10seconds", (end - start) > 6000);
+ //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)
@@ -80,7 +96,6 @@ public class FailoverMethodTest extends TestCase implements ExceptionListener
String connectionString = "amqp://guest:guest@/test?brokerlist='vm://:1?connectdelay='2000',retries='3''";
AMQConnectionURL url = new AMQConnectionURL(connectionString);
- TransportConnection.createVMBroker(1);
try
{
@@ -92,9 +107,16 @@ public class FailoverMethodTest extends TestCase implements ExceptionListener
TransportConnection.killAllVMBrokers();
_failoverComplete.await();
+
long end = System.currentTimeMillis();
- assertTrue("Failover took at over 10seconds", (end - start) > 6000);
+ //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)
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
index 1b082beee4..0221f7e275 100644
--- 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
@@ -1,9 +1,7 @@
package org.apache.qpid.server.plugins;
-import java.util.Collection;
import java.util.Map;
-import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.exchange.ExchangeType;
import junit.framework.TestCase;
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 bb9b201e5d..5fbea5e14f 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
@@ -24,20 +24,15 @@ import junit.framework.TestCase;
import org.apache.log4j.Logger;
-import org.apache.mina.common.IoSession;
-
import org.apache.qpid.AMQException;
import org.apache.qpid.codec.AMQCodecFactory;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.QueueRegistry;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.SkeletonMessageStore;
-import org.apache.qpid.server.virtualhost.VirtualHost;
import javax.management.JMException;
@@ -62,7 +57,7 @@ public class AMQProtocolSessionMBeanTest extends TestCase
AMQQueue queue =
new org.apache.qpid.server.queue.AMQQueue(new AMQShortString("testQueue_" + System.currentTimeMillis()), false,
new AMQShortString("test"), true, _protocolSession.getVirtualHost());
- AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore, null);
+ AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore);
channel.setDefaultQueue(queue);
_protocolSession.addChannel(channel);
channelCount = _mbean.channels().size();
@@ -73,7 +68,7 @@ public class AMQProtocolSessionMBeanTest extends TestCase
assertTrue(_mbean.getMaximumNumberOfChannels() == 1000L);
// check APIs
- AMQChannel channel3 = new AMQChannel(_protocolSession, 3, _messageStore, null);
+ AMQChannel channel3 = new AMQChannel(_protocolSession, 3, _messageStore);
channel3.setLocalTransactional();
_protocolSession.addChannel(channel3);
_mbean.rollbackTransactions(2);
@@ -93,14 +88,14 @@ public class AMQProtocolSessionMBeanTest extends TestCase
}
// check if closing of session works
- _protocolSession.addChannel(new AMQChannel(_protocolSession, 5, _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, _messageStore, null));
+ _protocolSession.addChannel(new AMQChannel(_protocolSession, 6, _messageStore));
fail();
}
catch (AMQException ex)
@@ -119,7 +114,7 @@ public class AMQProtocolSessionMBeanTest extends TestCase
new AMQMinaProtocolSession(new MockIoSession(), appRegistry.getVirtualHostRegistry(), new AMQCodecFactory(true),
null);
_protocolSession.setVirtualHost(appRegistry.getVirtualHostRegistry().getVirtualHost("test"));
- _channel = new AMQChannel(_protocolSession, 1, _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 ae02c1c28c..5adfebbffb 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
@@ -21,22 +21,12 @@
package org.apache.qpid.server.protocol;
import junit.framework.TestCase;
-import org.apache.mina.common.IoSession;
import org.apache.qpid.codec.AMQCodecFactory;
import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.registry.IApplicationRegistry;
import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.exchange.ExchangeRegistry;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.store.MessageStore;
-import org.apache.qpid.server.store.SkeletonMessageStore;
import org.apache.qpid.AMQException;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.framing.AMQShortString;
-
-import javax.management.JMException;
/** Test class to test MBean operations for AMQMinaProtocolSession. */
public class MaxChannelsTest extends TestCase
@@ -65,7 +55,7 @@ public class MaxChannelsTest extends TestCase
{
for (long currentChannel = 0L; currentChannel < maxChannels; currentChannel++)
{
- _protocolSession.addChannel(new AMQChannel(_protocolSession, (int) currentChannel, 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 790607e268..95ffb505fb 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
@@ -24,7 +24,6 @@ import junit.framework.TestCase;
import org.apache.log4j.Logger;
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;
@@ -37,7 +36,6 @@ import org.apache.qpid.server.store.TestableMemoryMessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.server.txn.TransactionalContext;
-import org.apache.qpid.server.util.TestApplicationRegistry;
import org.apache.qpid.server.util.NullApplicationRegistry;
import java.util.LinkedList;
@@ -77,7 +75,7 @@ public class AckTest extends TestCase
super.setUp();
_messageStore = new TestableMemoryMessageStore();
_protocolSession = new MockProtocolSession(_messageStore);
- _channel = new AMQChannel(_protocolSession,5, _messageStore, null/*dont need exchange registry*/);
+ _channel = new AMQChannel(_protocolSession,5, _messageStore /*dont need exchange registry*/);
_protocolSession.addChannel(_channel);
_subscriptionManager = new SubscriptionSet();
@@ -107,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;
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 812aec6a5d..521bedeccd 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
@@ -70,6 +70,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;
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 fcee3c7de4..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
@@ -190,6 +190,26 @@ public class MockProtocolSession implements AMQProtocolSession
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.
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 5846ad0a9d..458b510ef5 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
@@ -89,6 +89,11 @@ public class SubscriptionTestHelper implements Subscription
return null;
}
+ public void start()
+ {
+ //no-op
+ }
+
public void queueDeleted(AMQQueue queue)
{
}
@@ -123,11 +128,6 @@ public class SubscriptionTestHelper implements Subscription
//no-op
}
- public boolean isAutoClose()
- {
- return false;
- }
-
public void close()
{
//no-op
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..374c69fa00 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
@@ -61,6 +61,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;
@@ -109,6 +114,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;
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 0218109369..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
@@ -31,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;
@@ -54,7 +54,7 @@ public class TestApplicationRegistry extends ApplicationRegistry
private ManagedObjectRegistry _managedObjectRegistry;
- private AccessManager _accessManager;
+ private ACLPlugin _accessManager;
private PrincipalDatabaseManager _databaseManager;
@@ -137,11 +137,16 @@ 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;
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 624d9c9f3d..cf5e72eebc 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
@@ -21,15 +21,26 @@
package org.apache.qpid.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.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.Session;
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.Iterator;
import java.util.Map;
public class VMTestCase extends TestCase
@@ -85,14 +96,14 @@ public class VMTestCase extends TestCase
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())
{
@@ -104,20 +115,50 @@ public class VMTestCase extends TestCase
protected void tearDown() throws Exception
{
+ //Disabled
+// checkQueuesClean();
+
TransportConnection.killVMBroker(1);
ApplicationRegistry.remove(1);
super.tearDown();
}
- public int getMessageCount(String queueName)
+ private void checkQueuesClean() throws NamingException, JMSException
{
- return ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(_virtualhost.substring(1))
- .getQueueRegistry().getQueue(new AMQShortString(queueName)).getMessageCount();
+ 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 void testDummyinVMTestCase()
+ 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
index 037c8285bc..463946e14a 100644
--- 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
@@ -1,5 +1,8 @@
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;
@@ -69,21 +72,24 @@ public class DupsOkTest extends VMTestCase
producerConnection.close();
}
- public void testDupsOK() throws NamingException, JMSException, InterruptedException
+ public void testDupsOK() throws NamingException, JMSException, InterruptedException, AMQException
{
//Create Client
Connection clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
clientConnection.start();
- Session clientSession = clientConnection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
+ 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");
@@ -98,12 +104,26 @@ public class DupsOkTest extends VMTestCase
assertEquals("The queue should have 4999 msgs left", 4999, getMessageCount(_queue.getQueueName()));
}*/
- if (message.getIntProperty("count") == 9999)
+ if (message.getIntProperty("count") == MSG_COUNT)
{
- assertEquals("The queue should have 0 msgs left", 0, getMessageCount(_queue.getQueueName()));
-
- //This is the last message so release test.
- _awaitCompletion.countDown();
+ 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();
+ }
}
}
@@ -131,9 +151,9 @@ public class DupsOkTest extends VMTestCase
// consumer.close();
+ assertEquals("The queue should have 0 msgs left", 0, ((AMQSession) clientSession).getQueueDepth((AMQDestination) _queue));
clientConnection.close();
- assertEquals("The queue should have 0 msgs left", 0, getMessageCount(_queue.getQueueName()));
}
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/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java
index 3664be58bc..0ef0217234 100644
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.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
@@ -18,31 +18,32 @@
* under the License.
*
*/
-package org.apache.qpid.server.cluster.replay;
+package org.apache.qpid.test.client;
-import org.apache.qpid.framing.AMQMethodBody;
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+import javax.jms.Session;
-abstract class ChainedMethodRecorder <T extends AMQMethodBody> implements MethodRecorder<T>
+public class QueueBrowserClientAckTest extends QueueBrowserAutoAckTest
{
- private final MethodRecorder<T> _recorder;
-
- ChainedMethodRecorder()
+ public void setUp() throws Exception
{
- this(null);
- }
- ChainedMethodRecorder(MethodRecorder<T> recorder)
- {
- _recorder = recorder;
- }
+ super.setUp();
- public final void record(T method)
- {
- if(!doRecord(method) && _recorder != null)
- {
- _recorder.record(method);
- }
- }
+ _clientConnection.close();
+ _clientSession.close();
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
- protected abstract boolean doRecord(T method);
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+ }
}
diff --git a/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java
index 1ec5154a98..80d74b1b79 100644
--- a/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.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
@@ -18,36 +18,32 @@
* under the License.
*
*/
-package org.apache.qpid.server.cluster;
+package org.apache.qpid.test.client;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQDataBlock;
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+import javax.jms.Session;
-import java.util.ArrayList;
-import java.util.List;
-
-class RecordingBroker extends TestBroker
+public class QueueBrowserDupsOkTest extends QueueBrowserAutoAckTest
{
- private final List<AMQDataBlock> _messages = new ArrayList<AMQDataBlock>();
-
- RecordingBroker(String host, int port)
+ public void setUp() throws Exception
{
- super(host, port);
- }
- public void send(AMQDataBlock data) throws AMQException
- {
- _messages.add(data);
- }
+ super.setUp();
- List<AMQDataBlock> getMessages()
- {
- return _messages;
- }
+ _clientConnection.close();
+ _clientSession.close();
- void clear()
- {
- _messages.clear();
- }
+ _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/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java
index ebe1fe47dd..42e13c89e4 100644
--- a/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.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
@@ -18,44 +18,36 @@
* under the License.
*
*/
-package org.apache.qpid.server.cluster.util;
+package org.apache.qpid.test.client;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.qpid.client.AMQSession;
-/**
- * Maps a key to a collection of values
- *
- */
-public class MultiValuedMap<K, V>
-{
- private Map<K, Collection<V>> _map = new HashMap<K, Collection<V>>();
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
- public boolean add(K key, V value)
+public class QueueBrowserPreAckTest extends QueueBrowserAutoAckTest
+{
+ public void setUp() throws Exception
{
- Collection<V> values = get(key);
- if (values == null)
- {
- values = createList();
- _map.put(key, values);
- }
- return values.add(value);
- }
- public Collection<V> get(K key)
- {
- return _map.get(key);
- }
+ super.setUp();
- public Collection<V> remove(K key)
- {
- return _map.remove(key);
- }
+ _clientConnection.close();
+ _clientSession.close();
- protected Collection<V> createList()
- {
- return new ArrayList<V>();
+ _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
index fffe073362..9d1e461f05 100644
--- 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
@@ -7,6 +7,7 @@ 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;
@@ -49,7 +50,6 @@ public class FailoverTest extends TestCase implements ConnectionListener
TransportConnection.createVMBroker(usedBrokers);
}
- //undo last addition
conFactory = new AMQConnectionFactory(String.format(BROKER, usedBrokers - 1, usedBrokers));
_logger.info("Connecting on:" + conFactory.getConnectionURL());
@@ -197,6 +197,20 @@ public class FailoverTest extends TestCase implements ConnectionListener
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)
{
}
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java b/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java
index a8f79ad59d..706d99ffe2 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java
@@ -20,7 +20,7 @@
*/
package org.apache.qpid.test.framework;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+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.
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 e2106eaca0..ceb9baf49c 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
@@ -26,11 +26,11 @@ import org.apache.log4j.NDC;
import org.apache.qpid.test.framework.BrokerLifecycleAware;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-import uk.co.thebadgerset.junit.extensions.AsymptoticTestCase;
-import uk.co.thebadgerset.junit.extensions.SetupTaskAware;
-import uk.co.thebadgerset.junit.extensions.SetupTaskHandler;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+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;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java
index dac826fd5e..d1fcad9cc0 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java
@@ -26,7 +26,7 @@ import org.apache.qpid.client.AMQSession;
import org.apache.qpid.test.framework.localcircuit.LocalAMQPPublisherImpl;
import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.*;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java
index f18ba521fa..38a924a4ee 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java
@@ -28,7 +28,7 @@ import org.apache.qpid.test.framework.localcircuit.LocalReceiverImpl;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
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.*;
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 2c0f39d54c..574b4333e0 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,7 +20,7 @@
*/
package org.apache.qpid.test.framework;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
import javax.jms.Session;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java b/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java
index c9bba54c51..63c7fd61c3 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java
@@ -22,7 +22,7 @@ package org.apache.qpid.test.framework;
import org.apache.log4j.Logger;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+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
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 418776b5cb..994371d4b3 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,7 +20,7 @@
*/
package org.apache.qpid.test.framework;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import org.apache.qpid.junit.extensions.util.ParsedProperties;
/**
* A Publisher represents the status of the publishing side of a test circuit. Its main purpose is to provide assertions
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 4fd4ffeb48..8f9a246c70 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,7 +20,7 @@
*/
package org.apache.qpid.test.framework;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+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
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 6ad6185ece..3d4d9b36ec 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;
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 3a2655340e..1a08fe4b0f 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
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 0b3f6865d6..362236b1b8 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,9 +20,7 @@
*/
package org.apache.qpid.test.framework.clocksynch;
-import org.apache.log4j.Logger;
-
-import uk.co.thebadgerset.junit.extensions.ShutdownHookable;
+import org.apache.qpid.junit.extensions.ShutdownHookable;
import java.io.IOException;
import java.net.*;
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 78f05767d3..4f37a655c6 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.*;
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 628729cb51..a26b4b5c11 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 693fd854c7..712dc0ea24 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,7 +23,7 @@ package org.apache.qpid.test.framework.distributedcircuit;
import org.apache.qpid.test.framework.Assertion;
import org.apache.qpid.test.framework.Publisher;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+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
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 14782ee5e2..32307ce782 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,7 +23,7 @@ package org.apache.qpid.test.framework.distributedcircuit;
import org.apache.qpid.test.framework.Assertion;
import org.apache.qpid.test.framework.Receiver;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+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
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 845b0a39f6..8446bfa883 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
@@ -25,8 +25,8 @@ import org.apache.log4j.Logger;
import org.apache.qpid.test.framework.*;
import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
-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.*;
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 02fcb7fb55..2975bcc18a 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
@@ -1,32 +1,18 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
+/* Copyright Rupert Smith, 2005 to 2007, all rights reserved. */
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;
@@ -34,18 +20,12 @@ import org.apache.qpid.test.framework.TestUtils;
import org.apache.qpid.test.framework.clocksynch.UDPClockReference;
import org.apache.qpid.util.ConversationFactory;
-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
@@ -137,8 +117,7 @@ public class Coordinator extends TKTestRunner
String reportDir, String runName, boolean verbose, String brokerUrl, String virtualHost, TestEngine engine,
boolean terminate, boolean csv, boolean xml, List<TestDecoratorFactory> decoratorFactories)
{
- super(repetitions, duration, threads, delay, params, testCaseName, reportDir, runName, csv, xml, verbose,
- decoratorFactories);
+ 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 = "
@@ -480,7 +459,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.");
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 c2f34b44fc..958df63d45 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 995aa5e71d..c68ca5f7ef 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 00285ab37e..e9636e35da 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;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java
index b011e52ca7..1c138fe575 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java
@@ -30,9 +30,9 @@ import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer;
import org.apache.qpid.util.ReflectionUtils;
import org.apache.qpid.util.ReflectionUtilsException;
-import uk.co.thebadgerset.junit.extensions.SleepThrottle;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+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.*;
@@ -154,8 +154,8 @@ public class TestClient implements MessageListener
// 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(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args,
- new uk.co.thebadgerset.junit.extensions.util.CommandLineParser(
+ 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" },
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 014dd21292..bed6f0dfd6 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
index 44a807ed6d..14ae108da8 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java
@@ -24,7 +24,7 @@ import org.apache.qpid.client.AMQNoConsumersException;
import org.apache.qpid.client.AMQNoRouteException;
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.MessageConsumer;
import javax.jms.MessageProducer;
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 9c6220d97f..3cf5276b4d 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
@@ -24,7 +24,7 @@ import org.apache.log4j.Logger;
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.*;
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 bd15839e41..77d6754f9c 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
@@ -22,7 +22,7 @@ package org.apache.qpid.test.framework.localcircuit;
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.MessageConsumer;
import javax.jms.MessageProducer;
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 9a86c00a1b..f349a521a1 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,7 +22,7 @@ package org.apache.qpid.test.framework.localcircuit;
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.MessageConsumer;
import javax.jms.MessageProducer;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java
index 3a44531ad2..4545e3c164 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java
@@ -26,7 +26,7 @@ import junit.framework.TestResult;
import org.apache.qpid.test.framework.FrameworkBaseCase;
import org.apache.qpid.test.framework.LocalAMQPCircuitFactory;
-import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
/**
* AMQPFeatureDecorator applies decorations to {@link FrameworkBaseCase} tests, so that they may use Qpid/AMQP specific
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java
index 2e41e5c0c6..3a048ac042 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java
@@ -26,7 +26,7 @@ import junit.framework.TestResult;
import org.apache.qpid.test.framework.BrokerLifecycleAware;
import org.apache.qpid.test.framework.CauseFailureUserPrompt;
-import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
/**
* CauseFailureDecorator applies decorations to {@link BrokerLifecycleAware} tests, so that they may use different failure
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java
index a7ea9f9c7f..bcf052ea06 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java
@@ -29,8 +29,8 @@ import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.test.framework.BrokerLifecycleAware;
import org.apache.qpid.test.framework.FrameworkBaseCase;
-import uk.co.thebadgerset.junit.extensions.SetupTaskAware;
-import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+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
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 ade96427ac..8e9591580f 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,13 +25,9 @@ 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;
/**
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 ff4ee741ad..a82bb7c382 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;
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 4c56a68d8f..77889e8651 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;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java
index c7bbd70e99..845c3ed9c8 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java
@@ -27,8 +27,7 @@ import org.apache.qpid.test.framework.MessagingTestConfigProperties;
import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
-import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+import org.apache.qpid.junit.extensions.util.TestContextProperties;
/**
* ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS
diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java
index 7391bf23d2..066b4e24ba 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java
@@ -27,8 +27,8 @@ import org.apache.qpid.test.framework.MessagingTestConfigProperties;
import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-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;
/**
* MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS
diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java
index 163a6a67f6..f39d22bc67 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java
@@ -26,8 +26,8 @@ import org.apache.qpid.test.framework.MessagingTestConfigProperties;
import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
-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;
/**
* RollbackTest tests the rollback ability of transactional messaging.
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
index f83e6e51cb..8fddf651b4 100644
--- 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
@@ -33,7 +33,9 @@ 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;
@@ -113,7 +115,7 @@ public class AcknowledgeTest extends VMTestCase
_consumerB = _consumerSession.createConsumer(_queue);
sendMessages(NUM_MESSAGES/2);
int count = 0;
- Message msg = _consumerB.receive(100);
+ Message msg = _consumerB.receive(1500);
while (msg != null)
{
if (mode == Session.CLIENT_ACKNOWLEDGE)
@@ -130,7 +132,28 @@ public class AcknowledgeTest extends VMTestCase
_consumerA.close();
_consumerB.close();
_consumerSession.close();
- assertEquals("Wrong number of messages on queue", NUM_MESSAGES - count, getMessageCount(_queue.getQueueName()));
+ 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
diff --git a/python/tests/unbind.py b/python/tests/unbind.py
new file mode 100644
index 0000000000..2ae843a604
--- /dev/null
+++ b/python/tests/unbind.py
@@ -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.
+#
+from qpid.client import Client, Closed
+from qpid.queue import Empty
+from qpid.content import Content
+from qpid.testlib import testrunner, TestBase
+
+class UnbindTests(TestBase):
+ """Tests for the unbind method introduced in 0-9"""
+ def test_unbind_direct(self):
+ self.unbind_test(exchange="amq.direct", routing_key="key")
+
+ def test_unbind_topic(self):
+ self.unbind_test(exchange="amq.topic", routing_key="key")
+
+ def test_unbind_fanout(self):
+ self.unbind_test(exchange="amq.fanout")
+
+ def test_unbind_headers(self):
+ self.unbind_test(exchange="amq.match", args={ "x-match":"all", "a":"b"}, headers={"a":"b"})
+
+ def unbind_test(self, exchange, routing_key="", args={}, headers={}):
+ #bind two queues and consume from them
+ channel = self.channel
+
+ self.queue_declare(queue="unbind-queue-1")
+ self.queue_declare(queue="unbind-queue-2")
+
+ channel.basic_consume(queue="unbind-queue-1", consumer_tag="unbind-queue-1", no_ack=True)
+ channel.basic_consume(queue="unbind-queue-2", consumer_tag="unbind-queue-2", no_ack=True)
+
+ queue1 = self.client.queue("unbind-queue-1")
+ queue2 = self.client.queue("unbind-queue-2")
+
+ channel.queue_bind(exchange=exchange, queue="unbind-queue-1", routing_key=routing_key, arguments=args)
+ channel.queue_bind(exchange=exchange, queue="unbind-queue-2", routing_key=routing_key, arguments=args)
+
+ #send a message that will match both bindings
+ channel.basic_publish(exchange=exchange, routing_key=routing_key, content=Content("one", properties={'headers':headers}))
+
+ #unbind first queue
+ channel.queue_unbind(exchange=exchange, queue="unbind-queue-1", routing_key=routing_key, arguments=args)
+
+ #send another message
+ channel.basic_publish(exchange=exchange, routing_key=routing_key, content=Content("two", properties={'headers':headers}))
+
+ #check one queue has both messages and the other has only one
+ self.assertEquals("one", queue1.get(timeout=1).content.body)
+ try:
+ msg = queue1.get(timeout=1)
+ self.fail("Got extra message: %s" % msg.content.body)
+ except Empty: pass
+
+ self.assertEquals("one", queue2.get(timeout=1).content.body)
+ self.assertEquals("two", queue2.get(timeout=1).content.body)
+ try:
+ msg = queue2.get(timeout=1)
+ self.fail("Got extra message: " + msg)
+ except Empty: pass
+
diff --git a/specs/amqp.0-9.no-wip.xml b/specs/amqp.0-9.no-wip.xml
new file mode 100644
index 0000000000..f53c23c2c5
--- /dev/null
+++ b/specs/amqp.0-9.no-wip.xml
@@ -0,0 +1,4445 @@
+<?xml version = "1.0"?>
+
+<!--
+ EDITORS: (PH) Pieter Hintjens <ph@imatix.com>
+ (KvdR) Kim van der Riet <kim.vdriet@redhat.com>
+
+ These editors have been assigned by the AMQP working group.
+ Please do not edit/commit this file without consulting with
+ one of the above editors.
+ ========================================================
+
+ TODOs
+ - see TODO comments in the text
+-->
+
+<!--
+ Copyright Notice
+ ================
+ (c) Copyright JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc.,
+ iMatix Corporation, IONA\ufffd Technologies, Red Hat, Inc.,
+ TWIST Process Innovations, and 29West Inc. 2006. All rights reserved.
+
+ License
+ =======
+ JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc., iMatix
+ Corporation, IONA Technologies, Red Hat, Inc., TWIST Process Innovations, and
+ 29West Inc. (collectively, the "Authors") each hereby grants to you a worldwide,
+ perpetual, royalty-free, nontransferable, nonexclusive license to
+ (i) copy, display, distribute and implement the Advanced Messaging Queue Protocol
+ ("AMQP") Specification and (ii) the Licensed Claims that are held by
+ the Authors, all for the purpose of implementing the Advanced Messaging
+ Queue Protocol Specification. Your license and any rights under this
+ Agreement will terminate immediately without notice from
+ any Author if you bring any claim, suit, demand, or action related to
+ the Advanced Messaging Queue Protocol Specification against any Author.
+ Upon termination, you shall destroy all copies of the Advanced Messaging
+ Queue Protocol Specification in your possession or control.
+
+ As used hereunder, "Licensed Claims" means those claims of a patent or
+ patent application, throughout the world, excluding design patents and
+ design registrations, owned or controlled, or that can be sublicensed
+ without fee and in compliance with the requirements of this
+ Agreement, by an Author or its affiliates now or at any
+ future time and which would necessarily be infringed by implementation
+ of the Advanced Messaging Queue Protocol Specification. A claim is
+ necessarily infringed hereunder only when it is not possible to avoid
+ infringing it because there is no plausible non-infringing alternative
+ for implementing the required portions of the Advanced Messaging Queue
+ Protocol Specification. Notwithstanding the foregoing, Licensed Claims
+ shall not include any claims other than as set forth above even if
+ contained in the same patent as Licensed Claims; or that read solely
+ on any implementations of any portion of the Advanced Messaging Queue
+ Protocol Specification that are not required by the Advanced Messaging
+ Queue Protocol Specification, or that, if licensed, would require a
+ payment of royalties by the licensor to unaffiliated third parties.
+ Moreover, Licensed Claims shall not include (i) any enabling technologies
+ that may be necessary to make or use any Licensed Product but are not
+ themselves expressly set forth in the Advanced Messaging Queue Protocol
+ Specification (e.g., semiconductor manufacturing technology, compiler
+ technology, object oriented technology, networking technology, operating
+ system technology, and the like); or (ii) the implementation of other
+ published standards developed elsewhere and merely referred to in the
+ body of the Advanced Messaging Queue Protocol Specification, or
+ (iii) any Licensed Product and any combinations thereof the purpose or
+ function of which is not required for compliance with the Advanced
+ Messaging Queue Protocol Specification. For purposes of this definition,
+ the Advanced Messaging Queue Protocol Specification shall be deemed to
+ include both architectural and interconnection requirements essential
+ for interoperability and may also include supporting source code artifacts
+ where such architectural, interconnection requirements and source code
+ artifacts are expressly identified as being required or documentation to
+ achieve compliance with the Advanced Messaging Queue Protocol Specification.
+
+ As used hereunder, "Licensed Products" means only those specific portions
+ of products (hardware, software or combinations thereof) that implement
+ and are compliant with all relevant portions of the Advanced Messaging
+ Queue Protocol Specification.
+
+ The following disclaimers, which you hereby also acknowledge as to any
+ use you may make of the Advanced Messaging Queue Protocol Specification:
+
+ THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION IS PROVIDED "AS IS,"
+ AND THE AUTHORS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+ IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE
+ CONTENTS OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION ARE
+ SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF THE ADVANCED
+ MESSAGING QUEUE PROTOCOL SPECIFICATION WILL NOT INFRINGE ANY THIRD PARTY
+ PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+
+ THE AUTHORS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL,
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO ANY
+ USE, IMPLEMENTATION OR DISTRIBUTION OF THE ADVANCED MESSAGING QUEUE
+ PROTOCOL SPECIFICATION.
+
+ The name and trademarks of the Authors may NOT be used in any manner,
+ including advertising or publicity pertaining to the Advanced Messaging
+ Queue Protocol Specification or its contents without specific, written
+ prior permission. Title to copyright in the Advanced Messaging Queue
+ Protocol Specification will at all times remain with the Authors.
+
+ No other rights are granted by implication, estoppel or otherwise.
+
+ Upon termination of your license or rights under this Agreement, you
+ shall destroy all copies of the Advanced Messaging Queue Protocol
+ Specification in your possession or control.
+
+ Trademarks
+ ==========
+ "JPMorgan", "JPMorgan Chase", "Chase", the JPMorgan Chase logo and the
+ Octagon Symbol are trademarks of JPMorgan Chase & Co.
+
+ IMATIX and the iMatix logo are trademarks of iMatix Corporation sprl.
+
+ IONA, IONA Technologies, and the IONA logos are trademarks of IONA
+ Technologies PLC and/or its subsidiaries.
+
+ LINUX is a trademark of Linus Torvalds. RED HAT and JBOSS are registered
+ trademarks of Red Hat, Inc. in the US and other countries.
+
+ Java, all Java-based trademarks and OpenOffice.org are trademarks of
+ Sun Microsystems, Inc. in the United States, other countries, or both.
+
+ Other company, product, or service names may be trademarks or service
+ marks of others.
+
+ Links to full AMQP specification:
+ =================================
+ http://www.envoytech.org/spec/amq/
+ http://www.iona.com/opensource/amqp/
+ http://www.redhat.com/solutions/specifications/amqp/
+ http://www.twiststandards.org/tiki-index.php?page=AMQ
+ http://www.imatix.com/amqp
+-->
+
+<!--
+ <!DOCTYPE amqp SYSTEM "amqp.dtd">
+-->
+
+<!-- XML Notes
+
+ We use entities to indicate repetition; attributes to indicate properties.
+
+ We use the 'name' attribute as an identifier, usually within the context
+ of the surrounding entities.
+
+ We use spaces to seperate words in names, so that we can print names in
+ their natural form depending on the context - underlines for source code,
+ hyphens for written text, etc.
+
+ We do not enforce any particular validation mechanism but we support all
+ mechanisms. The protocol definition conforms to a formal grammar that is
+ published seperately in several technologies.
+
+ -->
+
+<amqp major = "0" minor = "9" port = "5672" comment = "AMQ Protocol version 0-9">
+ <!--
+ ======================================================
+ == CONSTANTS
+ ======================================================
+ -->
+ <!-- Frame types -->
+ <constant name = "frame-method" value = "1" />
+ <constant name = "frame-header" value = "2" />
+ <constant name = "frame-body" value = "3" />
+ <constant name = "frame-oob-method" value = "4" />
+ <constant name = "frame-oob-header" value = "5" />
+ <constant name = "frame-oob-body" value = "6" />
+ <constant name = "frame-trace" value = "7" />
+ <constant name = "frame-heartbeat" value = "8" />
+
+ <!-- Protocol constants -->
+ <constant name = "frame-min-size" value = "4096" />
+ <constant name = "frame-end" value = "206" />
+
+ <!-- Reply codes -->
+ <constant name = "reply-success" value = "200">
+ <doc>
+ Indicates that the method completed successfully. This reply code is
+ reserved for future use - the current protocol design does not use positive
+ confirmation and reply codes are sent only in case of an error.
+ </doc>
+ </constant>
+
+ <constant name = "not-delivered" value = "310" class = "soft-error">
+ <doc>
+ The client asked for a specific message that is no longer available.
+ The message was delivered to another client, or was purged from the queue
+ for some other reason.
+ </doc>
+ </constant>
+
+ <constant name = "content-too-large" value = "311" class = "soft-error">
+ <doc>
+ The client attempted to transfer content larger than the server could accept
+ at the present time. The client may retry at a later time.
+ </doc>
+ </constant>
+
+ <constant name = "no-route" value = "312" class = "soft-error">
+ <doc>
+ When the exchange cannot route the result of a .Publish, most likely due
+ to an invalid routing key. Only when the mandatory flag is set.
+ </doc>
+ </constant>
+
+ <constant name = "no-consumers" value = "313" class = "soft-error">
+ <doc>
+ When the exchange cannot deliver to a consumer when the immediate flag is
+ set. As a result of pending data on the queue or the absence of any
+ consumers of the queue.
+ </doc>
+ </constant>
+
+ <constant name = "connection-forced" value = "320" class = "hard-error">
+ <doc>
+ An operator intervened to close the connection for some reason. The client
+ may retry at some later date.
+ </doc>
+ </constant>
+
+ <constant name = "invalid-path" value = "402" class = "hard-error">
+ <doc>
+ The client tried to work with an unknown virtual host.
+ </doc>
+ </constant>
+
+ <constant name = "access-refused" value = "403" class = "soft-error">
+ <doc>
+ The client attempted to work with a server entity to which it has no
+ access due to security settings.
+ </doc>
+ </constant>
+
+ <constant name = "not-found" value = "404" class = "soft-error">
+ <doc>The client attempted to work with a server entity that does not exist.</doc>
+ </constant>
+
+ <constant name = "resource-locked" value = "405" class = "soft-error">
+ <doc>
+ The client attempted to work with a server entity to which it has no
+ access because another client is working with it.
+ </doc>
+ </constant>
+
+ <constant name = "precondition-failed" value = "406" class = "soft-error">
+ <doc>
+ The client requested a method that was not allowed because some precondition
+ failed.
+ </doc>
+ </constant>
+
+ <constant name = "frame-error" value = "501" class = "hard-error">
+ <doc>
+ The client sent a malformed frame that the server could not decode. This
+ strongly implies a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "syntax-error" value = "502" class = "hard-error">
+ <doc>
+ The client sent a frame that contained illegal values for one or more
+ fields. This strongly implies a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "command-invalid" value = "503" class = "hard-error">
+ <doc>
+ The client sent an invalid sequence of frames, attempting to perform an
+ operation that was considered invalid by the server. This usually implies
+ a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "channel-error" value = "504" class = "hard-error">
+ <doc>
+ The client attempted to work with a channel that had not been correctly
+ opened. This most likely indicates a fault in the client layer.
+ </doc>
+ </constant>
+
+ <constant name = "resource-error" value = "506" class = "hard-error">
+ <doc>
+ The server could not complete the method because it lacked sufficient
+ resources. This may be due to the client creating too many of some type
+ of entity.
+ </doc>
+ </constant>
+
+ <constant name = "not-allowed" value = "530" class = "hard-error">
+ <doc>
+ The client tried to work with some entity in a manner that is prohibited
+ by the server, due to security settings or by some other criteria.
+ </doc>
+ </constant>
+
+ <constant name = "not-implemented" value = "540" class = "hard-error">
+ <doc>
+ The client tried to use functionality that is not implemented in the
+ server.
+ </doc>
+ </constant>
+
+ <constant name = "internal-error" value = "541" class = "hard-error">
+ <doc>
+ The server could not complete the method because of an internal error.
+ The server may require intervention by an operator in order to resume
+ normal operations.
+ </doc>
+ </constant>
+
+ <!--
+ ======================================================
+ == DOMAIN TYPES
+ ======================================================
+ -->
+
+ <domain name = "access-ticket" type = "short" label = "access ticket granted by server">
+ <doc>
+ An access ticket granted by the server for a certain set of access rights
+ within a specific realm. Access tickets are valid within the channel where
+ they were created, and expire when the channel closes.
+ </doc>
+ <assert check = "ne" value = "0" />
+ </domain>
+
+ <domain name = "class-id" type = "short" />
+
+ <domain name = "consumer-tag" type = "shortstr" label = "consumer tag">
+ <doc>
+ Identifier for the consumer, valid within the current connection.
+ </doc>
+ </domain>
+
+ <domain name = "delivery-tag" type = "longlong" label = "server-assigned delivery tag">
+ <doc>
+ The server-assigned and channel-specific delivery tag
+ </doc>
+ <rule name = "channel-local">
+ <doc>
+ The delivery tag is valid only within the channel from which the message was
+ received. I.e. a client MUST NOT receive a message on one channel and then
+ acknowledge it on another.
+ </doc>
+ </rule>
+ <rule name = "non-zero">
+ <doc>
+ The server MUST NOT use a zero value for delivery tags. Zero is reserved
+ for client use, meaning "all messages so far received".
+ </doc>
+ </rule>
+ </domain>
+
+ <domain name = "exchange-name" type = "shortstr" label = "exchange name">
+ <doc>
+ The exchange name is a client-selected string that identifies the exchange for publish
+ methods. Exchange names may consist of any mixture of digits, letters, and underscores.
+ Exchange names are scoped by the virtual host.
+ </doc>
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "known-hosts" type = "shortstr" label = "list of known hosts">
+ <doc>
+ Specifies the list of equivalent or alternative hosts that the server knows about,
+ which will normally include the current server itself. Clients can cache this
+ information and use it when reconnecting to a server after a failure. This field
+ may be empty.
+ </doc>
+ </domain>
+
+ <domain name = "method-id" type = "short" />
+
+ <domain name = "no-ack" type = "bit" label = "no acknowledgement needed">
+ <doc>
+ If this field is set the server does not expect acknowledgements for
+ messages. That is, when a message is delivered to the client the server
+ automatically and silently acknowledges it on behalf of the client. This
+ functionality increases performance but at the cost of reliability.
+ Messages can get lost if a client dies before it can deliver them to the
+ application.
+ </doc>
+ </domain>
+
+ <domain name = "no-local" type = "bit" label = "do not deliver own messages">
+ <doc>
+ If the no-local field is set the server will not send messages to the connection that
+ published them.
+ </doc>
+ </domain>
+
+ <domain name = "path" type = "shortstr">
+ <doc>
+ Must start with a slash "/" and continue with path names separated by slashes. A path
+ name consists of any combination of at least one of [A-Za-z0-9] plus zero or more of
+ [.-_+!=:].
+ </doc>
+
+ <assert check = "notnull" />
+ <assert check = "syntax" rule = "path" />
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "peer-properties" type = "table">
+ <doc>
+ This string provides a set of peer properties, used for identification, debugging, and
+ general information.
+ </doc>
+ </domain>
+
+ <domain name = "queue-name" type = "shortstr" label = "queue name">
+ <doc>
+ The queue name identifies the queue within the vhost. Queue names may consist of any
+ mixture of digits, letters, and underscores.
+ </doc>
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "redelivered" type = "bit" label = "message is being redelivered">
+ <doc>
+ This indicates that the message has been previously delivered to this or
+ another client.
+ </doc>
+ <rule name = "implementation">
+ <doc>
+ The server SHOULD try to signal redelivered messages when it can. When
+ redelivering a message that was not successfully acknowledged, the server
+ SHOULD deliver it to the original client if possible.
+ </doc>
+ <doc type = "scenario">
+ Create a shared queue and publish a message to the queue. Consume the
+ message using explicit acknowledgements, but do not acknowledge the
+ message. Close the connection, reconnect, and consume from the queue
+ again. The message should arrive with the redelivered flag set.
+ </doc>
+ </rule>
+ <rule name = "hinting">
+ <doc>
+ The client MUST NOT rely on the redelivered field but should take it as a
+ hint that the message may already have been processed. A fully robust
+ client must be able to track duplicate received messages on non-transacted,
+ and locally-transacted channels.
+ </doc>
+ </rule>
+ </domain>
+
+ <domain name = "reply-code" type = "short" label = "reply code from server">
+ <doc>
+ The reply code. The AMQ reply codes are defined as constants at the start
+ of this formal specification.
+ </doc>
+ <assert check = "notnull" />
+ </domain>
+
+ <domain name = "reply-text" type = "shortstr" label = "localised reply text">
+ <doc>
+ The localised reply text. This text can be logged as an aid to resolving
+ issues.
+ </doc>
+ <assert check = "notnull" />
+ </domain>
+
+ <domain name = "channel-id" type = "longstr" label = "unique identifier for a channel" />
+
+ <!-- Domains for the message class -->
+ <domain name = "duration" type = "longlong" label = "duration in milliseconds" />
+ <domain name = "offset" type = "longlong" label = "offset into a message body" />
+ <domain name = "reference" type = "longstr" label = "pointer to a message body" />
+ <domain name = "destination" type = "shortstr" label = "destination for a message">
+ <doc>
+ Specifies the destination to which the message is to be
+ transferred. The destination can be empty, meaning the
+ default exchange or consumer.
+ </doc>
+ </domain>
+ <domain name = "reject-code" type = "short" label = "reject code for transfer">
+ <rule name = "01">
+ <doc>
+ The reject code must be one of 0 (generic) or 1 (immediate
+ delivery was attempted but failed).
+ </doc>
+ </rule>
+ </domain>
+ <domain name = "reject-text" type = "shortstr" label = "informational text for message reject"/>
+ <domain name = "security-token" type = "longstr" label = "security token">
+ <doc>
+ Used for authentication, replay prevention, and encrypted bodies.
+ </doc>
+ </domain>
+
+ <!-- Elementary domains -->
+ <domain name = "bit" type = "bit" label = "single bit" />
+ <domain name = "octet" type = "octet" label = "single octet" />
+ <domain name = "short" type = "short" label = "16-bit integer" />
+ <domain name = "long" type = "long" label = "32-bit integer" />
+ <domain name = "longlong" type = "longlong" label = "64-bit integer" />
+ <domain name = "shortstr" type = "shortstr" label = "short string" />
+ <domain name = "longstr" type = "longstr" label = "long string" />
+ <domain name = "timestamp" type = "timestamp" label = "64-bit timestamp" />
+ <domain name = "table" type = "table" label = "field table" />
+
+ <!-- == CONNECTION ======================================================= -->
+
+ <!-- TODO 0.81 - the 'handler' attribute of methods needs to be reviewed, and if
+ no current implementations use it, removed. /PH 2006/07/20
+ -->
+
+ <class name = "connection" handler = "connection" index = "10" label = "work with socket connections">
+ <doc>
+ The connection class provides methods for a client to establish a network connection to
+ a server, and for both peers to operate the connection thereafter.
+ </doc>
+
+ <doc type = "grammar">
+ connection = open-connection *use-connection close-connection
+ open-connection = C:protocol-header
+ S:START C:START-OK
+ *challenge
+ S:TUNE C:TUNE-OK
+ C:OPEN S:OPEN-OK | S:REDIRECT
+ challenge = S:SECURE C:SECURE-OK
+ use-connection = *channel
+ close-connection = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "start" synchronous = "1" index = "10" label = "start connection negotiation">
+ <doc>
+ This method starts the connection negotiation process by telling the client the
+ protocol version that the server proposes, along with a list of security mechanisms
+ which the client can use for authentication.
+ </doc>
+
+ <rule name = "protocol-name">
+ <doc>
+ If the server cannot support the protocol specified in the protocol header,
+ it MUST close the socket connection without sending any response method.
+ </doc>
+ <doc type = "scenario">
+ The client sends a protocol header containing an invalid protocol name.
+ The server must respond by closing the connection.
+ </doc>
+ </rule>
+ <rule name = "server-support">
+ <doc>
+ The server MUST provide a protocol version that is lower than or equal to
+ that requested by the client in the protocol header.
+ </doc>
+ <doc type = "scenario">
+ The client requests a protocol version that is higher than any valid
+ implementation, e.g. 9.0. The server must respond with a current
+ protocol version, e.g. 1.0.
+ </doc>
+ </rule>
+ <rule name = "client-support">
+ <doc>
+ If the client cannot handle the protocol version suggested by the server
+ it MUST close the socket connection.
+ </doc>
+ <doc type = "scenario">
+ The server sends a protocol version that is lower than any valid
+ implementation, e.g. 0.1. The client must respond by closing the
+ connection.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <response name = "start-ok" />
+
+ <field name = "version-major" domain = "octet" label = "protocol major version">
+ <doc>
+ The protocol version, major component, as transmitted in the AMQP protocol
+ header. This, combined with the protocol minor component fully describe the
+ protocol version, which is written in the format major-minor. Hence, with
+ major=1, minor=3, the protocol version would be "1-3".
+ </doc>
+ </field>
+
+ <field name = "version-minor" domain = "octet" label = "protocol minor version">
+ <doc>
+ The protocol version, minor component, as transmitted in the AMQP protocol
+ header. This, combined with the protocol major component fully describe the
+ protocol version, which is written in the format major-minor. Hence, with
+ major=1, minor=3, the protocol version would be "1-3".
+ </doc>
+ </field>
+
+ <field name = "server-properties" domain = "peer-properties" label = "server properties">
+ <rule name = "required-fields">
+ <doc>
+ The properties SHOULD contain at least these fields: "host", specifying the
+ server host name or address, "product", giving the name of the server product,
+ "version", giving the name of the server version, "platform", giving the name
+ of the operating system, "copyright", if appropriate, and "information", giving
+ other general information.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and inspects the server properties. It checks for
+ the presence of the required fields.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "mechanisms" domain = "longstr" label = "available security mechanisms">
+ <doc>
+ A list of the security mechanisms that the server supports, delimited by spaces.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "locales" domain = "longstr" label = "available message locales">
+ <doc>
+ A list of the message locales that the server supports, delimited by spaces. The
+ locale defines the language in which the server will send reply texts.
+ </doc>
+ <rule name = "required-support">
+ <doc>
+ The server MUST support at least the en_US locale.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and inspects the locales field. It checks for
+ the presence of the required locale(s).
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <method name = "start-ok" synchronous = "1" index = "11"
+ label = "select security mechanism and locale">
+ <doc>
+ This method selects a SASL security mechanism.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "client-properties" domain = "peer-properties" label = "client properties">
+ <rule name = "required-fields">
+ <!-- This rule is not testable from the client side -->
+ <doc>
+ The properties SHOULD contain at least these fields: "product", giving the name
+ of the client product, "version", giving the name of the client version, "platform",
+ giving the name of the operating system, "copyright", if appropriate, and
+ "information", giving other general information.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "mechanism" domain = "shortstr" label = "selected security mechanism">
+ <doc>
+ A single security mechanisms selected by the client, which must be one of those
+ specified by the server.
+ </doc>
+ <rule name = "security">
+ <doc>
+ The client SHOULD authenticate using the highest-level security profile it
+ can handle from the list provided by the server.
+ </doc>
+ </rule>
+ <rule name = "validity">
+ <doc>
+ If the mechanism field does not contain one of the security mechanisms
+ proposed by the server in the Start method, the server MUST close the
+ connection without sending any further data.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and sends an invalid security mechanism. The
+ server must respond by closing the connection (a socket close, with no
+ connection close negotiation).
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "response" domain = "longstr" label = "security response data">
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents of this
+ data are defined by the SASL security mechanism.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "locale" domain = "shortstr" label = "selected message locale">
+ <doc>
+ A single message locale selected by the client, which must be one of those
+ specified by the server.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "secure" synchronous = "1" index = "20" label = "security mechanism challenge">
+ <doc>
+ The SASL protocol works by exchanging challenges and responses until both peers have
+ received sufficient information to authenticate each other. This method challenges
+ the client to provide more information.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ <response name = "secure-ok" />
+
+ <field name = "challenge" domain = "longstr" label = "security challenge data">
+ <doc>
+ Challenge information, a block of opaque binary data passed to the security
+ mechanism.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "secure-ok" synchronous = "1" index = "21" label = "security mechanism response">
+ <doc>
+ This method attempts to authenticate, passing a block of SASL data for the security
+ mechanism at the server side.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "response" domain = "longstr" label = "security response data">
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents of this
+ data are defined by the SASL security mechanism.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "tune" synchronous = "1" index = "30"
+ label = "propose connection tuning parameters">
+ <doc>
+ This method proposes a set of connection configuration values to the client. The
+ client can accept and/or adjust these.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <response name = "tune-ok" />
+
+ <field name = "channel-max" domain = "short" label = "proposed maximum channels">
+ <doc>
+ The maximum total number of channels that the server allows per connection. Zero
+ means that the server does not impose a fixed limit, but the number of allowed
+ channels may be limited by available server resources.
+ </doc>
+ </field>
+
+ <field name = "frame-max" domain = "long" label = "proposed maximum frame size">
+ <doc>
+ The largest frame size that the server proposes for the connection. The client
+ can negotiate a lower value. Zero means that the server does not impose any
+ specific limit but may reject very large frames if it cannot allocate resources
+ for them.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ Until the frame-max has been negotiated, both peers MUST accept frames of up
+ to frame-min-size octets large, and the minimum negotiated value for frame-max
+ is also frame-min-size.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and sends a large properties field, creating a frame
+ of frame-min-size octets. The server must accept this frame.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "heartbeat" domain = "short" label = "desired heartbeat delay">
+ <!-- TODO 0.82 - the heartbeat negotiation mechanism was changed during
+ implementation because the model documented here does not actually
+ work properly. The best model we found is that the server proposes
+ a heartbeat value to the client; the client can reply with zero, meaning
+ 'do not use heartbeats (as documented here), or can propose its own
+ heartbeat value, which the server should then accept. This is different
+ from the model here which is disconnected - e.g. each side requests a
+ heartbeat independently. Basically a connection is heartbeated in
+ both ways, or not at all, depending on whether both peers support
+ heartbeating or not, and the heartbeat value should itself be chosen
+ by the client so that remote links can get a higher value. Also, the
+ actual heartbeat mechanism needs documentation, and is as follows: so
+ long as there is activity on a connection - in or out - both peers
+ assume the connection is active. When there is no activity, each peer
+ must send heartbeat frames. When no heartbeat frame is received after
+ N cycles (where N is at least 2), the connection can be considered to
+ have died. /PH 2006/07/19
+ -->
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the server wants.
+ Zero means the server does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "tune-ok" synchronous = "1" index = "31"
+ label = "negotiate connection tuning parameters">
+ <doc>
+ This method sends the client's connection tuning parameters to the server.
+ Certain fields are negotiated, others provide capability information.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "channel-max" domain = "short" label = "negotiated maximum channels">
+ <doc>
+ The maximum total number of channels that the client will use per connection.
+ </doc>
+ <rule name = "upper-limit">
+ <doc>
+ If the client specifies a channel max that is higher than the value provided
+ by the server, the server MUST close the connection without attempting a
+ negotiated close. The server may report the error in some fashion to assist
+ implementors.
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ <assert check = "le" method = "tune" field = "channel-max" />
+ </field>
+
+ <field name = "frame-max" domain = "long" label = "negotiated maximum frame size">
+ <doc>
+ The largest frame size that the client and server will use for the connection.
+ Zero means that the client does not impose any specific limit but may reject
+ very large frames if it cannot allocate resources for them. Note that the
+ frame-max limit applies principally to content frames, where large contents can
+ be broken into frames of arbitrary size.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ Until the frame-max has been negotiated, both peers MUST accept frames of up
+ to frame-min-size octets large, and the minimum negotiated value for frame-max
+ is also frame-min-size.
+ </doc>
+ </rule>
+ <rule name = "upper-limit">
+ <doc>
+ If the client specifies a frame max that is higher than the value provided
+ by the server, the server MUST close the connection without attempting a
+ negotiated close. The server may report the error in some fashion to assist
+ implementors.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "heartbeat" domain = "short" label = "desired heartbeat delay">
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the client wants. Zero
+ means the client does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "40" label = "open connection to virtual host">
+ <doc>
+ This method opens a connection to a virtual host, which is a collection of
+ resources, and acts to separate multiple application domains within a server.
+ The server may apply arbitrary limits per virtual host, such as the number
+ of each type of entity that may be used, per connection and/or in total.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "open-ok" />
+ <response name = "redirect" />
+
+ <field name = "virtual-host" domain = "path" label = "virtual host name">
+ <!-- TODO 0.82 - the entire vhost model needs review. This concept was
+ prompted by the HTTP vhost concept but does not fit very well into
+ AMQP. Currently we use the vhost as a "cluster identifier" which is
+ inaccurate usage. /PH 2006/07/19
+ -->
+ <assert check = "regexp" value = "^[a-zA-Z0-9/-_]+$" />
+ <doc>
+ The name of the virtual host to work with.
+ </doc>
+ <rule name = "separation">
+ <doc>
+ If the server supports multiple virtual hosts, it MUST enforce a full
+ separation of exchanges, queues, and all associated entities per virtual
+ host. An application, connected to a specific virtual host, MUST NOT be able
+ to access resources of another virtual host.
+ </doc>
+ </rule>
+ <rule name = "security">
+ <doc>
+ The server SHOULD verify that the client has permission to access the
+ specified virtual host.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "capabilities" domain = "shortstr" label = "required capabilities">
+ <doc>
+ The client can specify zero or more capability names, delimited by spaces.
+ The server can use this string to how to process the client's connection
+ request.
+ </doc>
+ </field>
+
+ <field name = "insist" domain = "bit" label = "insist on connecting to server">
+ <doc>
+ In a configuration with multiple collaborating servers, the server may respond
+ to a Connection.Open method with a Connection.Redirect. The insist option tells
+ the server that the client is insisting on a connection to the specified server.
+ </doc>
+ <rule name = "behaviour">
+ <doc>
+ When the client uses the insist option, the server MUST NOT respond with a
+ Connection.Redirect method. If it cannot accept the client's connection
+ request it should respond by closing the connection with a suitable reply
+ code.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "41" label = "signal that connection is ready">
+ <doc>
+ This method signals to the client that the connection is ready for use.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "known-hosts" domain = "known-hosts" />
+ </method>
+
+ <method name = "redirect" synchronous = "1" index = "42" label = "redirects client to other server">
+ <doc>
+ This method redirects the client to another server, based on the requested virtual
+ host and/or capabilities.
+ </doc>
+ <rule name = "usage">
+ <doc>
+ When getting the Connection.Redirect method, the client SHOULD reconnect to
+ the host specified, and if that host is not present, to any of the hosts
+ specified in the known-hosts list.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "host" domain = "shortstr" label = "server to connect to">
+ <doc>
+ Specifies the server to connect to. This is an IP address or a DNS name,
+ optionally followed by a colon and a port number. If no port number is
+ specified, the client should use the default port number for the protocol.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ <field name = "known-hosts" domain = "known-hosts" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "close" synchronous = "1" index = "50" label = "request a connection close">
+ <doc>
+ This method indicates that the sender wants to close the connection. This may be
+ due to internal conditions (e.g. a forced shut-down) or due to an error handling
+ a specific method, i.e. an exception. When a close is due to an exception, the
+ sender provides the class and method id of the method which caused the exception.
+ </doc>
+ <!-- TODO: the connection close mechanism needs to be reviewed from the ODF
+ documentation and better expressed as rules here. /PH 2006/07/20
+ -->
+ <rule name = "stability">
+ <doc>
+ After sending this method any received method except the Close-OK method MUST
+ be discarded.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ <response name = "close-ok" />
+
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "class-id" domain = "class-id" label = "failing method class">
+ <doc>
+ When the close is provoked by a method exception, this is the class of the
+ method.
+ </doc>
+ </field>
+
+ <field name = "method-id" domain = "method-id" label = "failing method ID">
+ <doc>
+ When the close is provoked by a method exception, this is the ID of the method.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "close-ok" synchronous = "1" index = "51" label = "confirm a connection close">
+ <doc>
+ This method confirms a Connection.Close method and tells the recipient that it is
+ safe to release resources for the connection and close the socket.
+ </doc>
+ <rule name = "reporting">
+ <doc>
+ A peer that detects a socket closure without having received a Close-Ok
+ handshake method SHOULD log the error.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == CHANNEL ========================================================== -->
+
+ <class name = "channel" handler = "channel" index = "20" label = "work with channels">
+ <doc>
+ The channel class provides methods for a client to establish a channel to a
+ server and for both peers to operate the channel thereafter.
+ </doc>
+
+ <doc type = "grammar">
+ channel = open-channel *use-channel close-channel
+ open-channel = C:OPEN S:OPEN-OK
+ / C:RESUME S:OK
+ use-channel = C:FLOW S:FLOW-OK
+ / S:FLOW C:FLOW-OK
+ / S:PING C:OK
+ / C:PONG S:OK
+ / C:PING S:OK
+ / S:PONG C:OK
+ / functional-class
+ close-channel = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "10" label = "open a channel for use">
+ <doc>
+ This method opens a channel to the server.
+ </doc>
+ <rule name = "state" on-failure = "channel-error">
+ <doc>
+ The client MUST NOT use this method on an already-opened channel.
+ </doc>
+ <doc type = "scenario">
+ Client opens a channel and then reopens the same channel.
+ </doc>
+ </rule>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "open-ok" />
+ <field name = "out-of-band" domain = "shortstr" label = "out-of-band settings">
+ <doc>
+ Configures out-of-band transfers on this channel. The syntax and meaning of this
+ field will be formally defined at a later date.
+ </doc>
+ <assert check = "null" />
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "11" label = "signal that the channel is ready">
+ <doc>
+ This method signals to the client that the channel is ready for use.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "channel-id" domain = "channel-id" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "flow" synchronous = "1" index = "20" label = "enable/disable flow from peer">
+ <doc>
+ This method asks the peer to pause or restart the flow of content data. This is a
+ simple flow-control mechanism that a peer can use to avoid overflowing its queues or
+ otherwise finding itself receiving more messages than it can process. Note that this
+ method is not intended for window control. The peer that receives a disable flow
+ method should finish sending the current content frame, if any, then pause.
+ </doc>
+
+ <rule name = "initial-state">
+ <doc>
+ When a new channel is opened, it is active (flow is active). Some applications
+ assume that channels are inactive until started. To emulate this behaviour a
+ client MAY open the channel, then pause it.
+ </doc>
+ </rule>
+
+ <rule name = "bidirectional">
+ <doc>
+ When sending content frames, a peer SHOULD monitor the channel for incoming
+ methods and respond to a Channel.Flow as rapidly as possible.
+ </doc>
+ </rule>
+
+ <rule name = "throttling">
+ <doc>
+ A peer MAY use the Channel.Flow method to throttle incoming content data for
+ internal reasons, for example, when exchanging data over a slower connection.
+ </doc>
+ </rule>
+
+ <rule name = "expected-behaviour">
+ <doc>
+ The peer that requests a Channel.Flow method MAY disconnect and/or ban a peer
+ that does not respect the request. This is to prevent badly-behaved clients
+ from overwhelming a broker.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <response name = "flow-ok" />
+
+ <field name = "active" domain = "bit" label = "start/stop content frames">
+ <doc>
+ If 1, the peer starts sending content frames. If 0, the peer stops sending
+ content frames.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "flow-ok" index = "21" label = "confirm a flow method">
+ <doc>
+ Confirms to the peer that a flow command was received and processed.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+ <field name = "active" domain = "bit" label = "current flow setting">
+ <doc>
+ Confirms the setting of the processed flow method: 1 means the peer will start
+ sending or continue to send content frames; 0 means it will not.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "close" synchronous = "1" index = "40" label = "request a channel close">
+ <doc>
+ This method indicates that the sender wants to close the channel. This may be due to
+ internal conditions (e.g. a forced shut-down) or due to an error handling a specific
+ method, i.e. an exception. When a close is due to an exception, the sender provides
+ the class and method id of the method which caused the exception.
+ </doc>
+
+ <!-- TODO: the channel close behaviour needs to be reviewed from the ODF
+ documentation and better expressed as rules here. /PH 2006/07/20
+ -->
+ <rule name = "stability">
+ <doc>
+ After sending this method any received method except the Close-OK method MUST
+ be discarded.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ <response name = "close-ok" />
+
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "class-id" domain = "class-id" label = "failing method class">
+ <doc>
+ When the close is provoked by a method exception, this is the class of the
+ method.
+ </doc>
+ </field>
+
+ <field name = "method-id" domain = "method-id" label = "failing method ID">
+ <doc>
+ When the close is provoked by a method exception, this is the ID of the method.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "close-ok" synchronous = "1" index = "41" label = "confirm a channel close">
+ <doc>
+ This method confirms a Channel.Close method and tells the recipient that it is safe
+ to release resources for the channel.
+ </doc>
+ <rule name = "reporting">
+ <doc>
+ A peer that detects a socket closure without having received a Channel.Close-Ok
+ handshake method SHOULD log the error.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ </method>
+
+ </class>
+
+ <!-- == ACCESS =========================================================== -->
+
+ <!-- TODO 0.82 - this class must be implemented by two teams before we can
+ consider it matured.
+ -->
+
+ <class name = "access" handler = "connection" index = "30" label = "work with access tickets">
+ <doc>
+ The protocol control access to server resources using access tickets. A
+ client must explicitly request access tickets before doing work. An access
+ ticket grants a client the right to use a specific set of resources -
+ called a "realm" - in specific ways.
+ </doc>
+
+ <doc type = "grammar">
+ access = C:REQUEST S:REQUEST-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "request" synchronous = "1" index = "10" label = "request an access ticket">
+ <doc>
+ This method requests an access ticket for an access realm. The server
+ responds by granting the access ticket. If the client does not have
+ access rights to the requested realm this causes a connection exception.
+ Access tickets are a per-channel resource.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "request-ok" />
+
+ <field name = "realm" domain = "shortstr" label = "name of requested realm">
+ <doc>
+ Specifies the name of the realm to which the client is requesting access.
+ The realm is a configured server-side object that collects a set of
+ resources (exchanges, queues, etc.). If the channel has already requested
+ an access ticket onto this realm, the previous ticket is destroyed and a
+ new ticket is created with the requested access rights, if allowed.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST specify a realm that is known to the server. The server
+ makes an identical response for undefined realms as it does for realms
+ that are defined but inaccessible to this client.
+ </doc>
+ <doc type = "scenario">
+ Client specifies an undefined realm.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive access to the realm, meaning that this will be the only
+ channel that uses the realm's resources.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MAY NOT request exclusive access to a realm that has active
+ access tickets, unless the same channel already had the only access
+ ticket onto that realm.
+ </doc>
+ <doc type = "scenario">
+ Client opens two channels and requests exclusive access to the same realm.
+ </doc>
+ </rule>
+ </field>
+ <field name = "passive" domain = "bit" label = "request passive access">
+ <doc>
+ Request message passive access to the specified access realm. Passive
+ access lets a client get information about resources in the realm but
+ not to make any changes to them.
+ </doc>
+ </field>
+ <field name = "active" domain = "bit" label = "request active access">
+ <doc>
+ Request message active access to the specified access realm. Active access lets
+ a client get create and delete resources in the realm.
+ </doc>
+ </field>
+ <field name = "write" domain = "bit" label = "request write access">
+ <doc>
+ Request write access to the specified access realm. Write access lets a client
+ publish messages to all exchanges in the realm.
+ </doc>
+ </field>
+ <field name = "read" domain = "bit" label = "request read access">
+ <doc>
+ Request read access to the specified access realm. Read access lets a client
+ consume messages from queues in the realm.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "request-ok" synchronous = "1" index = "11" label = "grant access to server resources">
+ <doc>
+ This method provides the client with an access ticket. The access ticket is valid
+ within the current channel and for the lifespan of the channel.
+ </doc>
+ <rule name = "per-channel" on-failure = "not-allowed">
+ <doc>
+ The client MUST NOT use access tickets except within the same channel as
+ originally granted.
+ </doc>
+ <doc type = "scenario">
+ Client opens two channels, requests a ticket on one channel, and then
+ tries to use that ticket in a second channel.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "ticket" domain = "access-ticket" />
+ </method>
+ </class>
+
+ <!-- == EXCHANGE ========================================================= -->
+
+ <class name = "exchange" handler = "channel" index = "40" label = "work with exchanges">
+ <doc>
+ Exchanges match and distribute messages across queues. Exchanges can be configured in
+ the server or created at runtime.
+ </doc>
+
+ <doc type = "grammar">
+ exchange = C:DECLARE S:DECLARE-OK
+ / C:DELETE S:DELETE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <rule name = "required-types">
+ <doc>
+ The server MUST implement these standard exchange types: fanout, direct.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to declare an exchange with each of these standard types.
+ </doc>
+ </rule>
+ <rule name = "recommended-types">
+ <doc>
+ The server SHOULD implement these standard exchange types: topic, headers.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to declare an exchange with each of these standard types.
+ </doc>
+ </rule>
+ <rule name = "required-instances">
+ <doc>
+ The server MUST, in each virtual host, pre-declare an exchange instance
+ for each standard exchange type that it implements, where the name of the
+ exchange instance, if defined, is "amq." followed by the exchange type name.
+ </doc>
+ <doc>
+ The server MUST, in each virtual host, pre-declare at least two direct
+ exchange instances: one named "amq.direct", the other with no public name
+ that serves as a default exchange for Publish methods.
+ </doc>
+ <doc type = "scenario">
+ Client creates a temporary queue and attempts to bind to each required
+ exchange instance ("amq.fanout", "amq.direct", "amq.topic", and "amq.headers"
+ if those types are defined).
+ </doc>
+ </rule>
+ <rule name = "default-exchange">
+ <doc>
+ The server MUST pre-declare a direct exchange with no public name to act as
+ the default exchange for content Publish methods and for default queue bindings.
+ </doc>
+ <doc type = "scenario">
+ Client checks that the default exchange is active by specifying a queue
+ binding with no exchange name, and publishing a message with a suitable
+ routing key but without specifying the exchange name, then ensuring that
+ the message arrives in the queue correctly.
+ </doc>
+ </rule>
+ <rule name = "default-access">
+ <doc>
+ The server MUST NOT allow clients to access the default exchange except
+ by specifying an empty exchange name in the Queue.Bind and content Publish
+ methods.
+ </doc>
+ </rule>
+ <rule name = "extensions">
+ <doc>
+ The server MAY implement other exchange types as wanted.
+ </doc>
+ </rule>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "declare" synchronous = "1" index = "10" label = "verify exchange exists, create if needed">
+ <doc>
+ This method creates an exchange if it does not already exist, and if the exchange
+ exists, verifies that it is of the correct and expected class.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ The server SHOULD support a minimum of 16 exchanges per virtual host and
+ ideally, impose no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ The client creates as many exchanges as it can until the server reports
+ an error; the number of exchanges successfully created must be at least
+ sixteen.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "declare-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ When a client defines a new exchange, this belongs to the access realm of the
+ ticket used. All further work done with that exchange must be done with an
+ access ticket for the same realm.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access to
+ the realm in which the exchange exists or will be created, or "passive"
+ access if the if-exists flag is set.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <rule name = "reserved" on-failure = "access-refused">
+ <doc>
+ Exchange names starting with "amq." are reserved for pre-declared and
+ standardised exchanges. The client MUST NOT attempt to create an exchange
+ starting with "amq.".
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]+$" />
+ </field>
+
+ <field name = "type" domain = "shortstr" label = "exchange type">
+ <doc>
+ Each exchange belongs to one of a set of exchange types implemented by the
+ server. The exchange types define the functionality of the exchange - i.e. how
+ messages are routed through it. It is not valid or meaningful to attempt to
+ change the type of an existing exchange.
+ </doc>
+ <rule name = "typed" on-failure = "not-allowed">
+ <doc>
+ Exchanges cannot be redeclared with different types. The client MUST not
+ attempt to redeclare an existing exchange with a different type than used
+ in the original Exchange.Declare method.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <rule name = "support" on-failure = "command-invalid">
+ <doc>
+ The client MUST NOT attempt to create an exchange with a type that the
+ server does not support.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]+$" />
+ </field>
+
+ <field name = "passive" domain = "bit" label = "do not create exchange">
+ <doc>
+ If set, the server will not create the exchange. The client can use this to
+ check whether an exchange exists without modifying the server state.
+ </doc>
+ <rule name = "not-found">
+ <doc>
+ If set, and the exchange does not already exist, the server MUST raise a
+ channel exception with reply code 404 (not found).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "durable" domain = "bit" label = "request a durable exchange">
+ <doc>
+ If set when creating a new exchange, the exchange will be marked as durable.
+ Durable exchanges remain active when a server restarts. Non-durable exchanges
+ (transient exchanges) are purged if/when a server restarts.
+ </doc>
+ <rule name = "support">
+ <doc>
+ The server MUST support both durable and transient exchanges.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <rule name = "sticky">
+ <doc>
+ The server MUST ignore the durable field if the exchange already exists.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <!-- TODO 0.82 - clarify how this works; there is no way to cancel a binding
+ except by deleting a queue.
+ -->
+ <field name = "auto-delete" domain = "bit" label = "auto-delete when unused">
+ <doc>
+ If set, the exchange is deleted when all queues have finished using it.
+ </doc>
+ <rule name = "sticky">
+ <doc>
+ The server MUST ignore the auto-delete field if the exchange already
+ exists.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "internal" domain = "bit" label = "create internal exchange">
+ <doc>
+ If set, the exchange may not be used directly by publishers, but only when bound
+ to other exchanges. Internal exchanges are used to construct wiring that is not
+ visible to applications.
+ </doc>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for declaration">
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics of these
+ arguments depends on the server implementation. This field is ignored if passive
+ is 1.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "declare-ok" synchronous = "1" index = "11" label = "confirm exchange declaration">
+ <doc>
+ This method confirms a Declare method and confirms the name of the exchange,
+ essential for automatically-named exchanges.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "delete" synchronous = "1" index = "20" label = "delete an exchange">
+ <doc>
+ This method deletes an exchange. When an exchange is deleted all queue bindings on
+ the exchange are cancelled.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "delete-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access
+ rights to the exchange's access realm.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <rule name = "exists" on-failure = "not-found">
+ <doc>
+ The client MUST NOT attempt to delete an exchange that does not exist.
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+
+ <!-- TODO 0.82 - discuss whether this option is useful or not. I don't have
+ any real use case for it. /PH 2006-07-23.
+ -->
+ <field name = "if-unused" domain = "bit" label = "delete only if unused">
+ <doc>
+ If set, the server will only delete the exchange if it has no queue bindings. If
+ the exchange has queue bindings the server does not delete it but raises a
+ channel exception instead.
+ </doc>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "delete-ok" synchronous = "1" index = "21"
+ label = "confirm deletion of an exchange">
+ <doc>This method confirms the deletion of an exchange.</doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- RG : Added Exchange.bound and Exchange.bound-ok -->
+ <method name="bound" synchronous="1" index="22">
+ <chassis name="server" implement="SHOULD"/>
+ <field name="exchange" domain="exchange-name"/>
+ <field name = "routing-key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key for the message. The routing key is
+ used for routing messages depending on the exchange configuration.
+ </doc>
+ </field>
+ <field name = "queue" domain = "queue-name"/>
+ </method>
+
+ <method name="bound-ok" synchronous="1" index="23">
+ <field name="reply-code" domain="reply-code"/>
+ <field name="reply-text" domain="reply-text"/>
+ <chassis name="client" implement="SHOULD"/>
+ </method>
+
+
+
+ </class>
+
+ <!-- == QUEUE ============================================================ -->
+
+ <class name = "queue" handler = "channel" index = "50" label = "work with queues">
+ <doc>
+ Queues store and forward messages. Queues can be configured in the server or created at
+ runtime. Queues must be attached to at least one exchange in order to receive messages
+ from publishers.
+ </doc>
+
+ <doc type = "grammar">
+ queue = C:DECLARE S:DECLARE-OK
+ / C:BIND S:BIND-OK
+ / C:PURGE S:PURGE-OK
+ / C:DELETE S:DELETE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <rule name = "any-content">
+ <doc>
+ A server MUST allow any content class to be sent to any queue, in any mix, and
+ queue and deliver these content classes independently. Note that all methods
+ that fetch content off queues are specific to a given content class.
+ </doc>
+ <doc type = "scenario">
+ Client creates an exchange of each standard type and several queues that
+ it binds to each exchange. It must then successfully send each of the standard
+ content types to each of the available queues.
+ </doc>
+ </rule>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "declare" synchronous = "1" index = "10" label = "declare queue, create if needed">
+ <doc>
+ This method creates or checks a queue. When creating a new queue the client can
+ specify various properties that control the durability of the queue and its
+ contents, and the level of sharing for the queue.
+ </doc>
+
+ <rule name = "default-binding">
+ <doc>
+ The server MUST create a default binding for a newly-created queue to the
+ default exchange, which is an exchange of type 'direct' and use the queue
+ name as the routing key.
+ </doc>
+ <doc type = "scenario">
+ Client creates a new queue, and then without explicitly binding it to an
+ exchange, attempts to send a message through the default exchange binding,
+ i.e. publish a message to the empty exchange, with the queue name as routing
+ key.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_35" -->
+ <rule name = "minimum-queues">
+ <doc>
+ The server SHOULD support a minimum of 256 queues per virtual host and ideally,
+ impose no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to create as many queues as it can until the server reports
+ an error. The resulting count must at least be 256.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "declare-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ When a client defines a new queue, this belongs to the access realm of the
+ ticket used. All further work done with that queue must be done with an access
+ ticket for the same realm.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access to
+ the realm in which the queue exists or will be created.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <rule name = "default-name">
+ <doc>
+ The queue name MAY be empty, in which case the server MUST create a new
+ queue with a unique generated name and return this to the client in the
+ Declare-Ok method.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to create several queues with an empty name. The client then
+ verifies that the server-assigned names are unique and different.
+ </doc>
+ </rule>
+ <rule name = "reserved-prefix" on-failure = "not-allowed">
+ <doc>
+ Queue names starting with "amq." are reserved for pre-declared and
+ standardised server queues. A client MAY NOT attempt to declare a queue with a
+ name that starts with "amq." and the passive option set to zero.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to create a queue with a name starting with "amq." and with
+ the passive option set to zero.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]*$" />
+ </field>
+
+ <field name = "passive" domain = "bit" label = "do not create queue">
+ <doc>
+ If set, the server will not create the queue. This field allows the client
+ to assert the presence of a queue without modifying the server state.
+ </doc>
+ <rule name = "passive" on-failure = "not-found">
+ <doc>
+ The client MAY ask the server to assert that a queue exists without
+ creating the queue if not. If the queue does not exist, the server
+ treats this as a failure.
+ </doc>
+ <doc type = "scenario">
+ Client declares an existing queue with the passive option and expects
+ the server to respond with a declare-ok. Client then attempts to declare
+ a non-existent queue with the passive option, and the server must close
+ the channel with the correct reply-code.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "durable" domain = "bit" label = "request a durable queue">
+ <doc>
+ If set when creating a new queue, the queue will be marked as durable. Durable
+ queues remain active when a server restarts. Non-durable queues (transient
+ queues) are purged if/when a server restarts. Note that durable queues do not
+ necessarily hold persistent messages, although it does not make sense to send
+ persistent messages to a transient queue.
+ </doc>
+ <!-- Rule test name: was "amq_queue_03" -->
+ <rule name = "persistence">
+ <doc>The server MUST recreate the durable queue after a restart.</doc>
+
+ <!-- TODO: use 'client does something' rather than 'a client does something'. -->
+ <doc type = "scenario">
+ A client creates a durable queue. The server is then restarted. The client
+ then attempts to send a message to the queue. The message should be successfully
+ delivered.
+ </doc>
+ </rule>
+ <!-- Rule test name: was "amq_queue_36" -->
+ <rule name = "types">
+ <doc>The server MUST support both durable and transient queues.</doc>
+ <doc type = "scenario">
+ A client creates two named queues, one durable and one transient.
+ </doc>
+ </rule>
+ <!-- Rule test name: was "amq_queue_37" -->
+ <rule name = "pre-existence">
+ <doc>The server MUST ignore the durable field if the queue already exists.</doc>
+ <doc type = "scenario">
+ A client creates two named queues, one durable and one transient. The client
+ then attempts to declare the two queues using the same names again, but reversing
+ the value of the durable flag in each case. Verify that the queues still exist
+ with the original durable flag values.
+ <!-- TODO: but how? -->
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exclusive" domain = "bit" label = "request an exclusive queue">
+ <doc>
+ Exclusive queues may only be consumed from by the current connection. Setting
+ the 'exclusive' flag always implies 'auto-delete'.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_38" -->
+ <rule name = "types">
+ <doc>
+ The server MUST support both exclusive (private) and non-exclusive (shared)
+ queues.
+ </doc>
+ <doc type = "scenario">
+ A client creates two named queues, one exclusive and one non-exclusive.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_04" -->
+ <rule name = "02" on-failure = "channel-error">
+ <doc>
+ The client MAY NOT attempt to declare any existing and exclusive queue
+ on multiple connections.
+ </doc>
+ <doc type = "scenario">
+ A client declares an exclusive named queue. A second client on a different
+ connection attempts to declare a queue of the same name.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "auto-delete" domain = "bit" label = "auto-delete queue when unused">
+ <doc>
+ If set, the queue is deleted when all consumers have finished using it. Last
+ consumer can be cancelled either explicitly or because its channel is closed. If
+ there was no consumer ever on the queue, it won't be deleted.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_31" -->
+ <rule name = "pre-existence">
+ <doc>
+ The server MUST ignore the auto-delete field if the queue already exists.
+ </doc>
+ <doc type = "scenario">
+ A client creates two named queues, one as auto-delete and one explicit-delete.
+ The client then attempts to declare the two queues using the same names again,
+ but reversing the value of the auto-delete field in each case. Verify that the
+ queues still exist with the original auto-delete flag values.
+ <!-- TODO: but how? -->
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for declaration">
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics of these
+ arguments depends on the server implementation. This field is ignored if passive
+ is 1.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "declare-ok" synchronous = "1" index = "11" label = "confirms a queue definition">
+ <doc>
+ This method confirms a Declare method and confirms the name of the queue, essential
+ for automatically-named queues.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Reports the name of the queue. If the server generated a queue name, this field
+ contains that name.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "message-count" domain = "long" label = "number of messages in queue">
+ <doc>
+ Reports the number of messages in the queue, which will be zero for
+ newly-created queues.
+ </doc>
+ </field>
+
+ <field name = "consumer-count" domain = "long" label = "number of consumers">
+ <doc>
+ Reports the number of active consumers for the queue. Note that consumers can
+ suspend activity (Channel.Flow) in which case they do not appear in this count.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "bind" synchronous = "1" index = "20" label = "bind queue to an exchange">
+ <doc>
+ This method binds a queue to an exchange. Until a queue is bound it will not receive
+ any messages. In a classic messaging model, store-and-forward queues are bound to a
+ direct exchange and subscription queues are bound to a topic exchange.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_25" -->
+ <rule name = "duplicates">
+ <doc>
+ A server MUST allow ignore duplicate bindings - that is, two or more bind
+ methods for a specific queue, with identical arguments - without treating these
+ as an error.
+ </doc>
+ <doc type = "scenario">
+ A client binds a named queue to an exchange. The client then repeats the bind
+ (with identical arguments).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_39" -->
+ <rule name = "failure">
+ <!--
+ TODO: Find correct on-failure code. The on-failure code returned should depend on why the bind
+ failed. Assuming that failures owing to bad parameters are covered in the rules relating
+ to those parameters, the only remaining reason for a failure would be the lack of
+ server resorces or some internal error - such as too many queues open. Would these
+ cases qualify as "resource error" 506 or "internal error" 541?
+ -->
+ <doc>If a bind fails, the server MUST raise a connection exception.</doc>
+ <doc type = "scenario">
+ TODO
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_12" -->
+ <rule name = "transient-exchange" on-failure = "not-allowed">
+ <doc>
+ The server MUST NOT allow a durable queue to bind to a transient exchange.
+ </doc>
+ <doc type = "scenario">
+ A client creates a transient exchange. The client then declares a named durable
+ queue and then attempts to bind the transient exchange to the durable queue.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_13" -->
+ <rule name = "durable-exchange">
+ <doc>
+ Bindings for durable queues are automatically durable and the server SHOULD
+ restore such bindings after a server restart.
+ </doc>
+ <doc type = "scenario">
+ A server creates a named durable queue and binds it to a durable exchange. The
+ server is restarted. The client then attempts to use the queue/exchange combination.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_17" -->
+ <rule name = "internal-exchange">
+ <doc>
+ If the client attempts to bind to an exchange that was declared as internal, the server
+ MUST raise a connection exception with reply code 530 (not allowed).
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind a named queue to an internal exchange.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_40" -->
+ <rule name = "binding-count">
+ <doc>
+ The server SHOULD support at least 4 bindings per queue, and ideally, impose no
+ limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ A client creates a named queue and attempts to bind it to 4 different non-internal
+ exchanges.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "bind-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ The client provides a valid access ticket giving "active" access rights to the
+ queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to bind. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "empty-queue" on-failure = "not-allowed">
+ <doc>
+ A client MUST NOT be allowed to bind a non-existent and unnamed queue (i.e.
+ empty queue name) to an exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind with an unnamed (empty) queue name to an exchange.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_26" -->
+ <rule name = "queue-existence" on-failure = "not-found">
+ <doc>
+ A client MUST NOT be allowed to bind a non-existent queue (i.e. not previously
+ declared) to an exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind an undeclared queue name to an exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name" label = "name of the exchange to bind to">
+ <!-- Rule test name: was "amq_queue_14" -->
+ <rule name = "exchange-existence" on-failure = "not-found">
+ <doc>
+ A client MUST NOT be allowed to bind a queue to a non-existent exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind an named queue to a undeclared exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "message routing key">
+ <doc>
+ Specifies the routing key for the binding. The routing key is used for routing
+ messages depending on the exchange configuration. Not all exchanges use a
+ routing key - refer to the specific exchange documentation. If the queue name
+ is empty, the server uses the last queue declared on the channel. If the
+ routing key is also empty, the server uses this queue name for the routing
+ key as well. If the queue name is provided but the routing key is empty, the
+ server does the binding with that empty routing key. The meaning of empty
+ routing keys depends on the exchange implementation.
+ </doc>
+ <rule name = "direct-exchange-key-matching">
+ <doc>
+ If a message queue binds to a direct exchange using routing key K and a
+ publisher sends the exchange a message with routing key R, then the message
+ MUST be passed to the message queue if K = R.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for binding">
+ <doc>
+ A set of arguments for the binding. The syntax and semantics of these arguments
+ depends on the exchange class.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "bind-ok" synchronous = "1" index = "21" label = "confirm bind successful">
+ <doc>This method confirms that the bind was successful.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "unbind" synchronous = "1" index = "50" label = "unbind a queue from an exchange">
+ <doc>This method unbinds a queue from an exchange.</doc>
+ <rule name = "01">
+ <doc>If a unbind fails, the server MUST raise a connection exception.</doc>
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="unbind-ok"/>
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ The client provides a valid access ticket giving "active"
+ access rights to the queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>Specifies the name of the queue to unbind.</doc>
+ <rule name = "02">
+ <doc>
+ If the queue does not exist the server MUST raise a channel exception
+ with reply code 404 (not found).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>The name of the exchange to unbind from.</doc>
+ <rule name = "03">
+ <doc>
+ If the exchange does not exist the server MUST raise a channel
+ exception with reply code 404 (not found).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "routing key of binding">
+ <doc>Specifies the routing key of the binding to unbind.</doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments of binding">
+ <doc>Specifies the arguments of the binding to unbind.</doc>
+ </field>
+ </method>
+
+ <method name = "unbind-ok" synchronous = "1" index = "51" label = "confirm unbind successful">
+ <doc>This method confirms that the unbind was successful.</doc>
+ <chassis name = "client" implement = "MUST"/>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "purge" synchronous = "1" index = "30" label = "purge a queue">
+ <doc>
+ This method removes all messages from a queue. It does not cancel consumers. Purged
+ messages are deleted without any formal "undo" mechanism.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_15" -->
+ <rule name = "01">
+ <doc>A call to purge MUST result in an empty queue.</doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_41" -->
+ <rule name = "02">
+ <doc>
+ On transacted channels the server MUST not purge messages that have already been
+ sent to a client but not yet acknowledged.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_42" -->
+ <rule name = "03">
+ <doc>
+ The server MAY implement a purge queue or log that allows system administrators
+ to recover accidentally-purged messages. The server SHOULD NOT keep purged
+ messages in the same storage spaces as the live messages since the volumes of
+ purged messages may get very large.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "purge-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>The access ticket must be for the access realm that holds the queue.</doc>
+
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the queue's access realm. Note that purging a queue is equivalent to reading
+ all messages and discarding them.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to purge. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+
+ <!-- TODO Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_16" -->
+ <rule name = "02">
+ <doc>
+ The queue MUST exist. Attempting to purge a non-existing queue MUST cause a
+ channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "purge-ok" synchronous = "1" index = "31" label = "confirms a queue purge">
+ <doc>This method confirms the purge of a queue.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "message-count" domain = "long" label = "number of messages purged">
+ <doc>Reports the number of messages purged.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "delete" synchronous = "1" index = "40" label = "delete a queue">
+ <doc>
+ This method deletes a queue. When a queue is deleted any pending messages are sent
+ to a dead-letter queue if this is defined in the server configuration, and all
+ consumers on the queue are cancelled.
+ </doc>
+
+ <!-- TODO: Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_43" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD use a dead-letter queue to hold messages that were pending on
+ a deleted queue, and MAY provide facilities for a system administrator to move
+ these messages back to an active queue.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "delete-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ The client provides a valid access ticket giving "active" access rights to the
+ queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to delete. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_21" -->
+ <rule name = "02">
+ <doc>
+ The queue must exist. If the client attempts to delete a non-existing queue
+ the server MUST raise a channel exception with reply code 404 (not found).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "if-unused" domain = "bit" label = "delete only if unused">
+ <doc>
+ If set, the server will only delete the queue if it has no consumers. If the
+ queue has consumers the server does does not delete it but raises a channel
+ exception instead.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_29" and "amq_queue_30" -->
+ <rule name = "01">
+ <doc>The server MUST respect the if-unused flag when deleting a queue.</doc>
+ </rule>
+ </field>
+
+ <field name = "if-empty" domain = "bit" label = "delete only if empty">
+ <doc>
+ If set, the server will only delete the queue if it has no messages.
+ </doc>
+ <rule name = "01">
+ <doc>
+ If the queue is not empty the server MUST raise a channel exception with
+ reply code 406 (precondition failed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "delete-ok" synchronous = "1" index = "41" label = "confirm deletion of a queue">
+ <doc>This method confirms the deletion of a queue.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "message-count" domain = "long" label = "number of messages purged">
+ <doc>Reports the number of messages purged.</doc>
+ </field>
+ </method>
+ </class>
+
+ <!-- == BASIC ============================================================ -->
+
+ <class name = "basic" handler = "channel" index = "60" label = "work with basic content">
+ <doc>
+ The Basic class provides methods that support an industry-standard messaging model.
+ </doc>
+
+ <doc type = "grammar">
+ basic = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN content
+ / S:DELIVER content
+ / C:GET ( S:GET-OK content / S:GET-EMPTY )
+ / C:ACK
+ / C:REJECT
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- Rule test name: was "amq_basic_08" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD respect the persistent property of basic messages and
+ SHOULD make a best-effort to hold persistent basic messages on a reliable
+ storage mechanism.
+ </doc>
+ <doc type = "scenario">
+ Send a persistent message to queue, stop server, restart server and then
+ verify whether message is still present. Assumes that queues are durable.
+ Persistence without durable queues makes no sense.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_09" -->
+ <rule name = "02">
+ <doc>
+ The server MUST NOT discard a persistent basic message in case of a queue
+ overflow.
+ </doc>
+ <doc type = "scenario">
+ Create a queue overflow situation with persistent messages and verify that
+ messages do not get lost (presumably the server will write them to disk).
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The server MAY use the Channel.Flow method to slow or stop a basic message
+ publisher when necessary.
+ </doc>
+ <doc type = "scenario">
+ Create a queue overflow situation with non-persistent messages and verify
+ whether the server responds with Channel.Flow or not. Repeat with persistent
+ messages.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_10" -->
+ <rule name = "04">
+ <doc>
+ The server MAY overflow non-persistent basic messages to persistent
+ storage.
+ </doc>
+ <!-- Test scenario: untestable -->
+ </rule>
+
+ <rule name = "05">
+ <doc>
+ The server MAY discard or dead-letter non-persistent basic messages on a
+ priority basis if the queue size exceeds some configured limit.
+ </doc>
+ <!-- Test scenario: untestable -->
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_11" -->
+ <rule name = "06">
+ <doc>
+ The server MUST implement at least 2 priority levels for basic messages,
+ where priorities 0-4 and 5-9 are treated as two distinct levels.
+ </doc>
+ <doc type = "scenario">
+ Send a number of priority 0 messages to a queue. Send one priority 9
+ message. Consume messages from the queue and verify that the first message
+ received was priority 9.
+ </doc>
+ </rule>
+
+ <rule name = "07">
+ <doc>
+ The server MAY implement up to 10 priority levels.
+ </doc>
+ <doc type = "scenario">
+ Send a number of messages with mixed priorities to a queue, so that all
+ priority values from 0 to 9 are exercised. A good scenario would be ten
+ messages in low-to-high priority. Consume from queue and verify how many
+ priority levels emerge.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_12" -->
+ <rule name = "08">
+ <doc>
+ The server MUST deliver messages of the same priority in order irrespective of
+ their individual persistence.
+ </doc>
+ <doc type = "scenario">
+ Send a set of messages with the same priority but different persistence
+ settings to a queue. Consume and verify that messages arrive in same order
+ as originally published.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_13" -->
+ <rule name = "09">
+ <doc>
+ The server MUST support automatic acknowledgements on Basic content, i.e.
+ consumers with the no-ack field set to FALSE.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and a consumer using automatic acknowledgements. Publish
+ a set of messages to the queue. Consume the messages and verify that all
+ messages are received.
+ </doc>
+ </rule>
+
+ <rule name = "10">
+ <doc>
+ The server MUST support explicit acknowledgements on Basic content, i.e.
+ consumers with the no-ack field set to TRUE.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and a consumer using explicit acknowledgements. Publish a
+ set of messages to the queue. Consume the messages but acknowledge only
+ half of them. Disconnect and reconnect, and consume from the queue.
+ Verify that the remaining messages are received.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a Basic content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "delivery-mode" domain = "octet" label = "non-persistent (1) or persistent (2)" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "correlation-id" domain = "shortstr" label = "application correlation identifier" />
+ <field name = "reply-to" domain = "shortstr" label = "destination to reply to" />
+ <field name = "expiration" domain = "shortstr" label = "message expiration specification" />
+ <field name = "message-id" domain = "shortstr" label = "application message identifier" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+ <field name = "type" domain = "shortstr" label = "message type name" />
+ <field name = "user-id" domain = "shortstr" label = "creating user id" />
+ <field name = "app-id" domain = "shortstr" label = "creating application id" />
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "intra-cluster routing identifier" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. The server
+ will send a message in advance if it is equal to or smaller in size than the
+ available prefetch size (and also falls into other prefetch limits). May be set
+ to zero, meaning "no specific limit", although other prefetch limits may still
+ apply. The prefetch-size is ignored if the no-ack option is set.
+ </doc>
+ <!-- Rule test name: was "amq_basic_17" -->
+ <rule name = "01">
+ <doc>
+ The server MUST ignore this setting when the client is not processing any
+ messages - i.e. the prefetch size does not limit the transfer of single
+ messages to a client, only the sending in advance of more messages while
+ the client still has one or more unacknowledged messages.
+ </doc>
+ <doc type = "scenario">
+ Define a QoS prefetch-size limit and send a single message that exceeds
+ that limit. Verify that the message arrives correctly.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This field may be used
+ in combination with the prefetch-size field; a message will only be sent in
+ advance if both prefetch windows (and those at the channel and connection level)
+ allow it. The prefetch-count is ignored if the no-ack option is set.
+ </doc>
+ <!-- Rule test name: was "amq_basic_18" -->
+ <rule name = "01">
+ <doc>
+ The server may send less data in advance than allowed by the client's
+ specified prefetch windows but it MUST NOT send more.
+ </doc>
+ <doc type = "scenario">
+ Define a QoS prefetch-size limit and a prefetch-count limit greater than
+ one. Send multiple messages that exceed the prefetch size. Verify that
+ no more than one message arrives at once.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_01" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, and ideally, impose
+ no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and create consumers on that queue until the server closes the
+ connection. Verify that the number of consumers created was at least sixteen
+ and report the total number.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer with an invalid (non-zero) access ticket.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+ <rule name = "01" on-failure = "not-allowed">
+ <doc>
+ If the queue name is empty the client MUST have previously declared a
+ queue using this channel.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer with an empty queue name and no previously
+ declared queue on the channel.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+ <rule name = "01" on-failure = "not-allowed">
+ <doc>
+ The client MUST NOT specify a tag that refers to an existing consumer.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create two consumers with the same non-empty tag.
+ </doc>
+ </rule>
+ <rule name = "02" on-failure = "not-allowed">
+ <doc>
+ The consumer tag is valid only within the channel from which the
+ consumer was created. I.e. a client MUST NOT create a consumer in one
+ channel and then use it in another.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer in one channel, then use in another channel,
+ in which consumers have also been created (to test that the server uses
+ unique consumer tags).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "no-ack" domain = "no-ack" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+ <!-- Rule test name: was "amq_basic_02" -->
+ <rule name = "01" on-failure = "access-refused">
+ <doc>
+ The client MAY NOT gain exclusive access to a queue that already has
+ active consumers.
+ </doc>
+ <doc type = "scenario">
+ Open two connections to a server, and in one connection create a shared
+ (non-exclusive) queue and then consume from the queue. In the second
+ connection attempt to consume from the same queue using the exclusive
+ option.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise
+ a channel or connection exception.
+ </doc>
+ </field>
+
+ <!-- RG changed name from filter to arguments on basic.consume: this is inline with qpid0-8 and 0-10 and has no effect on the wire level encoding
+ <field name = "arguments" domain = "table" label = "arguments for consuming"> -->
+ <field name = "arguments" domain = "table" label = "arguments for consuming">
+ <doc>
+ A set of filters for the consume. The syntax and semantics
+ of these filters depends on the providers implementation.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ The server provides the client with a consumer tag, which is used by the client
+ for methods called on the consumer at a later stage.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Holds the consumer tag specified by the client or provided by the server.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. This does not affect already delivered
+ messages, but it does mean the server will not send any more messages for
+ that consumer. The client may receive an arbitrary number of messages in
+ between sending the cancel method and receiving the cancel-ok reply.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the queue does not exist the server MUST ignore the cancel method, so
+ long as the consumer tag is valid for that channel.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "cancel-ok" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>
+ This method confirms that the cancellation was completed.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" content = "1" index = "40" label = "publish a message">
+ <doc>
+ This method publishes a message to a specific exchange. The message will be routed
+ to queues as defined by the exchange configuration and distributed to any active
+ consumers when the transaction, if any, is committed.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_06" -->
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_14" -->
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST raise
+ a channel exception with a reply code 403 (access refused).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_15" -->
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse basic content in which case it MUST raise a channel
+ exception with reply code 540 (not implemented).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+ <!-- Rule test name: was "amq_basic_07" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD implement the mandatory flag.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+ <!-- Rule test name: was "amq_basic_16" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD implement the immediate flag.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "50" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key name specified when the message was published.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" content = "1" index = "60"
+ label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a message to the client, via a consumer. In the asynchronous
+ message delivery model, the client starts a consumer using the Consume method, then
+ the server responds with Deliver methods as and when messages arrive for that
+ consumer.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_19" -->
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server SHOULD track the number of times a message has been delivered to
+ clients and when a message is redelivered a certain number of times - e.g. 5
+ times - without being acknowledged, the server SHOULD consider the message to be
+ unprocessable (possibly causing client applications to abort), and move the
+ message to a dead letter queue.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "get" synchronous = "1" index = "70" label = "direct access to a queue">
+ <doc>
+ This method provides a direct access to the messages in a queue using a synchronous
+ dialogue that is designed for specific types of application where synchronous
+ functionality is more important than performance.
+ </doc>
+
+ <response name = "get-ok" />
+ <response name = "get-empty" />
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-ack" domain = "no-ack" />
+ </method>
+
+ <method name = "get-ok" synchronous = "1" content = "1" index = "71"
+ label = "provide client with a message">
+ <doc>
+ This method delivers a message to the client following a get method. A message
+ delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the
+ get method.
+ </doc>
+
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ If empty, the message was published to the default exchange.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+
+ <field name = "message-count" domain = "long" label = "number of messages pending">
+ <doc>
+ This field reports the number of messages pending on the queue, excluding the
+ message being delivered. Note that this figure is indicative, not reliable, and
+ can change arbitrarily as messages are added to the queue and removed by other
+ clients.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "get-empty" synchronous = "1" index = "72"
+ label = "indicate no messages available">
+ <doc>
+ This method tells the client that the queue has no messages available for the
+ client.
+ </doc>
+
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "Cluster id">
+ <doc>
+ For use by cluster applications, should not be used by client applications.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "ack" index = "80" label = "acknowledge one or more messages">
+ <doc>
+ This method acknowledges one or more messages delivered via the Deliver or Get-Ok
+ methods. The client can ask to confirm a single message or a set of messages up to
+ and including a specific message.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "multiple" domain = "bit" label = "acknowledge multiple messages">
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including", so that the
+ client can acknowledge multiple messages with a single method. If set to zero,
+ the delivery tag refers to a single message. If the multiple field is 1, and the
+ delivery tag is zero, tells the server to acknowledge all outstanding messages.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_20" -->
+ <rule name = "01">
+ <doc>
+ The server MUST validate that a non-zero delivery-tag refers to an delivered
+ message, and raise a channel exception if this is not the case.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "reject" index = "90" label = "reject an incoming message">
+ <doc>
+ This method allows a client to reject a message. It can be used to interrupt and
+ cancel large incoming messages, or return untreatable messages to their original
+ queue.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_21" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD be capable of accepting and process the Reject method while
+ sending message content with a Deliver or Get-Ok method. I.e. the server should
+ read and process incoming methods while sending output frames. To cancel a
+ partially-send content, the server sends a content body frame of size 1 (i.e.
+ with no data except the frame-end octet).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_22" -->
+ <rule name = "02">
+ <doc>
+ The server SHOULD interpret this method as meaning that the client is unable to
+ process the message at this time.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <!-- TODO: Rule split? -->
+ <doc>
+ A client MUST NOT use this method as a means of selecting messages to process. A
+ rejected message MAY be discarded or dead-lettered, not necessarily passed to
+ another client.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be discarded. If this bit is 1, the
+ server will attempt to requeue the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_23" -->
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST NOT deliver the message to the same client within the
+ context of the current channel. The recommended strategy is to attempt to
+ deliver the message to an alternative consumer, and if that is not possible,
+ to move the message to a dead-letter queue. The server MAY use more
+ sophisticated tracking to hold the message on the queue and redeliver it to
+ the same client at a later stage.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "recover" index = "100" label = "redeliver unacknowledged messages">
+ <doc>
+ This method asks the broker to redeliver all unacknowledged messages on a specified
+ channel. Zero or more messages may be redelivered. This method is only allowed on
+ non-transacted channels.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST set the redelivered flag on all messages that are resent.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ The server MUST raise a channel exception if this is called on a transacted
+ channel.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be redelivered to the original
+ recipient. If this bit is 1, the server will attempt to requeue the message,
+ potentially then delivering it to an alternative subscriber.
+ </doc>
+ </field>
+ </method>
+
+
+ <!-- RG : Added recover-sync and recover-sync-ok to give a synchronous recover without interfering with the correct 0-9 recover method -->
+ <method name = "recover-sync" index = "102">
+ redeliver unacknowledged messages
+ <doc>
+ This method asks the broker to redeliver all unacknowledged messages on a
+ specified channel. Zero or more messages may be redelivered. This method
+ is only allowed on non-transacted channels.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "requeue" type = "bit">
+ requeue the message
+ <doc>
+ If this field is zero, the message will be redelivered to the original
+ recipient. If this bit is 1, the server will attempt to requeue the
+ message, potentially then delivering it to an alternative subscriber.
+ </doc>
+ </field>
+ <doc name="rule">
+ The server MUST set the redelivered flag on all messages that are resent.
+ </doc>
+ <doc name="rule">
+ The server MUST raise a channel exception if this is called on a
+ transacted channel.
+ </doc>
+ <response name="recover-sync-ok"/>
+ </method>
+ <method name="recover-sync-ok" synchronous="1" index="101">
+ confirm a successful recover
+ <doc>
+ This method confirms to the client that the recover succeeded.
+ Note that if an recover fails, the server raises a channel exception.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+
+
+ </class>
+
+ <!-- == FILE ============================================================= -->
+
+ <class name = "file" handler = "channel" index = "70" label = "work with file content">
+ <doc>
+ The file class provides methods that support reliable file transfer. File
+ messages have a specific set of properties that are required for interoperability
+ with file transfer applications. File messages and acknowledgements are subject to
+ channel transactions. Note that the file class does not provide message browsing
+ methods; these are not compatible with the staging model. Applications that need
+ browsable file transfer should use Basic content and the Basic class.
+ </doc>
+
+ <doc type = "grammar">
+ file = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:OPEN S:OPEN-OK C:STAGE content
+ / S:OPEN C:OPEN-OK S:STAGE content
+ / C:PUBLISH
+ / S:DELIVER
+ / S:RETURN
+ / C:ACK
+ / C:REJECT
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <rule name = "01">
+ <doc>
+ The server MUST make a best-effort to hold file messages on a reliable storage
+ mechanism.
+ </doc>
+ </rule>
+
+ <!-- TODO Rule implement attr inverse? -->
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "02">
+ <doc>
+ The server MUST NOT discard a file message in case of a queue overflow. The server
+ MUST use the Channel.Flow method to slow or stop a file message publisher when
+ necessary.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "03">
+ <doc>
+ The server MUST implement at least 2 priority levels for file messages, where
+ priorities 0-4 and 5-9 are treated as two distinct levels. The server MAY implement
+ up to 10 priority levels.
+ </doc>
+ </rule>
+
+ <rule name = "04">
+ <doc>
+ The server MUST support both automatic and explicit acknowledgements on file
+ content.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a File content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "reply-to" domain = "shortstr" label = "destination to reply to" />
+ <field name = "message-id" domain = "shortstr" label = "application message identifier" />
+ <field name = "filename" domain = "shortstr" label = "message filename" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "intra-cluster routing identifier" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. May be set
+ to zero, meaning "no specific limit". Note that other prefetch limits may still
+ apply. The prefetch-size is ignored if the no-ack option is set.
+ </doc>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This is compatible with
+ some file API implementations. This field may be used in combination with the
+ prefetch-size field; a message will only be sent in advance if both prefetch
+ windows (and those at the channel and connection level) allow it. The
+ prefetch-count is ignored if the no-ack option is set.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MAY send less data in advance than allowed by the client's
+ specified prefetch windows but it MUST NOT send more.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, unless the queue was
+ declared as private, and ideally, impose no limit except as defined by available
+ resources.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The tag MUST NOT refer to an existing consumer. If the client attempts to
+ create two consumers with the same non-empty tag the server MUST raise a
+ connection exception with reply code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "no-ack" domain = "no-ack" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>
+ If the server cannot grant exclusive access to the queue when asked, -
+ because there are other consumers active - it MUST raise a channel exception
+ with return code 405 (resource locked).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "filter" domain = "table" label = "arguments for consuming">
+ <doc>
+ A set of filters for the consume. The syntax and semantics
+ of these filters depends on the providers implementation.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ This method provides the client with a consumer tag which it MUST use in methods
+ that work with the consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>Holds the consumer tag specified by the client or provided by the server.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. This does not affect already delivered messages, but
+ it does mean the server will not send any more messages for that consumer.
+ </doc>
+
+ <response name = "cancel-ok" />
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>This method confirms that the cancellation was completed.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "40" label = "request to start staging">
+ <doc>
+ This method requests permission to start staging a message. Staging means sending
+ the message into a temporary area at the recipient end and then delivering the
+ message by referring to this temporary area. Staging is how the protocol handles
+ partial file transfers - if a message is partially staged and the connection breaks,
+ the next time the sender starts to stage it, it can restart from where it left off.
+ </doc>
+
+ <response name = "open-ok" />
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier. This is an arbitrary string chosen by the
+ sender. For staging to work correctly the sender must use the same staging
+ identifier when staging the same message a second time after recovery from a
+ failure. A good choice for the staging identifier would be the SHA1 hash of the
+ message properties data (including the original filename, revised time, etc.).
+ </doc>
+ </field>
+
+ <field name = "content-size" domain = "longlong" label = "message content size">
+ <doc>
+ The size of the content in octets. The recipient may use this information to
+ allocate or check available space in advance, to avoid "disk full" errors during
+ staging of very large messages.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The sender MUST accurately fill the content-size field. Zero-length content
+ is permitted.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "41" label = "confirm staging ready">
+ <doc>
+ This method confirms that the recipient is ready to accept staged data. If the
+ message was already partially-staged at a previous time the recipient will report
+ the number of octets already staged.
+ </doc>
+
+ <response name = "stage" />
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "staged-size" domain = "longlong" label = "already staged amount">
+ <doc>
+ The amount of previously-staged content in octets. For a new message this will
+ be zero.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The sender MUST start sending data from this octet offset in the message,
+ counting from zero.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The recipient MAY decide how long to hold partially-staged content and MAY
+ implement staging by always discarding partially-staged content. However if
+ it uses the file content type it MUST support the staging methods.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "stage" content = "1" index = "50" label = "stage message content">
+ <doc>
+ This method stages the message, sending the message content to the recipient from
+ the octet offset specified in the Open-Ok method.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" index = "60" label = "publish a message">
+ <doc>
+ This method publishes a staged file message to a specific exchange. The file message
+ will be routed to queues as defined by the exchange configuration and distributed to
+ any active consumers when the transaction, if any, is committed.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST
+ respond with a reply code 403 (access refused) and raise a channel
+ exception.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse file content in which case it MUST respond with a
+ reply code 540 (not implemented) and raise a channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the mandatory flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the immediate flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier of the message to publish. The message must have
+ been staged. Note that a client can send the Publish method asynchronously
+ without waiting for staging to finish.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "70" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" index = "80" label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a staged file message to the client, via a consumer. In the
+ asynchronous message delivery model, the client starts a consumer using the Consume
+ method, then the server responds with Deliver methods as and when messages arrive
+ for that consumer.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server SHOULD track the number of times a message has been delivered to
+ clients and when a message is redelivered a certain number of times - e.g. 5
+ times - without being acknowledged, the server SHOULD consider the message to be
+ unprocessable (possibly causing client applications to abort), and move the
+ message to a dead letter queue.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier of the message to deliver. The message must have
+ been staged. Note that a server can send the Deliver method asynchronously
+ without waiting for staging to finish.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "ack" index = "90" label = "acknowledge one or more messages">
+ <doc>
+ This method acknowledges one or more messages delivered via the Deliver method. The
+ client can ask to confirm a single message or a set of messages up to and including
+ a specific message.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "multiple" domain = "bit" label = "acknowledge multiple messages">
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including", so that the
+ client can acknowledge multiple messages with a single method. If set to zero,
+ the delivery tag refers to a single message. If the multiple field is 1, and the
+ delivery tag is zero, tells the server to acknowledge all outstanding messages.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST validate that a non-zero delivery-tag refers to an delivered
+ message, and raise a channel exception if this is not the case.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "reject" index = "100" label = "reject an incoming message">
+ <doc>
+ This method allows a client to reject a message. It can be used to return
+ untreatable messages to their original queue. Note that file content is staged
+ before delivery, so the client will not use this method to interrupt delivery of a
+ large message.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD interpret this method as meaning that the client is unable to
+ process the message at this time.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "02">
+ <doc>
+ A client MUST NOT use this method as a means of selecting messages to process. A
+ rejected message MAY be discarded or dead-lettered, not necessarily passed to
+ another client.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be discarded. If this bit is 1, the
+ server will attempt to requeue the message.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST NOT deliver the message to the same client within the
+ context of the current channel. The recommended strategy is to attempt to
+ deliver the message to an alternative consumer, and if that is not possible,
+ to move the message to a dead-letter queue. The server MAY use more
+ sophisticated tracking to hold the message on the queue and redeliver it to
+ the same client at a later stage.
+ </doc>
+ </rule>
+ </field>
+ </method>
+ </class>
+
+ <!-- == STREAM =========================================================== -->
+
+ <class name = "stream" handler = "channel" index = "80" label = "work with streaming content">
+ <doc>
+ The stream class provides methods that support multimedia streaming. The stream class
+ uses the following semantics: one message is one packet of data; delivery is
+ unacknowledged and unreliable; the consumer can specify quality of service parameters
+ that the server can try to adhere to; lower-priority messages may be discarded in favour
+ of high priority messages.
+ </doc>
+
+ <doc type = "grammar">
+ stream = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN
+ / S:DELIVER content
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD discard stream messages on a priority basis if the queue size
+ exceeds some configured limit.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST implement at least 2 priority levels for stream messages, where
+ priorities 0-4 and 5-9 are treated as two distinct levels. The server MAY implement
+ up to 10 priority levels.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The server MUST implement automatic acknowledgements on stream content. That is, as
+ soon as a message is delivered to a client via a Deliver method, the server must
+ remove it from the queue.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a Stream content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. May be set
+ to zero, meaning "no specific limit". Note that other prefetch limits may still
+ apply.
+ </doc>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This field may be used
+ in combination with the prefetch-size field; a message will only be sent in
+ advance if both prefetch windows (and those at the channel and connection level)
+ allow it.
+ </doc>
+ </field>
+
+ <field name = "consume-rate" domain = "long" label = "transfer rate in octets/second">
+ <doc>
+ Specifies a desired transfer rate in octets per second. This is usually
+ determined by the application that uses the streaming data. A value of zero
+ means "no limit", i.e. as rapidly as possible.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MAY ignore the prefetch values and consume rates, depending on
+ the type of stream and the ability of the server to queue and/or reply it.
+ The server MAY drop low-priority messages in favour of high-priority
+ messages.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, unless the queue was
+ declared as private, and ideally, impose no limit except as defined by available
+ resources.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ Streaming applications SHOULD use different channels to select different
+ streaming resolutions. AMQP makes no provision for filtering and/or transforming
+ streams except on the basis of priority-based selective delivery of individual
+ messages.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The tag MUST NOT refer to an existing consumer. If the client attempts to
+ create two consumers with the same non-empty tag the server MUST raise a
+ connection exception with reply code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>
+ If the server cannot grant exclusive access to the queue when asked, -
+ because there are other consumers active - it MUST raise a channel exception
+ with return code 405 (resource locked).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "filter" domain = "table" label = "arguments for consuming">
+ <doc>
+ A set of filters for the consume. The syntax and semantics
+ of these filters depends on the providers implementation.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ This method provides the client with a consumer tag which it may use in methods that
+ work with the consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>Holds the consumer tag specified by the client or provided by the server.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. Since message delivery is asynchronous the client
+ may continue to receive messages for a short while after cancelling a consumer. It
+ may process or discard these as appropriate.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "cancel-ok" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>This method confirms that the cancellation was completed.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" content = "1" index = "40" label = "publish a message">
+ <doc>
+ This method publishes a message to a specific exchange. The message will be routed
+ to queues as defined by the exchange configuration and distributed to any active
+ consumers as appropriate.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST
+ respond with a reply code 403 (access refused) and raise a channel
+ exception.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse stream content in which case it MUST respond with a
+ reply code 540 (not implemented) and raise a channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_stream_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the mandatory flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+
+ <!-- Rule test name: was "amq_stream_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the immediate flag.</doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "50" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" content = "1" index = "60"
+ label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a message to the client, via a consumer. In the asynchronous
+ message delivery model, the client starts a consumer using the Consume method, then
+ the server responds with Deliver methods as and when messages arrive for that
+ consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue that the message came from. Note that a single
+ channel can start many consumers on different queues.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+ </class>
+
+ <!-- == TX =============================================================== -->
+
+ <class name = "tx" handler = "channel" index = "90" label = "work with standard transactions">
+ <doc>
+ Standard transactions provide so-called "1.5 phase commit". We can ensure that work is
+ never lost, but there is a chance of confirmations being lost, so that messages may be
+ resent. Applications that use standard transactions must be able to detect and ignore
+ duplicate messages.
+ </doc>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "01">
+ <doc>
+ An client using standard transactions SHOULD be able to track all messages received
+ within a reasonable period, and thus detect and reject duplicates of the same
+ message. It SHOULD NOT pass these to the application layer.
+ </doc>
+ </rule>
+
+ <doc type = "grammar">
+ tx = C:SELECT S:SELECT-OK
+ / C:COMMIT S:COMMIT-OK
+ / C:ROLLBACK S:ROLLBACK-OK
+ </doc>
+
+ <chassis name = "server" implement = "SHOULD" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "select" synchronous = "1" index = "10" label = "select standard transaction mode">
+ <doc>
+ This method sets the channel to use standard transactions. The client must use this
+ method at least once on a channel before using the Commit or Rollback methods.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "select-ok" />
+ </method>
+
+ <method name = "select-ok" synchronous = "1" index = "11" label = "confirm transaction mode">
+ <doc>
+ This method confirms to the client that the channel was successfully set to use
+ standard transactions.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "commit" synchronous = "1" index = "20" label = "commit the current transaction">
+ <doc>
+ This method commits all messages published and acknowledged in the current
+ transaction. A new transaction starts immediately after a commit.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "commit-ok" />
+ </method>
+
+ <method name = "commit-ok" synchronous = "1" index = "21" label = "confirm a successful commit">
+ <doc>
+ This method confirms to the client that the commit succeeded. Note that if a commit
+ fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "rollback" synchronous = "1" index = "30"
+ label = "abandon the current transaction">
+ <doc>
+ This method abandons all messages published and acknowledged in the current
+ transaction. A new transaction starts immediately after a rollback.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "rollback-ok" />
+ </method>
+
+ <method name = "rollback-ok" synchronous = "1" index = "31" label = "confirm successful rollback">
+ <doc>
+ This method confirms to the client that the rollback succeeded. Note that if an
+ rollback fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == DTX ============================================================== -->
+
+ <class name = "dtx" handler = "channel" index = "100" label = "work with distributed transactions">
+ <doc>
+ Distributed transactions provide so-called "2-phase commit". The AMQP distributed
+ transaction model supports the X-Open XA architecture and other distributed transaction
+ implementations. The Dtx class assumes that the server has a private communications
+ channel (not AMQP) to a distributed transaction coordinator.
+ </doc>
+
+ <doc type = "grammar">
+ dtx = C:SELECT S:SELECT-OK
+ C:START S:START-OK
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "select" synchronous = "1" index = "10" label = "select standard transaction mode">
+ <doc>
+ This method sets the channel to use distributed transactions. The client must use
+ this method at least once on a channel before using the Start method.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "select-ok" />
+ </method>
+
+ <method name = "select-ok" synchronous = "1" index = "11" label = "confirm transaction mode">
+ <doc>
+ This method confirms to the client that the channel was successfully set to use
+ distributed transactions.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "start" synchronous = "1" index = "20"
+ label = "start a new distributed transaction">
+ <doc>
+ This method starts a new distributed transaction. This must be the first method on a
+ new channel that uses the distributed transaction mode, before any methods that
+ publish or consume messages.
+ </doc>
+ <chassis name = "server" implement = "MAY" />
+ <response name = "start-ok" />
+ <field name = "dtx-identifier" domain = "shortstr" label = "transaction identifier">
+ <doc>
+ The distributed transaction key. This identifies the transaction so that the
+ AMQP server can coordinate with the distributed transaction coordinator.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <method name = "start-ok" synchronous = "1" index = "21"
+ label = "confirm the start of a new distributed transaction">
+ <doc>
+ This method confirms to the client that the transaction started. Note that if a
+ start fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == TUNNEL =========================================================== -->
+
+ <class name = "tunnel" handler = "tunnel" index = "110" label = "methods for protocol tunnelling">
+ <doc>
+ The tunnel methods are used to send blocks of binary data - which can be serialised AMQP
+ methods or other protocol frames - between AMQP peers.
+ </doc>
+
+ <doc type = "grammar">
+ tunnel = C:REQUEST
+ / S:REQUEST
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "proxy-name" domain = "shortstr" label = "identity of tunnelling proxy" />
+ <field name = "data-name" domain = "shortstr" label = "name or type of message being tunnelled" />
+ <field name = "durable" domain = "octet" label = "message durability indicator" />
+ <field name = "broadcast" domain = "octet" label = "message broadcast mode" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "request" content = "1" index = "10" label = "sends a tunnelled method">
+ <doc>
+ This method tunnels a block of binary data, which can be an encoded
+ AMQP method or other data. The binary data is sent as the content for
+ the Tunnel.Request method.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <field name = "meta-data" domain = "table" label = "meta data for the tunnelled block">
+ <doc>
+ This field table holds arbitrary meta-data that the sender needs to
+ pass to the recipient.
+ </doc>
+ </field>
+ </method>
+ </class>
+
+</amqp>