summaryrefslogtreecommitdiff
path: root/Final/java
diff options
context:
space:
mode:
Diffstat (limited to 'Final/java')
-rwxr-xr-xFinal/java/broker/bin/msTool.sh60
-rw-r--r--Final/java/broker/bin/qpid-passwd30
-rw-r--r--Final/java/broker/bin/qpid-server31
-rw-r--r--Final/java/broker/bin/qpid-server-bdb.bat22
-rw-r--r--Final/java/broker/bin/qpid-server.bat70
-rw-r--r--Final/java/broker/bin/qpid.start21
-rw-r--r--Final/java/broker/bin/qpid.stop137
-rw-r--r--Final/java/broker/bin/qpid.stopall74
-rwxr-xr-xFinal/java/broker/bin/run.bat31
-rwxr-xr-xFinal/java/broker/bin/run.sh44
-rw-r--r--Final/java/broker/bin/runAll37
-rw-r--r--Final/java/broker/distribution/pom.xml153
-rw-r--r--Final/java/broker/distribution/src/main/assembly/broker-bin-tests.xml116
-rw-r--r--Final/java/broker/distribution/src/main/assembly/broker-bin.xml183
-rw-r--r--Final/java/broker/distribution/src/main/assembly/broker-src.xml78
-rw-r--r--Final/java/broker/etc/access19
-rw-r--r--Final/java/broker/etc/config.xml175
-rw-r--r--Final/java/broker/etc/debug.log4j.xml114
-rw-r--r--Final/java/broker/etc/jmxremote.access23
-rw-r--r--Final/java/broker/etc/log4j.xml112
-rw-r--r--Final/java/broker/etc/md5passwd21
-rw-r--r--Final/java/broker/etc/mstool-log4j.xml54
-rw-r--r--Final/java/broker/etc/passwd19
-rw-r--r--Final/java/broker/etc/passwdVhost19
-rw-r--r--Final/java/broker/etc/persistent_config.xml132
-rw-r--r--Final/java/broker/etc/qpid-server.conf25
-rw-r--r--Final/java/broker/etc/qpid-server.conf.jpp49
-rw-r--r--Final/java/broker/etc/qpid.passwd22
-rw-r--r--Final/java/broker/etc/transient_config.xml128
-rw-r--r--Final/java/broker/etc/virtualhosts.xml123
-rw-r--r--Final/java/broker/pom.xml256
-rwxr-xr-xFinal/java/broker/python-test.xml56
-rw-r--r--Final/java/broker/src/main/grammar/SelectorParser.jj604
-rw-r--r--Final/java/broker/src/main/java/log4j.properties24
-rw-r--r--Final/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java1007
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java188
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java244
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java989
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java25
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/Main.java521
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java68
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java68
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java139
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java79
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java80
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java235
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java118
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java269
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java217
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java75
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java138
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java229
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java426
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java97
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java32
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java45
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java51
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java209
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java219
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java323
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java90
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java98
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java40
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java48
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java275
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java106
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java40
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java595
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java210
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java37
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java37
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java77
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java68
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java110
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java29
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java42
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java322
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java76
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java337
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java126
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java57
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java102
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java67
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java79
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java173
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java95
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java105
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java58
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java64
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java130
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java78
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java53
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java72
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java61
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java71
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java63
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java118
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java141
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java173
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java54
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java205
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java114
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java70
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java34
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java135
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java206
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java122
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java115
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java77
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java73
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java63
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java110
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java97
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java191
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java283
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java41
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java39
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java38
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java388
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java239
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java43
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java37
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java34
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java98
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java58
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java48
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java30
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java60
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java57
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java62
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java282
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java773
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java46
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java240
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java52
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java175
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java305
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java49
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java67
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java135
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java46
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java1000
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java79
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java940
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java471
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java56
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java1049
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java71
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java100
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java137
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java50
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java143
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java245
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java52
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java46
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java92
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java47
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java138
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java27
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java43
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java63
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java43
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java680
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java34
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java229
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java121
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java227
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java26
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java213
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java176
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java71
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java467
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java34
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java155
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java66
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java63
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java27
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java42
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java41
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java183
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java108
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java118
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java68
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java43
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java599
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java236
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java240
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java130
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java100
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java34
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java160
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java48
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java37
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java249
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java76
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java47
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java123
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java44
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java38
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java129
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java60
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java50
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java105
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java61
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java71
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java38
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java149
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java60
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java36
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java284
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java52
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java35
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java30
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java209
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java261
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java68
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java97
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java705
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java77
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java262
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java227
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java58
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java171
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java109
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java55
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java131
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java38
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java105
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java122
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java44
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java259
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java70
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java652
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java66
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java85
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java36
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java55
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java299
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java98
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java314
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java94
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java205
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java68
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java54
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java233
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java513
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java81
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java51
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java90
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java121
-rw-r--r--Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java363
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java130
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java50
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java607
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java138
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java199
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java295
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java52
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java301
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java300
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java73
-rw-r--r--Final/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java88
-rw-r--r--Final/java/client-java14/README.txt33
-rw-r--r--Final/java/client-java14/etc/sasl.properties20
-rw-r--r--Final/java/client-java14/pom.xml224
-rw-r--r--Final/java/client-java14/src/main/assembly/client-java14-bin.xml74
-rw-r--r--Final/java/client-java14/src/main/assembly/jar-with-dependencies.xml104
-rw-r--r--Final/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java343
-rw-r--r--Final/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java347
-rw-r--r--Final/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java275
-rw-r--r--Final/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java61
-rw-r--r--Final/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java66
-rw-r--r--Final/java/client/distribution/pom.xml156
-rw-r--r--Final/java/client/distribution/src/main/assembly/client-bin-tests.xml107
-rw-r--r--Final/java/client/distribution/src/main/assembly/client-bin.xml78
-rw-r--r--Final/java/client/distribution/src/main/assembly/client-java1.4-bin.xml74
-rw-r--r--Final/java/client/distribution/src/main/assembly/client-src.xml62
-rw-r--r--Final/java/client/example/bin/set_classpath.bat50
-rwxr-xr-xFinal/java/client/example/bin/set_classpath.sh83
-rw-r--r--Final/java/client/example/pom.xml152
-rw-r--r--Final/java/client/example/source-jar.xml35
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml45
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java162
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java136
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java54
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java141
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java104
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java181
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java57
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java72
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java123
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java81
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java98
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java54
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java54
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java168
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java80
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java57
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties39
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java139
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java51
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java181
-rw-r--r--Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java51
-rw-r--r--Final/java/client/pom.xml249
-rw-r--r--Final/java/client/src/main/java/client.log4j28
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java42
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java353
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java1289
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java409
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java455
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java451
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java59
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java40
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java40
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java149
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java136
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java204
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQSession.java2800
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java26
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java69
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java72
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java115
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java226
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java45
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java996
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java691
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/Closeable.java83
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java72
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java65
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java36
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java65
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java31
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java43
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java97
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java115
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java230
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java61
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java38
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java205
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java126
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java49
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java240
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java75
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java49
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java128
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java64
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java47
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java59
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java52
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java53
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java105
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java50
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java52
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java102
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java48
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java73
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java73
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java239
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java97
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java59
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java58
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.java135
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java151
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java801
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java685
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java103
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java388
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java43
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java552
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java507
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java43
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java197
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java43
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java204
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java43
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java201
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java46
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java202
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java41
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java127
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.java45
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java131
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java732
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java459
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java311
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java61
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java121
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java115
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java30
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java231
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties21
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java198
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties20
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java71
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java102
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java60
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java105
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java63
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java72
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java56
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java48
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java26
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java276
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.java53
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java36
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/StateListener.java30
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java122
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java57
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java62
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java41
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java32
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java101
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java318
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java65
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java109
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java58
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java74
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/ChannelLimitReachedException.java46
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/Connection.java69
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionListener.java58
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java86
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java324
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/Message.java28
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/MessageConsumer.java27
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java53
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/Session.java101
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java76
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java261
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java147
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jndi/Example.properties39
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java37
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java337
-rw-r--r--Final/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java527
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java185
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java213
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java212
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt11
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java129
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java277
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/codec/Client.java133
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/codec/Server.java103
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java35
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java69
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java29
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/config/Connector.java40
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java28
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java111
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java112
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java196
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java167
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java117
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java175
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java133
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java273
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java196
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java166
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java153
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java102
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java93
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java271
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java269
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java176
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java122
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java95
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java153
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties38
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/topic/Config.java243
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java141
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java155
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java175
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java110
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java45
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java45
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java127
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java44
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java151
-rw-r--r--Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java185
-rw-r--r--Final/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java125
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java152
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java252
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java44
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java252
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java195
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java277
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java71
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.java234
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java46
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java335
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java288
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java96
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java176
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java62
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java130
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java194
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java1277
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java231
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java276
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java369
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java77
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.java115
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java140
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java122
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java257
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java80
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java205
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java115
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java94
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java100
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java235
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java412
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java87
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java107
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java175
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java194
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java481
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java145
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java123
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java68
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java84
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java112
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java46
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java569
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java383
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java345
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java623
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java300
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.java104
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java232
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java147
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java389
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java72
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java94
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java108
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java138
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java411
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java160
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java192
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java80
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java375
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java532
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java313
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/testutil/Config.java199
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.java287
-rw-r--r--Final/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.java52
-rw-r--r--Final/java/client/test/bin/IBM-JNDI-Setup.bat69
-rwxr-xr-xFinal/java/client/test/bin/IBM-JNDI-Setup.sh27
-rw-r--r--Final/java/client/test/bin/IBM-Publisher.bat62
-rwxr-xr-xFinal/java/client/test/bin/IBM-Publisher.sh22
-rw-r--r--Final/java/client/test/bin/IBM-PutGet.bat62
-rwxr-xr-xFinal/java/client/test/bin/IBM-PutGet.sh21
-rw-r--r--Final/java/client/test/bin/IBM-README.txt19
-rw-r--r--Final/java/client/test/bin/IBM-Receiver.bat62
-rwxr-xr-xFinal/java/client/test/bin/IBM-Receiver.sh22
-rw-r--r--Final/java/client/test/bin/IBM-Sender.bat62
-rwxr-xr-xFinal/java/client/test/bin/IBM-Sender.sh22
-rw-r--r--Final/java/client/test/bin/IBM-Subscriber.bat62
-rwxr-xr-xFinal/java/client/test/bin/IBM-Subscriber.sh22
-rwxr-xr-xFinal/java/client/test/bin/headersListener.sh22
-rwxr-xr-xFinal/java/client/test/bin/headersListenerGroup.sh25
-rwxr-xr-xFinal/java/client/test/bin/headersPublisher.sh22
-rwxr-xr-xFinal/java/client/test/bin/run_many.sh30
-rwxr-xr-xFinal/java/client/test/bin/serviceProvidingClient.sh24
-rwxr-xr-xFinal/java/client/test/bin/serviceRequestingClient.sh27
-rwxr-xr-xFinal/java/client/test/bin/testService.sh22
-rwxr-xr-xFinal/java/client/test/bin/topicListener.sh23
-rwxr-xr-xFinal/java/client/test/bin/topicPublisher.sh22
-rw-r--r--Final/java/client/test/etc/ApacheDS.properties24
-rw-r--r--Final/java/client/test/example_build.xml104
-rw-r--r--Final/java/cluster/doc/design.docbin0 -> 70144 bytes
-rw-r--r--Final/java/cluster/pom.xml69
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java42
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java46
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java45
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java91
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java26
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java247
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java26
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java368
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java73
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java130
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java63
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java60
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java190
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java133
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java80
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java396
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java72
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java107
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java31
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java90
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java26
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java107
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java117
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java31
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java26
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java36
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java28
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java29
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java28
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java44
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java272
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java36
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java30
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java28
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java98
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java48
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java166
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java55
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java73
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java136
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java51
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java239
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java55
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java25
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java79
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java38
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java60
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java62
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java59
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java69
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java90
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java125
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java56
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java85
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java31
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java31
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java31
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java29
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java31
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java48
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java83
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java32
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java77
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java37
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java311
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java83
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java72
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java53
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java61
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java175
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java102
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java116
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java64
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java60
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java91
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java176
-rw-r--r--Final/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java56
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java270
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java237
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java43
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java106
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java53
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java29
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java45
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java57
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java70
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java29
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java47
-rw-r--r--Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java269
-rw-r--r--Final/java/common/bin/qpid-run238
-rw-r--r--Final/java/common/etc/qpid-run.conf25
-rw-r--r--Final/java/common/etc/qpid-run.conf.dev26
-rw-r--r--Final/java/common/pom.xml144
-rw-r--r--Final/java/common/protocol-version.xml60
-rw-r--r--Final/java/common/readme.txt4
-rw-r--r--Final/java/common/src/main/java/log4j.properties28
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java41
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQChannelException.java68
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java44
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java73
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java40
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQException.java101
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java48
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java43
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java50
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java77
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java163
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java66
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java61
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java37
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java190
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/configuration/Configured.java44
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java60
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java164
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java65
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java53
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java120
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java61
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java75
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java41
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java132
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java46
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java31
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java41
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java39
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java436
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQType.java701
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java48
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java80
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java799
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java31
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java81
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java103
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/Content.java26
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java106
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java48
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java123
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java50
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java58
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java57
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java35
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java1028
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java1017
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java38
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java71
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java31
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java125
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java194
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java98
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java198
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java47
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java32
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java36
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java32
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java32
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/pool/Event.java155
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/pool/Job.java162
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java471
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java102
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java145
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java227
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java95
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java69
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java43
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java46
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java47
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java157
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java294
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/url/BindingURL.java56
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/url/URLHelper.java172
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java97
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java689
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java250
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java70
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java38
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/FileUtils.java195
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java43
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java75
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java200
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java228
-rw-r--r--Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java44
-rw-r--r--Final/java/common/src/main/resources/org/apache/qpid/ssl/qpid.certbin0 -> 756 bytes
-rw-r--r--Final/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java188
-rw-r--r--Final/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java906
-rw-r--r--Final/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java111
-rw-r--r--Final/java/common/src/test/java/org/apache/qpid/session/TestSession.java277
-rw-r--r--Final/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java554
-rw-r--r--Final/java/distribution/pom.xml210
-rw-r--r--Final/java/distribution/src/main/assembly/bin-test.xml120
-rw-r--r--Final/java/distribution/src/main/assembly/bin.xml127
-rw-r--r--Final/java/distribution/src/main/assembly/management-eclipse-plugin-unix.xml131
-rw-r--r--Final/java/distribution/src/main/assembly/management-eclipse-plugin.xml162
-rw-r--r--Final/java/distribution/src/main/assembly/src.xml106
-rw-r--r--Final/java/doc/AMQBlazeDetailedDesign.vsdbin0 -> 120320 bytes
-rw-r--r--Final/java/doc/FramingClassDiagram.vsdbin0 -> 206848 bytes
-rw-r--r--Final/java/etc/coding_standards.xml118
-rw-r--r--Final/java/etc/license_header.txt20
-rw-r--r--Final/java/etc/log4j.xml61
-rw-r--r--Final/java/integrationtests/README.txt13
-rw-r--r--Final/java/integrationtests/docs/RunningSustainedTests.txt17
-rw-r--r--Final/java/integrationtests/jar-with-dependencies.xml47
-rw-r--r--Final/java/integrationtests/pom.xml146
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java135
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java209
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java239
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java214
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java243
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java84
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java90
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java88
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java193
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java193
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java906
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java126
-rw-r--r--Final/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java463
-rw-r--r--Final/java/integrationtests/src/resources/sustained-log4j.xml69
-rw-r--r--Final/java/management/eclipse-plugin/META-INF/MANIFEST.MF13
-rw-r--r--Final/java/management/eclipse-plugin/README.txt21
-rw-r--r--Final/java/management/eclipse-plugin/bin/qpidmc.bat55
-rwxr-xr-xFinal/java/management/eclipse-plugin/bin/qpidmc.sh64
-rwxr-xr-xFinal/java/management/eclipse-plugin/bin/qpidmc_gtk.sh24
-rwxr-xr-xFinal/java/management/eclipse-plugin/bin/qpidmc_motif.sh24
-rw-r--r--Final/java/management/eclipse-plugin/icons/Thumbs.dbbin0 -> 97280 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/add.gifbin0 -> 318 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/delete.gifbin0 -> 143 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/icon_ClosedFolder.gifbin0 -> 160 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/icon_OpenFolder.gifbin0 -> 152 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/mbean_view.pngbin0 -> 2046 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/notifications.gifbin0 -> 104 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/qpidConnections.gifbin0 -> 168 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/qpidmc.gifbin0 -> 1225 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/qpidmc16.gifbin0 -> 928 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/qpidmc32.bmpbin0 -> 1139 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/qpidmc32.gifbin0 -> 1139 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/reconnect.gifbin0 -> 327 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/refresh.gifbin0 -> 182 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/splash.bmpbin0 -> 207078 bytes
-rw-r--r--Final/java/management/eclipse-plugin/icons/stop.gifbin0 -> 215 bytes
-rw-r--r--Final/java/management/eclipse-plugin/plugin.properties20
-rw-r--r--Final/java/management/eclipse-plugin/plugin.xml223
-rw-r--r--Final/java/management/eclipse-plugin/pom.xml252
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java84
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java63
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationActionBarAdvisor.java96
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java147
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchAdvisor.java46
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java65
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java140
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java132
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java40
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java103
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java46
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ServerRegistry.java172
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java136
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AddServer.java274
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/CloseConnection.java56
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/EditAttribute.java51
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/ReconnectServer.java209
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/Refresh.java53
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/RemoveServer.java50
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/VersionAction.java89
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/InfoRequiredException.java36
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java31
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java77
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientNotificationListener.java41
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java48
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java628
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java466
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/AttributeData.java96
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ManagedAttributeModel.java118
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationInfoModel.java51
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java101
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationData.java110
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationDataModel.java72
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ParameterData.java95
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/CRAMMD5HashedSaslClientFactory.java60
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/ClientSaslFactory.java54
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/JCAProvider.java56
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/PlainSaslClient.java203
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/SaslProvider.java35
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UserPasswordCallbackHandler.java73
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UsernameHashedPasswordCallbackHandler.java82
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java936
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ConnectionTypeTabControl.java59
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ExchangeTypeTabControl.java60
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/INotificationViewer.java32
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTypeTabControl.java336
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanView.java545
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java1253
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NotificationsTabControl.java427
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NumberVerifyListener.java46
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java899
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/QueueTypeTabControl.java296
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TabControl.java102
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TreeObject.java126
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/VHNotificationsTabControl.java483
-rw-r--r--Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ViewUtility.java641
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/.eclipseproduct23
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/eclipse.exebin0 -> 180224 bytes
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/eclipse.ini4
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/license.eclipse.txt88
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/sasl/MANIFEST.MF8
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/startup.jarbin0 -> 33049 bytes
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/unix/configuration/config.ini27
-rw-r--r--Final/java/management/eclipse-plugin/src/main/resources/win32/configuration/config.ini26
-rw-r--r--Final/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java118
-rw-r--r--Final/java/perftests/RunningPerformanceTests.txt141
-rwxr-xr-xFinal/java/perftests/bin/run_many.sh30
-rwxr-xr-xFinal/java/perftests/bin/topicListener.sh33
-rwxr-xr-xFinal/java/perftests/bin/topicPublisher.sh32
-rw-r--r--Final/java/perftests/dist-zip.xml45
-rw-r--r--Final/java/perftests/distribution/pom.xml131
-rw-r--r--Final/java/perftests/distribution/src/main/assembly/performance.xml103
-rwxr-xr-xFinal/java/perftests/etc/scripts/CTQ-Qpid-1.sh21
-rwxr-xr-xFinal/java/perftests/etc/scripts/CTQ-Qpid-2.sh21
-rwxr-xr-xFinal/java/perftests/etc/scripts/CTQ-Qpid-3.sh21
-rwxr-xr-xFinal/java/perftests/etc/scripts/CTQ-Qpid-4.sh21
-rwxr-xr-xFinal/java/perftests/etc/scripts/CTQ-Qpid-5.sh21
-rwxr-xr-xFinal/java/perftests/etc/scripts/CTQ-Qpid-6.sh21
-rwxr-xr-xFinal/java/perftests/etc/scripts/PT-Qpid-13.sh42
-rwxr-xr-xFinal/java/perftests/etc/scripts/PT-Qpid-14.sh41
-rwxr-xr-xFinal/java/perftests/etc/scripts/sendAndWaitClient.sh22
-rw-r--r--Final/java/perftests/jar-with-dependencies.xml82
-rw-r--r--Final/java/perftests/pom.xml714
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java121
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java35
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java69
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java29
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/config/Connector.java40
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java28
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java112
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java243
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java141
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java153
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java175
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java170
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java292
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java107
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java452
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java314
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java93
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java196
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java453
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java1688
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java251
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/topic/Config.java326
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java303
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java157
-rw-r--r--Final/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java186
-rw-r--r--Final/java/perftests/src/main/java/perftests.log4j45
-rw-r--r--Final/java/pom.xml773
-rw-r--r--Final/java/release-docs/RELEASE_NOTES.txt160
-rw-r--r--Final/java/resources/DISCLAIMER5
-rw-r--r--Final/java/resources/LICENSE341
-rw-r--r--Final/java/resources/META-INF/DISCLAIMER10
-rw-r--r--Final/java/resources/META-INF/LICENSE203
-rw-r--r--Final/java/resources/META-INF/NOTICE105
-rw-r--r--Final/java/resources/NOTICE79
-rw-r--r--Final/java/resources/README40
-rw-r--r--Final/java/systests/distribution/pom.xml112
-rw-r--r--Final/java/systests/distribution/src/main/assembly/systests.xml91
-rw-r--r--Final/java/systests/etc/bin/testclients.sh23
-rw-r--r--Final/java/systests/pom.xml111
-rw-r--r--Final/java/systests/src/main/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java125
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java92
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java232
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java336
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java101
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java296
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java308
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java305
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/failure/DeadlockTest.java211
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/failure/HeapExhaustion.java230
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java122
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java78
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MockIoSession.java297
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java374
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java265
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java177
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageReturnTest.java315
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java93
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java205
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/PersistentTestManual.java279
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/QueueDepthWithSelectorTest.java214
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java102
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java144
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java160
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java166
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java147
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java146
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java73
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java307
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnTest.java188
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/util/AveragedRun.java66
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/util/RunStats.java57
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java149
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/server/util/TimedRun.java52
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/VMBrokerSetup.java52
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java128
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java152
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/Assertion.java39
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/AssertionBase.java66
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/Circuit.java109
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEnd.java91
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java149
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/DropInTest.java51
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java158
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java280
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java11
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessageMonitor.java105
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java485
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java56
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java48
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestClientDetails.java86
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java189
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java45
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java123
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java69
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/LocalClockSynchronizer.java73
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java166
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java464
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java469
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java63
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java53
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java315
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java532
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java166
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java245
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java208
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/OptOutTestCase.java69
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClientControlledTest.java108
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java399
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java452
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java180
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java100
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/package.html22
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java128
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java111
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java201
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java145
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java296
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/util/ClasspathScanner.java234
-rw-r--r--Final/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java479
-rw-r--r--Final/java/systests/src/main/java/systests.log4j28
-rw-r--r--Final/java/systests/src/old_test/java/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java184
-rw-r--r--Final/java/systests/src/old_test/java/org/apache/qpid/server/protocol/TestProtocolInitiation.java266
-rw-r--r--Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java49
-rw-r--r--Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueuePerfTest.java258
-rw-r--r--Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/SendPerfTest.java181
-rw-r--r--Final/java/systests/src/old_test/java/org/apache/qpid/server/util/ConcurrentTest.java79
-rw-r--r--Final/java/systests/src/old_test/java/org/apache/qpid/test/unit/ack/DisconnectAndRedeliverTest.java215
1029 files changed, 153426 insertions, 0 deletions
diff --git a/Final/java/broker/bin/msTool.sh b/Final/java/broker/bin/msTool.sh
new file mode 100755
index 0000000000..73b1eb2ec7
--- /dev/null
+++ b/Final/java/broker/bin/msTool.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+die() {
+ if [[ $1 = -usage ]]; then
+ shift
+ usage=true
+ else
+ usage=false
+ fi
+ echo "$@"
+ $usage && echo
+ $usage && usage
+ exit 1
+}
+
+cygwin=false
+if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then
+ cygwin=true
+fi
+
+if [ -z "$QPID_TOOLS" ]; then
+ if [ -z "$QPID_HOME" ]; then
+ die "QPID_TOOLS must be set"
+ else
+ QPID_TOOLS=$QPID_HOME
+ fi
+fi
+
+if $cygwin; then
+ QPID_TOOLS=$(cygpath -w $QPID_TOOLS)
+fi
+
+# Set classpath to include Qpid jar with all required jars in manifest
+QPID_LIBS=$QPID_TOOLS/lib/qpid-incubating.jar
+
+# Set other variables used by the qpid-run script before calling
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_OPTS=-Dlog4j.configuration=file:$QPID_TOOLS/etc/mstool-log4j.xml \
+ QPID_CLASSPATH=$QPID_LIBS
+
+. qpid-run org.apache.qpid.tools.messagestore.MessageStoreTool "$@"
diff --git a/Final/java/broker/bin/qpid-passwd b/Final/java/broker/bin/qpid-passwd
new file mode 100644
index 0000000000..f046252522
--- /dev/null
+++ b/Final/java/broker/bin/qpid-passwd
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Set classpath to include Qpid jar with all required jars in manifest
+QPID_LIBS=$QPID_HOME/lib/qpid-incubating.jar
+
+# Set other variables used by the qpid-run script before calling
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ QPID_CLASSPATH=$QPID_LIBS
+
+. qpid-run org.apache.qpid.tools.security.Passwd "$@"
diff --git a/Final/java/broker/bin/qpid-server b/Final/java/broker/bin/qpid-server
new file mode 100644
index 0000000000..f1f4d72e64
--- /dev/null
+++ b/Final/java/broker/bin/qpid-server
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Set classpath to include Qpid jar with all required jars in manifest
+QPID_LIBS=$QPID_HOME/lib/qpid-incubating.jar:$QPID_HOME/lib/bdbstore-launch.jar
+
+# Set other variables used by the qpid-run script before calling
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ JAVA_GC="-XX:-UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \
+ QPID_CLASSPATH=$QPID_LIBS
+
+. qpid-run org.apache.qpid.server.Main "$@"
diff --git a/Final/java/broker/bin/qpid-server-bdb.bat b/Final/java/broker/bin/qpid-server-bdb.bat
new file mode 100644
index 0000000000..8964e577df
--- /dev/null
+++ b/Final/java/broker/bin/qpid-server-bdb.bat
@@ -0,0 +1,22 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+set BDBSTORE_HOME=c:\qpid\trunk\java\bdbstore
+set QPID_MODULE_JARS=%BDBSTORE_HOME%\target\qpid-bdbstore-1.0-incubating-M2-SNAPSHOT.jar;%BDBSTORE_HOME%\lib\bdb\je-3.1.0.jar
+.\qpid-server.bat \ No newline at end of file
diff --git a/Final/java/broker/bin/qpid-server.bat b/Final/java/broker/bin/qpid-server.bat
new file mode 100644
index 0000000000..a99022cd2d
--- /dev/null
+++ b/Final/java/broker/bin/qpid-server.bat
@@ -0,0 +1,70 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+echo off
+REM Script to run the Qpid Java Broker
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\qpid-server.bat" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\qpid-server.bat" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of agruments (up to the command line limit, anyway).
+set QPID_ARGS=%1
+if ""%1""=="""" goto runCommand
+shift
+:loop
+if ""%1""=="""" goto runCommand
+set QPID_ARGS=%QPID_ARGS% %1
+shift
+goto loop
+
+rem QPID_OPTS intended to hold any -D props for use
+rem user must enclose any value for QPID_OPTS in double quotes
+:runCommand
+set LAUNCH_JAR=%QPID_HOME%\lib\qpid-incubating.jar
+set MODULE_JARS=%QPID_MODULE_JARS%
+"%JAVA_HOME%\bin\java" -server -Xmx1024m %QPID_OPTS% -DQPID_HOME="%QPID_HOME%" -cp "%LAUNCH_JAR%;%MODULE_JARS%" org.apache.qpid.server.Main %QPID_ARGS%
+
+:end
diff --git a/Final/java/broker/bin/qpid.start b/Final/java/broker/bin/qpid.start
new file mode 100644
index 0000000000..99f2d89f64
--- /dev/null
+++ b/Final/java/broker/bin/qpid.start
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+qpid-server -run:debug "$@" \ No newline at end of file
diff --git a/Final/java/broker/bin/qpid.stop b/Final/java/broker/bin/qpid.stop
new file mode 100644
index 0000000000..6482fc3293
--- /dev/null
+++ b/Final/java/broker/bin/qpid.stop
@@ -0,0 +1,137 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# qpid.stop Script
+#
+# Script checks for a given pid running PROGRAM and attempts to quit it
+#
+
+MAX_ATTEMPTS=1
+SLEEP_DELAY=1
+PROGRAM="DQPID"
+
+
+#
+# Print what is going to be done
+#
+printActions()
+{
+#ps=`ps o command p $1|grep $PROGRAM`
+ps=`ps -o args -p $1|grep $PROGRAM`
+echo "Attempting to kill: $ps"
+}
+
+#
+# Forcably Quit the specified PID($1)
+#
+forceQuit()
+{
+kill -9 $1
+}
+
+
+#
+# Gracefully ask the PID($1) to quit
+#
+quit()
+{
+kill $1
+}
+
+#
+# Grep the ps log for the PID ($1) to ensure that it has quit
+#
+lookup()
+{
+result=`ps -o args -p $1 |grep -v grep |grep $PROGRAM |wc -l`
+}
+
+#
+# Sleep and then check then lookup the PID($1) to ensure it has quit
+#
+check()
+{
+echo "Waiting $SLEEP_DELAY second for $1 to exit"
+sleep $SLEEP_DELAY
+lookup $1
+}
+
+
+
+#
+# Verify the PID($1) is available
+#
+verifyPid()
+{
+lookup $1
+if [[ $[$result] == 1 ]] ; then
+ brokerspid=$1
+else
+ echo "Unable to locate Qpid Process with PID $1"
+ exit -1
+fi
+}
+
+#
+# Main Run
+#
+
+# Check if we are killing all qpid pids or just one.
+if [[ $# == 0 ]] ; then
+ echo "Killing All Qpid Brokers for user: '$USER'"
+ qpid.stopall
+ exit $?
+else
+ verifyPid $1
+fi
+
+printActions $brokerspid
+
+# Attempt to quit the process MAX_ATTEMPTS Times
+attempt=0
+while [[ $[$result] > 0 && $[$attempt] < $[$MAX_ATTEMPTS] ]] ; do
+ quit $brokerspid
+ check $brokerspid
+ attempt=$[$attempt + 1]
+done
+
+# Check that it has quit
+if [[ $[$result] == 0 ]] ; then
+ echo "Process quit"
+ exit 0
+else
+
+ # Now attempt to force quit the process
+ attempt=0
+ while [[ $[$result] > 0 && $[$attempt] < $[$MAX_ATTEMPTS] ]] ; do
+ forceQuit $brokerspid
+ check $brokerspid
+ attempt=$[$attempt + 1]
+ done
+
+
+ # Output final status
+ if [[ $[$result] > 0 && $[$attempt] == $[$MAX_ATTEMPTS] ]] ; then
+ echo "Stopped trying to kill process: $brokerspid"
+ echo "Attempted to stop $attempt times"
+ else
+ echo "Done "
+ fi
+fi
diff --git a/Final/java/broker/bin/qpid.stopall b/Final/java/broker/bin/qpid.stopall
new file mode 100644
index 0000000000..d71f591de8
--- /dev/null
+++ b/Final/java/broker/bin/qpid.stopall
@@ -0,0 +1,74 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# qpid.stopall script
+#
+# Script attempts to stop all PROGRAMs running under the current user
+# Utilises qpid.stop to perform the actual stopping
+#
+
+PROGRAM="DQPID"
+
+#
+# grep ps for instances of $PROGRAM and collect PIDs
+#
+lookup()
+{
+#pids=`ps o pid,command | grep $PROGRAM | grep -v grep | cut -d ' ' -f 1`
+pids=`ps -ef |grep $USER | grep $PROGRAM | grep -v grep | awk '{print $2}'`
+result=`echo -n $pids | wc -w`
+}
+
+
+#
+# Show the PS output for given set of pids
+#
+showPids()
+{
+ps -o user,pid,args -p $pids
+}
+
+
+#
+# Main Run
+#
+
+lookup
+
+if [[ $[$result] == 0 ]] ; then
+ echo "No Qpid Brokers found running under user '$USER'"
+ exit 0
+fi
+
+for pid in $pids ; do
+
+qpid.stop $pid
+
+done
+
+# Check we have quit all
+lookup
+
+if [[ $[$result] == 0 ]] ; then
+ echo "All Qpid brokers successfully quit"
+else
+ echo "Some brokers were not quit"
+ showPids $pids
+fi
diff --git a/Final/java/broker/bin/run.bat b/Final/java/broker/bin/run.bat
new file mode 100755
index 0000000000..5b0aa0f23b
--- /dev/null
+++ b/Final/java/broker/bin/run.bat
@@ -0,0 +1,31 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+set CORE_CLASSES=..\classes;..\testclasses
+
+set COMMONDIR=..\..\common
+
+set COMMONLIB=%COMMONDIR%\lib\slf4j\slf4j-simple.jar;%COMMONDIR%\lib\logging-log4j\log4j-1.2.9.jar;%COMMONDIR%\lib\mina\mina-core-0.9.2.jar
+
+set DIST=..\lib\bdb\je-3.0.12.jar;..\dist\amqpd.jar;..\..\client\dist\amqp-common.jar
+
+set CP=%CORE_CLASSES%;%DIST%;%COMMONLIB%
+
+"%JAVA_HOME%\bin\java" -Xmx512m -Damqj.logging.level=INFO -cp %CP% %*
diff --git a/Final/java/broker/bin/run.sh b/Final/java/broker/bin/run.sh
new file mode 100755
index 0000000000..6b62049e94
--- /dev/null
+++ b/Final/java/broker/bin/run.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+COMMONDIR=../../common
+
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/slf4j/slf4j-simple.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/logging-log4j/log4j-1.2.13.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/mina/mina-core-0.9.5-SNAPSHOT.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/mina/mina-filter-ssl-0.9.5-SNAPSHOT.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-collections/commons-collections-3.1.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-cli/commons-cli-1.0.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-configuration/commons-configuration-1.2.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-logging/commons-logging-api.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-logging/commons-logging.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-lang/commons-lang-2.1.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/junit/junit-4.0.jar
+
+DIST=../lib/bdb/je-3.0.12.jar:../dist/amqpd-tests.jar:../dist/amqpd.jar:../../client/dist/amqp-common.jar
+
+CP=../intellijclasses:$DIST:$COMMONLIB
+
+if [ "$(uname -a | fgrep Cygwin)" != "" ]; then
+ CP=$(cygpath --mixed --path $CP)
+fi
+
+"$JAVA_HOME/bin/java" -Xmx512m -Dprepopulate=10 -Damqj.logging.level=INFO -cp $CP $*
diff --git a/Final/java/broker/bin/runAll b/Final/java/broker/bin/runAll
new file mode 100644
index 0000000000..4ced1d263b
--- /dev/null
+++ b/Final/java/broker/bin/runAll
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+doRun()
+{
+ class=$1
+ shift
+ echo
+ echo ================================================================================
+ echo Running $class
+ ./run.sh $class "$@"
+}
+
+# parameters are: clients messages iterations
+doRun org.apache.qpid.server.queue.SendPerfTest 10 1000 100
+
+doRun org.apache.qpid.server.queue.QueuePerfTest
+
+doRun org.apache.qpid.server.queue.QueueConcurrentPerfTest
diff --git a/Final/java/broker/distribution/pom.xml b/Final/java/broker/distribution/pom.xml
new file mode 100644
index 0000000000..8737d943eb
--- /dev/null
+++ b/Final/java/broker/distribution/pom.xml
@@ -0,0 +1,153 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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-broker-distribution</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Broker Distributions</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <java.source.version>1.5</java.source.version>
+ <qpid.version>${pom.version}</qpid.version>
+ <qpid.targetDir>${project.build.directory}</qpid.targetDir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <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>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>${assembly.version}</version>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/broker-bin.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ <outputDirectory>${qpid.targetDir}</outputDirectory>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <finalName>qpid-incubating</finalName>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/broker-bin.xml</descriptor>
+ <descriptor>src/main/assembly/broker-src.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ </build>
+
+ <profiles>
+ <profile>
+ <id>tests</id>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <type>test-jar</type>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/broker-bin-tests.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/Final/java/broker/distribution/src/main/assembly/broker-bin-tests.xml b/Final/java/broker/distribution/src/main/assembly/broker-bin-tests.xml
new file mode 100644
index 0000000000..fa017d6232
--- /dev/null
+++ b/Final/java/broker/distribution/src/main/assembly/broker-bin-tests.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <id>java-broker-bin-with-tests</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <!-- Apache license files -->
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include easy access to test source-->
+ <fileSet>
+ <directory>../src/test</directory>
+ <outputDirectory>qpid-${qpid.version}/src</outputDirectory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </fileSet>
+
+ <!-- Execution Scripts -->
+ <fileSet>
+ <directory>../bin/</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ <fileMode>777</fileMode> <!-- RWX -->
+ </fileSet>
+
+ <!-- Configuration -->
+ <fileSet>
+ <directory>../etc/</directory>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ <fileMode>420</fileMode>
+ </fileSet>
+
+ <!-- Metadata Jar -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-incubating.jar</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+
+
+ <files>
+ <!-- Common Run scripts -->
+ <file>
+ <source>../../common/bin/qpid-run</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>qpid-run</destName>
+ <fileMode>493</fileMode>
+ </file>
+
+ <!-- Common Configuration -->
+ <file>
+ <source>../../common/etc/qpid-run.conf</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>qpid-run.conf</destName>
+ <fileMode>420</fileMode>
+ </file>
+ </files>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-broker-distribution</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/broker/distribution/src/main/assembly/broker-bin.xml b/Final/java/broker/distribution/src/main/assembly/broker-bin.xml
new file mode 100644
index 0000000000..e66190a3f4
--- /dev/null
+++ b/Final/java/broker/distribution/src/main/assembly/broker-bin.xml
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>java-broker-bin</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <!-- Apache Licensing -->
+ <fileSet>
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>../..</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>../../src/main/release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ <!-- Metadata Jar -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-incubating.jar</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+ <files>
+ <!-- due to a bug in the assembly plugin (MASSEMBLY-153) you have
+ to use decimal numbers to specify fileMode -->
+ <file>
+ <source>../../common/etc/qpid-run.conf</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>qpid-run.conf</destName>
+ <fileMode>420</fileMode>
+ </file>
+ <file>
+ <source>../etc/config.xml</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>config.xml</destName>
+ <fileMode>420</fileMode>
+ </file>
+ <file>
+ <source>../etc/jmxremote.access</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>jmxremote.access</destName>
+ <fileMode>420</fileMode>
+ </file>
+ <file>
+ <source>../etc/log4j.xml</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>log4j.xml</destName>
+ <fileMode>420</fileMode>
+ </file>
+ <file>
+ <source>../etc/passwd</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>passwd</destName>
+ <fileMode>420</fileMode>
+ </file>
+ <file>
+ <source>../etc/qpid-server.conf</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>qpid-server.conf</destName>
+ <fileMode>420</fileMode>
+ </file>
+ <file>
+ <source>../etc/virtualhosts.xml</source>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <destName>virtualhosts.xml</destName>
+ <fileMode>420</fileMode>
+ </file>
+ <file>
+ <source>../../common/bin/qpid-run</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>qpid-run</destName>
+ <fileMode>473</fileMode>
+ </file>
+ <file>
+ <source>../bin/qpid-passwd</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>qpid-passwd</destName>
+ <fileMode>473</fileMode>
+ </file>
+ <file>
+ <source>../bin/qpid-server</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>qpid-server</destName>
+ <fileMode>473</fileMode>
+ </file>
+ <file>
+ <source>../bin/qpid-server.bat</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>qpid-server.bat</destName>
+ <fileMode>473</fileMode>
+ </file>
+ <file>
+ <source>../bin/run.bat</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>run.bat</destName>
+ <fileMode>473</fileMode>
+ </file>
+ <file>
+ <source>../bin/run.sh</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>run.sh</destName>
+ <fileMode>473</fileMode>
+ </file>
+ <file>
+ <source>../bin/runAll</source>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <destName>runAll</destName>
+ <fileMode>473</fileMode>
+ </file>
+ </files>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <!-- This needs to be tidied up QPID-280 -->
+ <excludes>
+ <exclude>org.apache.qpid:qpid-broker-distribution</exclude>
+ <exclude>org.apache.qpid.management:org.apache.qpid.management.ui</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.commands</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.contenttype</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.expressions</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.jobs</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime.compatibility.auth</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime.compatibility.registry</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.common</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.preferences</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.registry</exclude>
+ <exclude>org.eclipse.help:org.eclipse.help</exclude>
+ <exclude>org.eclipse.jface:org.eclipse.jface</exclude>
+ <exclude>org.eclipse.osgi:org.eclipse.osgi</exclude>
+ <exclude>org.eclipse.swt:org.eclipse.swt</exclude>
+ <exclude>org.eclipse.swt:org.eclipse.swt.win32.win32.x86</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui.forms</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui.workbench</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/broker/distribution/src/main/assembly/broker-src.xml b/Final/java/broker/distribution/src/main/assembly/broker-src.xml
new file mode 100644
index 0000000000..28a22c3851
--- /dev/null
+++ b/Final/java/broker/distribution/src/main/assembly/broker-src.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>java-broker-src</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <!-- Apache Licensing -->
+ <fileSet>
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}-src</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>licenses/*.*</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ <include>BUILDING.txt</include>
+ </includes>
+ </fileSet>
+ <!-- Broker source -->
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>qpid-${qpid.version}-src</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ <!-- Tidy up wrt to QPID-280 -->
+ <excludes>
+ <exclude>build.xml</exclude>
+ <exclude>distribution/build.xml</exclude>
+ <exclude>benchmark</exclude>
+ <exclude>benchmark/**/*</exclude>
+ <exclude>**/target</exclude>
+ <exclude>**/target/**/*</exclude>
+ <exclude>**/build</exclude>
+ <exclude>**/build/**/*</exclude>
+ <exclude>**/.settings</exclude>
+ <exclude>**/.classpath</exclude>
+ <exclude>**/.project</exclude>
+ <exclude>**/.wtpmodules</exclude>
+ <exclude>**/surefire*</exclude>
+ <exclude>**/cobertura.ser</exclude>
+ <exclude>bin</exclude>
+ <exclude>bin/*</exclude>
+ <exclude>lib</exclude>
+ <exclude>lib/**/*</exclude>
+ <exclude>**/var/journal</exclude>
+ <exclude>**/build.out*</exclude>
+ <exclude>**/eclipse-plugin/bin/**</exclude>
+ <exclude>**/eclipse-plugin/plugins/**</exclude>
+ <exclude>**/eclipse-plugin/src/main/resources/**</exclude>
+ </excludes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/Final/java/broker/etc/access b/Final/java/broker/etc/access
new file mode 100644
index 0000000000..58b7443fa9
--- /dev/null
+++ b/Final/java/broker/etc/access
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+guest:localhost(rw),test(rw) \ No newline at end of file
diff --git a/Final/java/broker/etc/config.xml b/Final/java/broker/etc/config.xml
new file mode 100644
index 0000000000..b5b81bbeb0
--- /dev/null
+++ b/Final/java/broker/etc/config.xml
@@ -0,0 +1,175 @@
+<?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>true</qpidnio>
+ <transport>nio</transport>
+ <port>5672</port>
+ <sslport>8672</sslport>
+ <socketReceiveBuffer>32768</socketReceiveBuffer>
+ <socketSendBuffer>32768</socketSendBuffer>
+ </connector>
+ <management>
+ <enabled>true</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>
+ </advanced>
+
+ <security>
+ <principal-databases>
+ <principal-database>
+ <name>passwordfile</name>
+ <class>org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase</class>
+ <attributes>
+ <attribute>
+ <name>passwordFile</name>
+ <value>${conf}/passwdVhost</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>
+ </access>
+ <jmx>
+ <access>${conf}/jmxremote.access</access>
+ <principal-database>passwordfile</principal-database>
+ </jmx>
+ </security>
+
+ <virtualhosts>
+ <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>
+ </localhost>
+ </virtualhost>
+
+ <virtualhost>
+ <name>development</name>
+ <development>
+ <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>
+
+ <virtualhost>
+ <name>test</name>
+ <test>
+ <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>
+
+ </virtualhosts>
+ <heartbeat>
+ <delay>0</delay>
+ <timeoutFactor>2.0</timeoutFactor>
+ </heartbeat>
+ <queue>
+ <auto_register>true</auto_register>
+ </queue>
+
+ <virtualhosts>${conf}/virtualhosts.xml</virtualhosts>
+</broker>
+
+
diff --git a/Final/java/broker/etc/debug.log4j.xml b/Final/java/broker/etc/debug.log4j.xml
new file mode 100644
index 0000000000..e8fd7e119d
--- /dev/null
+++ b/Final/java/broker/etc/debug.log4j.xml
@@ -0,0 +1,114 @@
+<?xml version="1.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.
+ -
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="ArchivingFileAppender" class="org.apache.log4j.QpidCompositeRollingAppender">
+ <!-- Ensure that logs allways have the dateFormat set-->
+ <param name="StaticLogFileName" value="false"/>
+ <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
+ <param name="Append" value="false"/>
+ <!-- Change the direction so newer files have bigger numbers -->
+ <!-- So log.1 is written then log.2 etc This prevents a lot of file renames at log rollover -->
+ <param name="CountDirection" value="1"/>
+ <!-- Use default 10MB -->
+ <!--param name="MaxFileSize" value="100000"/-->
+ <param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm"/>
+ <!-- Unlimited number of backups -->
+ <param name="MaxSizeRollBackups" value="-1"/>
+ <!-- Compress(gzip) the backup files-->
+ <param name="CompressBackupFiles" value="true"/>
+ <!-- Compress the backup files using a second thread -->
+ <param name="CompressAsync" value="true"/>
+ <!-- Start at zero numbered files-->
+ <param name="ZeroBased" value="true"/>
+ <!-- Backup Location -->
+ <param name="backupFilesToPath" value="${QPID_WORK}/backup/log"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="FileAppender" class="org.apache.log4j.FileAppender">
+ <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
+ <param name="Append" value="false"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="AlertFile" class="org.apache.log4j.FileAppender">
+ <param name="File" value="${QPID_WORK}/log/alert.log"/>
+ <param name="Append" value="false"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <category name="Qpid.Broker">
+ <priority value="debug"/>
+ <appender-ref ref="AlertFile"/>
+ <!--appender-ref ref="STDOUT"/-->
+ </category>
+
+
+ <category name="org.apache.qpid.server.queue.AMQQueueMBean">
+ <priority value="info"/>
+ <appender-ref ref="AlertFile"/>
+ </category>
+
+
+ <!-- Provide warnings to standard output -->
+ <!--category name="org.apache.qpid">
+ <priority value="warn"/>
+ <appender-ref ref="STDOUT"/>
+ </category-->
+
+
+ <!-- Additional level settings for debugging -->
+ <!-- Each class in the Broker is a category that can have its logging level adjusted. -->
+ <!-- This will provide more details if available about that classes processing. -->
+ <!--category name="org.apache.qpid.server.txn">
+ <priority value="debug"/>
+ </category>-->
+
+ <!--<category name="org.apache.qpid.server.store">
+ <priority value="debug"/>
+ </category-->
+
+ <!-- Log all info events to file -->
+ <root>
+ <priority value="info"/>
+ <appender-ref ref="STDOUT"/>
+ <appender-ref ref="FileAppender"/>
+ </root>
+
+</log4j:configuration>
diff --git a/Final/java/broker/etc/jmxremote.access b/Final/java/broker/etc/jmxremote.access
new file mode 100644
index 0000000000..1a51a6991b
--- /dev/null
+++ b/Final/java/broker/etc/jmxremote.access
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#Generated by JMX Console : Last edited by user:admin
+#Tue Jun 12 16:46:39 BST 2007
+admin=admin
+guest=readonly
+user=readwrite
diff --git a/Final/java/broker/etc/log4j.xml b/Final/java/broker/etc/log4j.xml
new file mode 100644
index 0000000000..af8e7a8293
--- /dev/null
+++ b/Final/java/broker/etc/log4j.xml
@@ -0,0 +1,112 @@
+<?xml version="1.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.
+ -
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="ArchivingFileAppender" class="org.apache.log4j.QpidCompositeRollingAppender">
+ <!-- Ensure that logs allways have the dateFormat set-->
+ <param name="StaticLogFileName" value="false"/>
+ <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
+ <param name="Append" value="false"/>
+ <!-- Change the direction so newer files have bigger numbers -->
+ <!-- So log.1 is written then log.2 etc This prevents a lot of file renames at log rollover -->
+ <param name="CountDirection" value="1"/>
+ <!-- Use default 10MB -->
+ <!--param name="MaxFileSize" value="100000"/-->
+ <param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm"/>
+ <!-- Unlimited number of backups -->
+ <param name="MaxSizeRollBackups" value="-1"/>
+ <!-- Compress(gzip) the backup files-->
+ <param name="CompressBackupFiles" value="true"/>
+ <!-- Compress the backup files using a second thread -->
+ <param name="CompressAsync" value="true"/>
+ <!-- Start at zero numbered files-->
+ <param name="ZeroBased" value="true"/>
+ <!-- Backup Location -->
+ <param name="backupFilesToPath" value="${QPID_WORK}/backup/log"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="FileAppender" class="org.apache.log4j.FileAppender">
+ <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
+ <param name="Append" value="false"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="AlertFile" class="org.apache.log4j.FileAppender">
+ <param name="File" value="${QPID_WORK}/log/alert.log"/>
+ <param name="Append" value="false"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <category name="Qpid.Broker">
+ <priority value="debug"/>
+ <appender-ref ref="AlertFile"/>
+ <appender-ref ref="STDOUT"/>
+ </category>
+
+ <category name="org.apache.qpid.server.queue.AMQQueueMBean">
+ <priority value="info"/>
+ <appender-ref ref="AlertFile"/>
+ </category>
+
+ <!-- Provide warnings to standard output -->
+ <category name="org.apache.qpid">
+ <priority value="warn"/>
+ <appender-ref ref="STDOUT"/>
+ </category>
+
+
+ <!-- Examples of additional logging settings -->
+ <!-- Used to generate extra debug. See debug.log4j.xml -->
+
+ <!--<category name="org.apache.qpid.server.store">
+ <priority value="debug"/>
+ </category-->
+
+ <!--category name="org.apache.qpid.server.txn">
+ <priority value="debug"/>
+ </category>-->
+
+ <!-- Log all info events to file -->
+ <root>
+ <priority value="info"/>
+ <appender-ref ref="FileAppender"/>
+ <!--appender-ref ref="ArchivingFileAppender"/-->
+ </root>
+
+</log4j:configuration>
diff --git a/Final/java/broker/etc/md5passwd b/Final/java/broker/etc/md5passwd
new file mode 100644
index 0000000000..6a149919de
--- /dev/null
+++ b/Final/java/broker/etc/md5passwd
@@ -0,0 +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
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+guest:CE4DQ6BIb/BVMN9scFyLtA==
+admin:ISMvKXpXpadDiUoOSoAfww==
+user:aBzonUodYLhwSa8s9A10sA==
diff --git a/Final/java/broker/etc/mstool-log4j.xml b/Final/java/broker/etc/mstool-log4j.xml
new file mode 100644
index 0000000000..8c46010e2d
--- /dev/null
+++ b/Final/java/broker/etc/mstool-log4j.xml
@@ -0,0 +1,54 @@
+<?xml version="1.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.
+ -
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+ <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <!--param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/-->
+ <param name="ConversionPattern" value="%d %-5p [%t] (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <category name="org.apache.qpid.tools">
+ <priority value="info"/>
+ </category>
+
+ <category name="org.apache.qpid">
+ <priority value="error"/>
+ </category>
+
+ <category name="org.apache.qpid.server.security">
+ <priority value="error"/>
+ </category>
+
+ <category name="org.apache.qpid.server.management">
+ <priority value="error"/>
+ </category>
+
+
+ <root>
+ <priority value="info"/>
+ <appender-ref ref="STDOUT"/>
+ </root>
+</log4j:configuration>
diff --git a/Final/java/broker/etc/passwd b/Final/java/broker/etc/passwd
new file mode 100644
index 0000000000..966a16153d
--- /dev/null
+++ b/Final/java/broker/etc/passwd
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+guest:guest
diff --git a/Final/java/broker/etc/passwdVhost b/Final/java/broker/etc/passwdVhost
new file mode 100644
index 0000000000..48ce8299b6
--- /dev/null
+++ b/Final/java/broker/etc/passwdVhost
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+guest:guest:localhost,test
diff --git a/Final/java/broker/etc/persistent_config.xml b/Final/java/broker/etc/persistent_config.xml
new file mode 100644
index 0000000000..178a73515c
--- /dev/null
+++ b/Final/java/broker/etc/persistent_config.xml
@@ -0,0 +1,132 @@
+<?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.
+ -
+
+ This is an example config using the BDBMessageStore available from
+ the Red Hat Messaging project at etp.108.redhat.com and distributed under GPL.
+ -->
+
+<broker>
+ <prefix>${QPID_HOME}</prefix>
+ <work>${QPID_WORK}</work>
+ <conf>${prefix}/etc</conf>
+ <connector>
+ <qpidnio>true</qpidnio>
+ <transport>nio</transport>
+ <port>5672</port>
+ <sslport>8672</sslport>
+ <socketReceiveBuffer>32768</socketReceiveBuffer>
+ <socketSendBuffer>32768</socketSendBuffer>
+ </connector>
+ <management>
+ <enabled>true</enabled>
+ <jmxport>8999</jmxport>
+ </management>
+ <advanced>
+ <filterchain enableExecutorPool="true"/>
+ <enablePooledAllocator>false</enablePooledAllocator>
+ <enableDirectBuffers>false</enableDirectBuffers>
+ <framesize>65535</framesize>
+ <compressBufferOnQueue>false</compressBufferOnQueue>
+ </advanced>
+
+ <security>
+ <principal-databases>
+ <principal-database>
+ <name>passwordfile</name>
+ <class>org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase</class>
+ <attributes>
+ <attribute>
+ <name>passwordFile</name>
+ <value>${conf}/passwdVhost</value>
+ </attribute>
+ </attributes>
+ </principal-database>
+ </principal-databases>
+
+ <access>
+ <class>org.apache.qpid.server.security.access.AllowAll</class>
+ </access>
+ <jmx>
+ <access>${conf}/jmxremote.access</access>
+ <principal-database>passwordfile</principal-database>
+ </jmx>
+ </security>
+
+ <virtualhosts>
+ <virtualhost>
+ <name>localhost</name>
+ <localhost>
+ <store>
+ <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>
+
+ <virtualhost>
+ <name>development</name>
+ <development>
+ <store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
+ <environment-path>${work}/bdbstore/development-store</environment-path>
+ </store>
+ </development>
+ </virtualhost>
+
+ <virtualhost>
+ <name>test</name>
+ <test>
+ <store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
+ <environment-path>${work}/bdbstore/test-store</environment-path>
+ </store>
+ </test>
+ </virtualhost>
+
+ </virtualhosts>
+ <heartbeat>
+ <delay>0</delay>
+ <timeoutFactor>2.0</timeoutFactor>
+ </heartbeat>
+ <queue>
+ <auto_register>true</auto_register>
+ </queue>
+
+ <virtualhosts>${conf}/virtualhosts.xml</virtualhosts>
+</broker>
+
+
diff --git a/Final/java/broker/etc/qpid-server.conf b/Final/java/broker/etc/qpid-server.conf
new file mode 100644
index 0000000000..c310094817
--- /dev/null
+++ b/Final/java/broker/etc/qpid-server.conf
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+QPID_LIBS=$QPID_HOME/lib/qpid-incubating.jar:$QPID_HOME/lib/bdbstore-launch.jar
+
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ CLASSPATH=$QPID_LIBS
diff --git a/Final/java/broker/etc/qpid-server.conf.jpp b/Final/java/broker/etc/qpid-server.conf.jpp
new file mode 100644
index 0000000000..3ed2431ef3
--- /dev/null
+++ b/Final/java/broker/etc/qpid-server.conf.jpp
@@ -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.
+#
+
+QPID_LIBS=$(build-classpath backport-util-concurrent \
+ commons-beanutils \
+ commons-beanutils-core \
+ commons-cli \
+ commons-codec \
+ commons-collections \
+ commons-configuration \
+ commons-digester \
+ commons-lang \
+ commons-logging \
+ commons-logging-api \
+ dom4j \
+ geronimo-jms-1.1-api \
+ isorelax \
+ jaxen \
+ log4j \
+ mina/core \
+ mina/filter-ssl \
+ mina/java5 \
+ msv-msv \
+ qpid-broker \
+ qpid-client \
+ qpid-common \
+ relaxngDatatype \
+ slf4j)
+
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ CLASSPATH=$QPID_LIBS
diff --git a/Final/java/broker/etc/qpid.passwd b/Final/java/broker/etc/qpid.passwd
new file mode 100644
index 0000000000..79b5e11777
--- /dev/null
+++ b/Final/java/broker/etc/qpid.passwd
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+guest:CE4DQ6BIb/BVMN9scFyLtA==
+admin:ISMvKXpXpadDiUoOSoAfww==
+user:CE4DQ6BIb/BVMN9scFyLtA==
+
diff --git a/Final/java/broker/etc/transient_config.xml b/Final/java/broker/etc/transient_config.xml
new file mode 100644
index 0000000000..164d66cd1b
--- /dev/null
+++ b/Final/java/broker/etc/transient_config.xml
@@ -0,0 +1,128 @@
+<?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.
+ -
+
+ This is an example config file that uses the MemoryMessageStore.
+ As a result it is aimed at brokers sending transient messages.
+
+ -->
+<broker>
+ <prefix>${QPID_HOME}</prefix>
+ <work>${QPID_WORK}</work>
+ <conf>${prefix}/etc</conf>
+ <connector>
+ <qpidnio>true</qpidnio>
+ <transport>nio</transport>
+ <port>5672</port>
+ <sslport>8672</sslport>
+ <socketReceiveBuffer>32768</socketReceiveBuffer>
+ <socketSendBuffer>32768</socketSendBuffer>
+ </connector>
+ <management>
+ <enabled>true</enabled>
+ <jmxport>8999</jmxport>
+ </management>
+ <advanced>
+ <filterchain enableExecutorPool="true"/>
+ <enablePooledAllocator>false</enablePooledAllocator>
+ <enableDirectBuffers>false</enableDirectBuffers>
+ <framesize>65535</framesize>
+ <compressBufferOnQueue>false</compressBufferOnQueue>
+ </advanced>
+
+ <security>
+ <principal-databases>
+ <principal-database>
+ <name>passwordfile</name>
+ <class>org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase</class>
+ <attributes>
+ <attribute>
+ <name>passwordFile</name>
+ <value>${conf}/passwdVhost</value>
+ </attribute>
+ </attributes>
+ </principal-database>
+ </principal-databases>
+ <access>
+ <class>org.apache.qpid.server.security.access.AllowAll</class>
+ </access>
+ <jmx>
+ <access>${conf}/jmxremote.access</access>
+ <principal-database>passwordfile</principal-database>
+ </jmx>
+ </security>
+
+ <virtualhosts>
+ <virtualhost>
+ <name>localhost</name>
+ <localhost>
+ <store>
+ <class>org.apache.qpid.server.store.MemoryMessageStore</class>
+ </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>
+
+ <virtualhost>
+ <name>development</name>
+ <development>
+ <store>
+ <class>org.apache.qpid.server.store.MemoryMessageStore</class>
+ </store>
+ </development>
+ </virtualhost>
+
+ <virtualhost>
+ <name>test</name>
+ <test>
+ <store>
+ <class>org.apache.qpid.server.store.MemoryMessageStore</class>
+ </store>
+ </test>
+ </virtualhost>
+
+ </virtualhosts>
+ <heartbeat>
+ <delay>0</delay>
+ <timeoutFactor>2.0</timeoutFactor>
+ </heartbeat>
+ <queue>
+ <auto_register>true</auto_register>
+ </queue>
+
+ <virtualhosts>${conf}/virtualhosts.xml</virtualhosts>
+</broker>
+
+
diff --git a/Final/java/broker/etc/virtualhosts.xml b/Final/java/broker/etc/virtualhosts.xml
new file mode 100644
index 0000000000..f62ec3f5d7
--- /dev/null
+++ b/Final/java/broker/etc/virtualhosts.xml
@@ -0,0 +1,123 @@
+<?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.
+ -
+ -->
+<virtualhosts>
+ <default>test</default>
+ <virtualhost>
+ <name>localhost</name>
+ <localhost>
+ <exchanges>
+ <exchange>
+ <type>direct</type>
+ <name>test.direct</name>
+ <durable>true</durable>
+ </exchange>
+ <exchange>
+ <type>topic</type>
+ <name>test.topic</name>
+ </exchange>
+ </exchanges>
+ <queues>
+ <exchange>amq.direct</exchange>
+ <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb -->
+ <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb -->
+ <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins -->
+
+ <queue>
+ <name>queue</name>
+ </queue>
+ <queue>
+ <name>ping</name>
+ </queue>
+ <queue>
+ <name>test-queue</name>
+ <test-queue>
+ <exchange>test.direct</exchange>
+ <durable>true</durable>
+ </test-queue>
+ </queue>
+ <queue>
+ <name>test-ping</name>
+ <test-ping>
+ <exchange>test.direct</exchange>
+ </test-ping>
+ </queue>
+
+ </queues>
+ </localhost>
+ </virtualhost>
+
+
+ <virtualhost>
+ <name>development</name>
+ <development>
+ <queues>
+ <minimumAlertRepeatGap>30000</minimumAlertRepeatGap>
+ <maximumMessageCount>5000</maximumMessageCount>
+ <queue>
+ <name>queue</name>
+ <queue>
+ <exchange>amq.direct</exchange>
+ <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb -->
+ <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb -->
+ <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins -->
+ </queue>
+ </queue>
+ <queue>
+ <name>ping</name>
+ <ping>
+ <exchange>amq.direct</exchange>
+ <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb -->
+ <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb -->
+ <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins -->
+ </ping>
+ </queue>
+ </queues>
+ </development>
+ </virtualhost>
+ <virtualhost>
+ <name>test</name>
+ <test>
+ <queues>
+ <minimumAlertRepeatGap>30000</minimumAlertRepeatGap>
+ <maximumMessageCount>5000</maximumMessageCount>
+ <queue>
+ <name>queue</name>
+ <queue>
+ <exchange>amq.direct</exchange>
+ <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb -->
+ <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb -->
+ <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins -->
+ </queue>
+ </queue>
+ <queue>
+ <name>ping</name>
+ <ping>
+ <exchange>amq.direct</exchange>
+ <maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb -->
+ <maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb -->
+ <maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins -->
+ </ping>
+ </queue>
+ </queues>
+ </test>
+ </virtualhost>
+</virtualhosts>
diff --git a/Final/java/broker/pom.xml b/Final/java/broker/pom.xml
new file mode 100644
index 0000000000..914a272571
--- /dev/null
+++ b/Final/java/broker/pom.xml
@@ -0,0 +1,256 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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-broker</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Broker</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-configuration</groupId>
+ <artifactId>commons-configuration</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-beanutils</groupId>
+ <artifactId>commons-beanutils</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-beanutils</groupId>
+ <artifactId>commons-beanutils-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-digester</groupId>
+ <artifactId>commons-digester</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>xerces</groupId>
+ <artifactId>xercesImpl</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+
+ <!-- Test Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+
+ <plugins>
+
+
+ <!--plugin>
+ <artifactId>minijar-maven-plugin</artifactId>
+ <groupId>org.codehaus.mojo</groupId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>minijars</goal>
+ </goals>
+ <configuration>
+ <stripUnusedClasses>true</stripUnusedClasses>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin-->
+
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>javacc-maven-plugin</artifactId>
+ <version>2.0</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <configuration>
+ <sourceDirectory>${basedir}/src/main/grammar</sourceDirectory>
+ <outputDirectory>${basedir}/target/generated-sources</outputDirectory>
+ <packageName>org.apache.qpid.server.filter.jms.selector</packageName>
+ </configuration>
+ <goals>
+ <goal>javacc</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemProperties>
+ <property>
+ <name>amqj.noAutoCreateVMBroker</name>
+ <value>true</value>
+ </property>
+ <property>
+ <name>amqj.logging.level</name>
+ <value>${amqj.logging.level}</value>
+ </property>
+ <property>
+ <name>log4j.configuration</name>
+ <value>file:///${basedir}/src/main/java/log4j.properties</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <testResources>
+ <testResource>
+ <targetPath>src/</targetPath>
+ <filtering>false</filtering>
+ <directory>src/test/java</directory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </testResource>
+ </testResources>
+
+ <pluginManagement>
+ <plugins>
+ <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>python_test</id>
+ <phase>test</phase>
+ <configuration>
+ <tasks>
+
+ <condition property="skip-python-tests" value="true">
+ <isset property="skip.python.tests"/>
+ </condition>
+
+ <property name="command"
+ value="python run-tests -v -I java_failing.txt -b localhost:2000"/>
+ <!--value="bash -c 'python run-tests -v -I java_failing.txt'"/>-->
+
+ <ant antfile="python-test.xml" inheritRefs="true">
+ <target name="run-tests" />
+ </ant>
+
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+
+ </build>
+
+</project>
diff --git a/Final/java/broker/python-test.xml b/Final/java/broker/python-test.xml
new file mode 100755
index 0000000000..e044207948
--- /dev/null
+++ b/Final/java/broker/python-test.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<!-- ====================================================================== -->
+<!-- Ant build file (http://ant.apache.org/) for Ant 1.6.2 or above. -->
+<!-- ====================================================================== -->
+
+<project basedir="." default="default">
+
+ <target name="default" >
+ <echo message="Used via maven to run python tests."/>
+ </target>
+
+ <dirname property="broker.dir" file="${ant.file.python-test}"/>
+
+ <property name="pythondir" value="${broker.dir}/../../python"/>
+
+ <target name="run-tests" unless="skip-python-tests">
+
+ <echo message="Starting Broker with command"/>
+
+ <java classname="org.apache.qpid.server.RunBrokerWithCommand"
+ fork="true"
+ dir="${pythondir}"
+ failonerror="true"
+ >
+ <arg value="${command}"/>
+ <arg value="-p"/>
+ <arg value="2000"/>
+ <arg value="-m"/>
+ <arg value="2001"/>
+
+ <classpath refid="maven.test.classpath"/>
+ <sysproperty key="QPID_HOME" value="${broker.dir}"/>
+ <sysproperty key="QPID_WORK" value="${broker.dir}${file.separator}target"/>
+ </java>
+
+ </target>
+</project>
diff --git a/Final/java/broker/src/main/grammar/SelectorParser.jj b/Final/java/broker/src/main/grammar/SelectorParser.jj
new file mode 100644
index 0000000000..adec1b348d
--- /dev/null
+++ b/Final/java/broker/src/main/grammar/SelectorParser.jj
@@ -0,0 +1,604 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+ //
+ // Original File from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+ //
+
+// ----------------------------------------------------------------------------
+// OPTIONS
+// ----------------------------------------------------------------------------
+options {
+ STATIC = false;
+ UNICODE_INPUT = true;
+
+ // some performance optimizations
+ OPTIMIZE_TOKEN_MANAGER = true;
+ ERROR_REPORTING = false;
+}
+
+// ----------------------------------------------------------------------------
+// PARSER
+// ----------------------------------------------------------------------------
+
+PARSER_BEGIN(SelectorParser)
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter.jms.selector;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+import org.apache.qpid.AMQInvalidArgumentException;
+import org.apache.qpid.server.filter.ArithmeticExpression;
+import org.apache.qpid.server.filter.BooleanExpression;
+import org.apache.qpid.server.filter.ComparisonExpression;
+import org.apache.qpid.server.filter.ConstantExpression;
+import org.apache.qpid.server.filter.Expression;
+import org.apache.qpid.server.filter.LogicExpression;
+import org.apache.qpid.server.filter.PropertyExpression;
+import org.apache.qpid.server.filter.UnaryExpression;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ *
+ * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj
+ */
+public class SelectorParser {
+
+ public SelectorParser() {
+ this(new StringReader(""));
+ }
+
+ public BooleanExpression parse(String sql) throws AMQInvalidArgumentException {
+ this.ReInit(new StringReader(sql));
+
+ try {
+ return this.JmsSelector();
+ }
+ catch (Throwable e) {
+ throw (AMQInvalidArgumentException)new AMQInvalidArgumentException(sql).initCause(e);
+ }
+
+ }
+
+ private BooleanExpression asBooleanExpression(Expression value) throws ParseException {
+ if (value instanceof BooleanExpression) {
+ return (BooleanExpression) value;
+ }
+ if (value instanceof PropertyExpression) {
+ return UnaryExpression.createBooleanCast( value );
+ }
+ throw new ParseException("Expression will not result in a boolean value: " + value);
+ }
+
+
+}
+
+PARSER_END(SelectorParser)
+
+// ----------------------------------------------------------------------------
+// Tokens
+// ----------------------------------------------------------------------------
+
+/* White Space */
+SPECIAL_TOKEN :
+{
+ " " | "\t" | "\n" | "\r" | "\f"
+}
+
+/* Comments */
+SKIP:
+{
+ <LINE_COMMENT: "--" (~["\n","\r"])* ("\n"|"\r"|"\r\n") >
+}
+
+SKIP:
+{
+ <BLOCK_COMMENT: "/*" (~["*"])* "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/">
+}
+
+/* Reserved Words */
+TOKEN [IGNORE_CASE] :
+{
+ < NOT : "NOT">
+ | < AND : "AND">
+ | < OR : "OR">
+ | < BETWEEN : "BETWEEN">
+ | < LIKE : "LIKE">
+ | < ESCAPE : "ESCAPE">
+ | < IN : "IN">
+ | < IS : "IS">
+ | < TRUE : "TRUE" >
+ | < FALSE : "FALSE" >
+ | < NULL : "NULL" >
+ | < XPATH : "XPATH" >
+ | < XQUERY : "XQUERY" >
+}
+
+/* Literals */
+TOKEN [IGNORE_CASE] :
+{
+
+ < DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* (["l","L"])? >
+ | < HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
+ | < OCTAL_LITERAL: "0" (["0"-"7"])* >
+ | < FLOATING_POINT_LITERAL:
+ (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? // matches: 5.5 or 5. or 5.5E10 or 5.E10
+ | "." (["0"-"9"])+ (<EXPONENT>)? // matches: .5 or .5E10
+ | (["0"-"9"])+ <EXPONENT> // matches: 5E10
+ >
+ | < #EXPONENT: "E" (["+","-"])? (["0"-"9"])+ >
+ | < STRING_LITERAL: "'" ( ("''") | ~["'"] )* "'" >
+}
+
+TOKEN [IGNORE_CASE] :
+{
+ < ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* >
+}
+
+// ----------------------------------------------------------------------------
+// Grammer
+// ----------------------------------------------------------------------------
+BooleanExpression JmsSelector() :
+{
+ Expression left=null;
+}
+{
+ (
+ left = orExpression()
+ )
+ {
+ return asBooleanExpression(left);
+ }
+
+}
+
+Expression orExpression() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ (
+ left = andExpression()
+ (
+ <OR> right = andExpression()
+ {
+ left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ )*
+ )
+ {
+ return left;
+ }
+
+}
+
+
+Expression andExpression() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ (
+ left = equalityExpression()
+ (
+ <AND> right = equalityExpression()
+ {
+ left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ )*
+ )
+ {
+ return left;
+ }
+}
+
+Expression equalityExpression() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ (
+ left = comparisonExpression()
+ (
+
+ "=" right = comparisonExpression()
+ {
+ left = ComparisonExpression.createEqual(left, right);
+ }
+ |
+ "<>" right = comparisonExpression()
+ {
+ left = ComparisonExpression.createNotEqual(left, right);
+ }
+ |
+ LOOKAHEAD(2)
+ <IS> <NULL>
+ {
+ left = ComparisonExpression.createIsNull(left);
+ }
+ |
+ <IS> <NOT> <NULL>
+ {
+ left = ComparisonExpression.createIsNotNull(left);
+ }
+ )*
+ )
+ {
+ return left;
+ }
+}
+
+Expression comparisonExpression() :
+{
+ Expression left;
+ Expression right;
+ Expression low;
+ Expression high;
+ String t, u;
+ boolean not;
+ ArrayList list;
+}
+{
+ (
+ left = addExpression()
+ (
+
+ ">" right = addExpression()
+ {
+ left = ComparisonExpression.createGreaterThan(left, right);
+ }
+ |
+ ">=" right = addExpression()
+ {
+ left = ComparisonExpression.createGreaterThanEqual(left, right);
+ }
+ |
+ "<" right = addExpression()
+ {
+ left = ComparisonExpression.createLessThan(left, right);
+ }
+ |
+ "<=" right = addExpression()
+ {
+ left = ComparisonExpression.createLessThanEqual(left, right);
+ }
+ |
+ {
+ u=null;
+ }
+ <LIKE> t = stringLitteral()
+ [ <ESCAPE> u = stringLitteral() ]
+ {
+ left = ComparisonExpression.createLike(left, t, u);
+ }
+ |
+ LOOKAHEAD(2)
+ {
+ u=null;
+ }
+ <NOT> <LIKE> t = stringLitteral() [ <ESCAPE> u = stringLitteral() ]
+ {
+ left = ComparisonExpression.createNotLike(left, t, u);
+ }
+ |
+ <BETWEEN> low = addExpression() <AND> high = addExpression()
+ {
+ left = ComparisonExpression.createBetween(left, low, high);
+ }
+ |
+ LOOKAHEAD(2)
+ <NOT> <BETWEEN> low = addExpression() <AND> high = addExpression()
+ {
+ left = ComparisonExpression.createNotBetween(left, low, high);
+ }
+ |
+ <IN>
+ "("
+ t = stringLitteral()
+ {
+ list = new ArrayList();
+ list.add( t );
+ }
+ (
+ ","
+ t = stringLitteral()
+ {
+ list.add( t );
+ }
+
+ )*
+ ")"
+ {
+ left = ComparisonExpression.createInFilter(left, list);
+ }
+ |
+ LOOKAHEAD(2)
+ <NOT> <IN>
+ "("
+ t = stringLitteral()
+ {
+ list = new ArrayList();
+ list.add( t );
+ }
+ (
+ ","
+ t = stringLitteral()
+ {
+ list.add( t );
+ }
+
+ )*
+ ")"
+ {
+ left = ComparisonExpression.createNotInFilter(left, list);
+ }
+
+ )*
+ )
+ {
+ return left;
+ }
+}
+
+Expression addExpression() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ left = multExpr()
+ (
+ LOOKAHEAD( ("+"|"-") multExpr())
+ (
+ "+" right = multExpr()
+ {
+ left = ArithmeticExpression.createPlus(left, right);
+ }
+ |
+ "-" right = multExpr()
+ {
+ left = ArithmeticExpression.createMinus(left, right);
+ }
+ )
+
+ )*
+ {
+ return left;
+ }
+}
+
+Expression multExpr() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ left = unaryExpr()
+ (
+ "*" right = unaryExpr()
+ {
+ left = ArithmeticExpression.createMultiply(left, right);
+ }
+ |
+ "/" right = unaryExpr()
+ {
+ left = ArithmeticExpression.createDivide(left, right);
+ }
+ |
+ "%" right = unaryExpr()
+ {
+ left = ArithmeticExpression.createMod(left, right);
+ }
+
+ )*
+ {
+ return left;
+ }
+}
+
+
+Expression unaryExpr() :
+{
+ String s=null;
+ Expression left=null;
+}
+{
+ (
+ LOOKAHEAD( "+" unaryExpr() )
+ "+" left=unaryExpr()
+ |
+ "-" left=unaryExpr()
+ {
+ left = UnaryExpression.createNegate(left);
+ }
+ |
+ <NOT> left=unaryExpr()
+ {
+ left = UnaryExpression.createNOT( asBooleanExpression(left) );
+ }
+ |
+ <XPATH> s=stringLitteral()
+ {
+ left = UnaryExpression.createXPath( s );
+ }
+ |
+ <XQUERY> s=stringLitteral()
+ {
+ left = UnaryExpression.createXQuery( s );
+ }
+ |
+ left = primaryExpr()
+ )
+ {
+ return left;
+ }
+
+}
+
+Expression primaryExpr() :
+{
+ Expression left=null;
+}
+{
+ (
+ left = literal()
+ |
+ left = variable()
+ |
+ "(" left = orExpression() ")"
+ )
+ {
+ return left;
+ }
+}
+
+
+
+ConstantExpression literal() :
+{
+ Token t;
+ String s;
+ ConstantExpression left=null;
+}
+{
+ (
+ (
+ s = stringLitteral()
+ {
+ left = new ConstantExpression(s);
+ }
+ )
+ |
+ (
+ t = <DECIMAL_LITERAL>
+ {
+ left = ConstantExpression.createFromDecimal(t.image);
+ }
+ )
+ |
+ (
+ t = <HEX_LITERAL>
+ {
+ left = ConstantExpression.createFromHex(t.image);
+ }
+ )
+ |
+ (
+ t = <OCTAL_LITERAL>
+ {
+ left = ConstantExpression.createFromOctal(t.image);
+ }
+ )
+ |
+ (
+ t = <FLOATING_POINT_LITERAL>
+ {
+ left = ConstantExpression.createFloat(t.image);
+ }
+ )
+ |
+ (
+ <TRUE>
+ {
+ left = ConstantExpression.TRUE;
+ }
+ )
+ |
+ (
+ <FALSE>
+ {
+ left = ConstantExpression.FALSE;
+ }
+ )
+ |
+ (
+ <NULL>
+ {
+ left = ConstantExpression.NULL;
+ }
+ )
+ )
+ {
+ return left;
+ }
+}
+
+String stringLitteral() :
+{
+ Token t;
+ StringBuffer rc = new StringBuffer();
+ boolean first=true;
+}
+{
+ t = <STRING_LITERAL>
+ {
+ // 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 rc.toString();
+ }
+}
+
+PropertyExpression variable() :
+{
+ Token t;
+ PropertyExpression left=null;
+}
+{
+ (
+ t = <ID>
+ {
+ left = new PropertyExpression(t.image);
+ }
+ )
+ {
+ return left;
+ }
+}
diff --git a/Final/java/broker/src/main/java/log4j.properties b/Final/java/broker/src/main/java/log4j.properties
new file mode 100644
index 0000000000..87f04f4991
--- /dev/null
+++ b/Final/java/broker/src/main/java/log4j.properties
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootCategory=${amqj.logging.level}, console
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.Threshold=info
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%t %d %p [%c{4}] %m%n
diff --git a/Final/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java b/Final/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java
new file mode 100644
index 0000000000..7e0c4defe1
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java
@@ -0,0 +1,1007 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.log4j;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.log4j.helpers.CountingQuietWriter;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * <p>CompositeRollingAppender combines RollingFileAppender and DailyRollingFileAppender<br> It can function as either
+ * or do both at the same time (making size based rolling files like RollingFileAppender until a data/time boundary is
+ * crossed at which time it rolls all of those files as per the DailyRollingFileAppender) based on the setting for
+ * <code>rollingStyle</code>.<br> <br> To use CompositeRollingAppender to roll log files as they reach a certain size
+ * (like RollingFileAppender), set rollingStyle=1 (@see config.size)<br> To use CompositeRollingAppender to roll log
+ * files at certain time intervals (daily for example), set rollingStyle=2 and a datePattern (@see config.time)<br> To
+ * have CompositeRollingAppender roll log files at a certain size AND rename those according to time intervals, set
+ * rollingStyle=3 (@see config.composite)<br>
+ *
+ * <p>A of few additional optional features have been added:<br> -- Attach date pattern for current log file (@see
+ * staticLogFileName)<br> -- Backup number increments for newer files (@see countDirection)<br> -- Infinite number of
+ * backups by file size (@see maxSizeRollBackups)<br> <br> <p>A few notes and warnings: For large or infinite number of
+ * backups countDirection > 0 is highly recommended, with staticLogFileName = false if time based rolling is also used
+ * -- this will reduce the number of file renamings to few or none. Changing staticLogFileName or countDirection
+ * without clearing the directory could have nasty side effects. If Date/Time based rolling is enabled,
+ * CompositeRollingAppender will attempt to roll existing files in the directory without a date/time tag based on the
+ * last modified date of the base log files last modification.<br> <br> <p>A maximum number of backups based on
+ * date/time boundries would be nice but is not yet implemented.<br>
+ *
+ * @author Kevin Steppe
+ * @author Heinz Richter
+ * @author Eirik Lygre
+ * @author Ceki G&uuml;lc&uuml;
+ * @author Martin Ritchie
+ */
+public class QpidCompositeRollingAppender extends FileAppender
+{
+ // The code assumes that the following 'time' constants are in a increasing
+ // sequence.
+ static final int TOP_OF_TROUBLE = -1;
+ static final int TOP_OF_MINUTE = 0;
+ static final int TOP_OF_HOUR = 1;
+ static final int HALF_DAY = 2;
+ static final int TOP_OF_DAY = 3;
+ static final int TOP_OF_WEEK = 4;
+ static final int TOP_OF_MONTH = 5;
+
+ /** Style of rolling to use */
+ static final int BY_SIZE = 1;
+ static final int BY_DATE = 2;
+ static final int BY_COMPOSITE = 3;
+
+ // Not currently used
+ static final String S_BY_SIZE = "Size";
+ static final String S_BY_DATE = "Date";
+ static final String S_BY_COMPOSITE = "Composite";
+
+ /** The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd" meaning daily rollover. */
+ private String datePattern = "'.'yyyy-MM-dd";
+
+ /**
+ * The actual formatted filename that is currently being written to or will be the file transferred to on roll over
+ * (based on staticLogFileName).
+ */
+ private String scheduledFilename = null;
+
+ /** The timestamp when we shall next recompute the filename. */
+ private long nextCheck = System.currentTimeMillis() - 1;
+
+ /** Holds date of last roll over */
+ Date now = new Date();
+
+ SimpleDateFormat sdf;
+
+ /** Helper class to determine next rollover time */
+ RollingCalendar rc = new RollingCalendar();
+
+ /** Current period for roll overs */
+ int checkPeriod = TOP_OF_TROUBLE;
+
+ /** The default maximum file size is 10MB. */
+ protected long maxFileSize = 10 * 1024 * 1024;
+
+ /** There is zero backup files by default. */
+ protected int maxSizeRollBackups = 0;
+ /** How many sized based backups have been made so far */
+ protected int curSizeRollBackups = 0;
+
+ /** not yet implemented */
+ protected int maxTimeRollBackups = -1;
+ protected int curTimeRollBackups = 0;
+
+ /**
+ * By default newer files have lower numbers. (countDirection < 0) ie. log.1 is most recent, log.5 is the 5th
+ * backup, etc... countDirection > 0 does the opposite ie. log.1 is the first backup made, log.5 is the 5th backup
+ * made, etc. For infinite backups use countDirection > 0 to reduce rollOver costs.
+ */
+ protected int countDirection = -1;
+
+ /** Style of rolling to Use. BY_SIZE (1), BY_DATE(2), BY COMPOSITE(3) */
+ protected int rollingStyle = BY_COMPOSITE;
+ protected boolean rollDate = true;
+ protected boolean rollSize = true;
+
+ /**
+ * By default file.log is always the current file. Optionally file.log.yyyy-mm-dd for current formated datePattern
+ * can by the currently logging file (or file.log.curSizeRollBackup or even file.log.yyyy-mm-dd.curSizeRollBackup)
+ * This will make time based roll overs with a large number of backups much faster -- it won't have to rename all
+ * the backups!
+ */
+ protected boolean staticLogFileName = true;
+
+ /** FileName provided in configuration. Used for rolling properly */
+ protected String baseFileName;
+
+ /** Do we want to .gz our backup files. */
+ protected boolean compress = false;
+
+ /** Do we want to use a second thread when compressing our backup files. */
+ protected boolean compressAsync = false;
+
+ /** Do we want to start numbering files at zero. */
+ protected boolean zeroBased = false;
+
+ /** Path provided in configuration. Used for moving backup files to */
+ protected String backupFilesToPath = null;
+ private final ConcurrentLinkedQueue<CompressJob> _compress = new ConcurrentLinkedQueue<CompressJob>();
+ private AtomicBoolean _compressing = new AtomicBoolean(false);
+
+ /** The default constructor does nothing. */
+ public QpidCompositeRollingAppender()
+ { }
+
+ /**
+ * Instantiate a <code>CompositeRollingAppender</code> and open the file designated by <code>filename</code>. The
+ * opened filename will become the ouput destination for this appender.
+ */
+ public QpidCompositeRollingAppender(Layout layout, String filename, String datePattern) throws IOException
+ {
+ this(layout, filename, datePattern, true);
+ }
+
+ /**
+ * Instantiate a CompositeRollingAppender and open the file designated by <code>filename</code>. The opened filename
+ * will become the ouput destination for this appender.
+ *
+ * <p>If the <code>append</code> parameter is true, the file will be appended to. Otherwise, the file desginated by
+ * <code>filename</code> will be truncated before being opened.
+ */
+ public QpidCompositeRollingAppender(Layout layout, String filename, boolean append) throws IOException
+ {
+ super(layout, filename, append);
+ }
+
+ /**
+ * Instantiate a CompositeRollingAppender and open the file designated by <code>filename</code>. The opened filename
+ * will become the ouput destination for this appender.
+ */
+ public QpidCompositeRollingAppender(Layout layout, String filename, String datePattern, boolean append)
+ throws IOException
+ {
+ super(layout, filename, append);
+ this.datePattern = datePattern;
+ activateOptions();
+ }
+
+ /**
+ * Instantiate a CompositeRollingAppender and open the file designated by <code>filename</code>. The opened filename
+ * will become the output destination for this appender.
+ *
+ * <p>The file will be appended to. DatePattern is default.
+ */
+ public QpidCompositeRollingAppender(Layout layout, String filename) throws IOException
+ {
+ super(layout, filename);
+ }
+
+ /**
+ * The <b>DatePattern</b> takes a string in the same format as expected by {@link java.text.SimpleDateFormat}. This
+ * options determines the rollover schedule.
+ */
+ public void setDatePattern(String pattern)
+ {
+ datePattern = pattern;
+ }
+
+ /** Returns the value of the <b>DatePattern</b> option. */
+ public String getDatePattern()
+ {
+ return datePattern;
+ }
+
+ /** Returns the value of the <b>maxSizeRollBackups</b> option. */
+ public int getMaxSizeRollBackups()
+ {
+ return maxSizeRollBackups;
+ }
+
+ /**
+ * Get the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ * @since 1.1
+ */
+ public long getMaximumFileSize()
+ {
+ return maxFileSize;
+ }
+
+ /**
+ * <p>Set the maximum number of backup files to keep around based on file size.
+ *
+ * <p>The <b>MaxSizeRollBackups</b> option determines how many backup files are kept before the oldest is erased.
+ * This option takes an integer value. If set to zero, then there will be no backup files and the log file will be
+ * truncated when it reaches <code>MaxFileSize</code>. If a negative number is supplied then no deletions will be
+ * made. Note that this could result in very slow performance as a large number of files are rolled over unless
+ * {@link #setCountDirection} up is used.
+ *
+ * <p>The maximum applys to -each- time based group of files and -not- the total. Using a daily roll the maximum
+ * total files would be (#days run) * (maxSizeRollBackups)
+ */
+ public void setMaxSizeRollBackups(int maxBackups)
+ {
+ maxSizeRollBackups = maxBackups;
+ }
+
+ /**
+ * Set the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ * <p>This method is equivalent to {@link #setMaxFileSize} except that it is required for differentiating the setter
+ * taking a <code>long</code> argument from the setter taking a <code>String</code> argument by the JavaBeans {@link
+ * java.beans.Introspector Introspector}.
+ *
+ * @see #setMaxFileSize(String)
+ */
+ public void setMaxFileSize(long maxFileSize)
+ {
+ this.maxFileSize = maxFileSize;
+ }
+
+ /**
+ * Set the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ * <p>This method is equivalent to {@link #setMaxFileSize} except that it is required for differentiating the setter
+ * taking a <code>long</code> argument from the setter taking a <code>String</code> argument by the JavaBeans {@link
+ * java.beans.Introspector Introspector}.
+ *
+ * @see #setMaxFileSize(String)
+ */
+ public void setMaximumFileSize(long maxFileSize)
+ {
+ this.maxFileSize = maxFileSize;
+ }
+
+ /**
+ * Set the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ * <p>In configuration files, the <b>MaxFileSize</b> option takes an long integer in the range 0 - 2^63. You can
+ * specify the value with the suffixes "KB", "MB" or "GB" so that the integer is interpreted being expressed
+ * respectively in kilobytes, megabytes or gigabytes. For example, the value "10KB" will be interpreted as 10240.
+ */
+ public void setMaxFileSize(String value)
+ {
+ maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
+ }
+
+ protected void setQWForFiles(Writer writer)
+ {
+ qw = new CountingQuietWriter(writer, errorHandler);
+ }
+
+ // Taken verbatum from DailyRollingFileAppender
+ int computeCheckPeriod()
+ {
+ RollingCalendar c = new RollingCalendar();
+ // set sate to 1970-01-01 00:00:00 GMT
+ Date epoch = new Date(0);
+ if (datePattern != null)
+ {
+ for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++)
+ {
+ String r0 = sdf.format(epoch);
+ c.setType(i);
+ Date next = new Date(c.getNextCheckMillis(epoch));
+ String r1 = sdf.format(next);
+ // LogLog.debug("Type = "+i+", r0 = "+r0+", r1 = "+r1);
+ if ((r0 != null) && (r1 != null) && !r0.equals(r1))
+ {
+ return i;
+ }
+ }
+ }
+
+ return TOP_OF_TROUBLE; // Deliberately head for trouble...
+ }
+
+ // Now for the new stuff
+ /**
+ * Handles append time behavior for CompositeRollingAppender. This checks if a roll over either by date (checked
+ * first) or time (checked second) is need and then appends to the file last.
+ */
+ protected void subAppend(LoggingEvent event)
+ {
+
+ if (rollDate)
+ {
+ long n = System.currentTimeMillis();
+ if (n >= nextCheck)
+ {
+ now.setTime(n);
+ nextCheck = rc.getNextCheckMillis(now);
+
+ rollOverTime();
+ }
+ }
+
+ if (rollSize)
+ {
+ if ((fileName != null) && (((CountingQuietWriter) qw).getCount() >= maxFileSize))
+ {
+ rollOverSize();
+ }
+ }
+
+ super.subAppend(event);
+ }
+
+ public void setFile(String file)
+ {
+ baseFileName = file.trim();
+ fileName = file.trim();
+ }
+
+ /**
+ * Creates and opens the file for logging. If <code>staticLogFileName</code> is false then the fully qualified name
+ * is determined and used.
+ */
+ public synchronized void setFile(String fileName, boolean append) throws IOException
+ {
+ if (!staticLogFileName)
+ {
+ scheduledFilename = fileName = fileName.trim() + sdf.format(now);
+ if (countDirection > 0)
+ {
+ scheduledFilename = fileName = fileName + '.' + (++curSizeRollBackups);
+ }
+ }
+
+ super.setFile(fileName, append, bufferedIO, bufferSize);
+
+ if (append)
+ {
+ File f = new File(fileName);
+ ((CountingQuietWriter) qw).setCount(f.length());
+ }
+ }
+
+ public int getCountDirection()
+ {
+ return countDirection;
+ }
+
+ public void setCountDirection(int direction)
+ {
+ countDirection = direction;
+ }
+
+ public int getRollingStyle()
+ {
+ return rollingStyle;
+ }
+
+ public void setRollingStyle(int style)
+ {
+ rollingStyle = style;
+ switch (rollingStyle)
+ {
+
+ case BY_SIZE:
+ rollDate = false;
+ rollSize = true;
+ break;
+
+ case BY_DATE:
+ rollDate = true;
+ rollSize = false;
+ break;
+
+ case BY_COMPOSITE:
+ rollDate = true;
+ rollSize = true;
+ break;
+
+ default:
+ errorHandler.error("Invalid rolling Style, use 1 (by size only), 2 (by date only) or 3 (both)");
+ }
+ }
+
+ /*
+ public void setRollingStyle(String style) {
+ if (style == S_BY_SIZE) {
+ rollingStyle = BY_SIZE;
+ }
+ else if (style == S_BY_DATE) {
+ rollingStyle = BY_DATE;
+ }
+ else if (style == S_BY_COMPOSITE) {
+ rollingStyle = BY_COMPOSITE;
+ }
+ }
+ */
+ public boolean getStaticLogFileName()
+ {
+ return staticLogFileName;
+ }
+
+ public void setStaticLogFileName(boolean s)
+ {
+ staticLogFileName = s;
+ }
+
+ public void setStaticLogFileName(String value)
+ {
+ setStaticLogFileName(OptionConverter.toBoolean(value, true));
+ }
+
+ public boolean getCompressBackupFiles()
+ {
+ return compress;
+ }
+
+ public void setCompressBackupFiles(boolean c)
+ {
+ compress = c;
+ }
+
+ public boolean getCompressAsync()
+ {
+ return compressAsync;
+ }
+
+ public void setCompressAsync(boolean c)
+ {
+ compressAsync = c;
+ if (compressAsync)
+ {
+ executor = Executors.newFixedThreadPool(1);
+
+ compressor = new Compressor();
+ }
+ }
+
+ public boolean getZeroBased()
+ {
+ return zeroBased;
+ }
+
+ public void setZeroBased(boolean z)
+ {
+ zeroBased = z;
+ }
+
+ public String getBackupFilesToPath()
+ {
+ return backupFilesToPath;
+ }
+
+ public void setbackupFilesToPath(String path)
+ {
+ File td = new File(path);
+ if (!td.exists())
+ {
+ td.mkdirs();
+ }
+
+ backupFilesToPath = path;
+ }
+
+ /**
+ * Initializes based on exisiting conditions at time of <code> activateOptions</code>. The following is done:<br>
+ * <br> A) determine curSizeRollBackups<br> B) determine curTimeRollBackups (not implemented)<br> C) initiates a
+ * roll over if needed for crossing a date boundary since the last run.
+ */
+ protected void existingInit()
+ {
+
+ if (zeroBased)
+ {
+ curSizeRollBackups = -1;
+ }
+
+ curTimeRollBackups = 0;
+
+ // part A starts here
+ String filter;
+ if (staticLogFileName || !rollDate)
+ {
+ filter = baseFileName + ".*";
+ }
+ else
+ {
+ filter = scheduledFilename + ".*";
+ }
+
+ File f = new File(baseFileName);
+ f = f.getParentFile();
+ if (f == null)
+ {
+ f = new File(".");
+ }
+
+ LogLog.debug("Searching for existing files in: " + f);
+ String[] files = f.list();
+
+ if (files != null)
+ {
+ for (int i = 0; i < files.length; i++)
+ {
+ if (!files[i].startsWith(baseFileName))
+ {
+ continue;
+ }
+
+ int index = files[i].lastIndexOf(".");
+
+ if (staticLogFileName)
+ {
+ int endLength = files[i].length() - index;
+ if ((baseFileName.length() + endLength) != files[i].length())
+ {
+ // file is probably scheduledFilename + .x so I don't care
+ continue;
+ }
+ }
+
+ try
+ {
+ int backup = Integer.parseInt(files[i].substring(index + 1, files[i].length()));
+ LogLog.debug("From file: " + files[i] + " -> " + backup);
+ if (backup > curSizeRollBackups)
+ {
+ curSizeRollBackups = backup;
+ }
+ }
+ catch (Exception e)
+ {
+ // this happens when file.log -> file.log.yyyy-mm-dd which is normal
+ // when staticLogFileName == false
+ LogLog.debug("Encountered a backup file not ending in .x " + files[i]);
+ }
+ }
+ }
+
+ LogLog.debug("curSizeRollBackups starts at: " + curSizeRollBackups);
+ // part A ends here
+
+ // part B not yet implemented
+
+ // part C
+ if (staticLogFileName && rollDate)
+ {
+ File old = new File(baseFileName);
+ if (old.exists())
+ {
+ Date last = new Date(old.lastModified());
+ if (!(sdf.format(last).equals(sdf.format(now))))
+ {
+ scheduledFilename = baseFileName + sdf.format(last);
+ LogLog.debug("Initial roll over to: " + scheduledFilename);
+ rollOverTime();
+ }
+ }
+ }
+
+ LogLog.debug("curSizeRollBackups after rollOver at: " + curSizeRollBackups);
+ // part C ends here
+
+ }
+
+ /**
+ * Sets initial conditions including date/time roll over information, first check, scheduledFilename, and calls
+ * <code>existingInit</code> to initialize the current # of backups.
+ */
+ public void activateOptions()
+ {
+
+ // REMOVE removed rollDate from boolean to enable Alex's change
+ if (datePattern != null)
+ {
+ now.setTime(System.currentTimeMillis());
+ sdf = new SimpleDateFormat(datePattern);
+ int type = computeCheckPeriod();
+ // printPeriodicity(type);
+ rc.setType(type);
+ // next line added as this removes the name check in rollOver
+ nextCheck = rc.getNextCheckMillis(now);
+ }
+ else
+ {
+ if (rollDate)
+ {
+ LogLog.error("Either DatePattern or rollingStyle options are not set for [" + name + "].");
+ }
+ }
+
+ existingInit();
+
+ if (rollDate && (fileName != null) && (scheduledFilename == null))
+ {
+ scheduledFilename = fileName + sdf.format(now);
+ }
+
+ try
+ {
+ this.setFile(fileName, true);
+ }
+ catch (IOException e)
+ {
+ errorHandler.error("Cannot set file name:" + fileName);
+ }
+
+ super.activateOptions();
+ }
+
+ /**
+ * Rollover the file(s) to date/time tagged file(s). Opens the new file (through setFile) and resets
+ * curSizeRollBackups.
+ */
+ protected void rollOverTime()
+ {
+
+ curTimeRollBackups++;
+
+ this.closeFile(); // keep windows happy.
+
+ // delete the old stuff here
+
+ if (staticLogFileName)
+ {
+ /* Compute filename, but only if datePattern is specified */
+ if (datePattern == null)
+ {
+ errorHandler.error("Missing DatePattern option in rollOver().");
+
+ return;
+ }
+
+ // is the new file name equivalent to the 'current' one
+ // something has gone wrong if we hit this -- we should only
+ // roll over if the new file will be different from the old
+ String dateFormat = sdf.format(now);
+ if (scheduledFilename.equals(fileName + dateFormat))
+ {
+ errorHandler.error("Compare " + scheduledFilename + " : " + fileName + dateFormat);
+
+ return;
+ }
+
+ // close current file, and rename it to datedFilename
+ this.closeFile();
+
+ // we may have to roll over a large number of backups here
+ String from, to;
+ for (int i = 1; i <= curSizeRollBackups; i++)
+ {
+ from = fileName + '.' + i;
+ to = scheduledFilename + '.' + i;
+ rollFile(from, to, false);
+ }
+
+ rollFile(fileName, scheduledFilename, compress);
+ }
+ else
+ {
+ if (compress)
+ {
+ compress(fileName);
+ }
+ }
+
+ try
+ {
+ // This will also close the file. This is OK since multiple
+ // close operations are safe.
+ curSizeRollBackups = 0; // We're cleared out the old date and are ready for the new
+
+ // new scheduled name
+ scheduledFilename = fileName + sdf.format(now);
+ this.setFile(baseFileName, false);
+ }
+ catch (IOException e)
+ {
+ errorHandler.error("setFile(" + fileName + ", false) call failed.");
+ }
+
+ }
+
+ /**
+ * Renames file <code>from</code> to file <code>to</code>. It also checks for existence of target file and deletes
+ * if it does.
+ */
+ protected void rollFile(String from, String to, boolean compress)
+ {
+ if (from.equals(to))
+ {
+ if (compress)
+ {
+ LogLog.debug("Attempting to compress file with same output name.");
+ }
+
+ return;
+ }
+
+ File target = new File(to);
+ if (target.exists())
+ {
+ LogLog.debug("deleting existing target file: " + target);
+ target.delete();
+ }
+
+ File file = new File(from);
+ if (compress)
+ {
+ compress(file, target);
+ }
+ else
+ {
+ if (!file.getPath().equals(target.getPath()))
+ {
+ file.renameTo(target);
+ }
+ }
+
+ LogLog.debug(from + " -> " + to);
+ }
+
+ protected void compress(String file)
+ {
+ File f = new File(file);
+ compress(f, f);
+ }
+
+ private void compress(File from, File target)
+ {
+ if (compressAsync)
+ {
+ synchronized (_compress)
+ {
+ _compress.offer(new CompressJob(from, target));
+ }
+
+ startCompression();
+ }
+ else
+ {
+ doCompress(from, target);
+ }
+ }
+
+ private void startCompression()
+ {
+ if (_compressing.compareAndSet(false, true))
+ {
+ executor.execute(compressor);
+ }
+ }
+
+ /** Delete's the specified file if it exists */
+ protected static void deleteFile(String fileName)
+ {
+ File file = new File(fileName);
+ if (file.exists())
+ {
+ file.delete();
+ }
+ }
+
+ /**
+ * Implements roll overs base on file size.
+ *
+ * <p>If the maximum number of size based backups is reached (<code>curSizeRollBackups == maxSizeRollBackups</code)
+ * then the oldest file is deleted -- it's index determined by the sign of countDirection.<br> If
+ * <code>countDirection</code> < 0, then files {<code>File.1</code>, ..., <code>File.curSizeRollBackups -1</code>}
+ * are renamed to {<code>File.2</code>, ..., <code>File.curSizeRollBackups</code>}. Moreover, <code>File</code> is
+ * renamed <code>File.1</code> and closed.<br>
+ *
+ * A new file is created to receive further log output.
+ *
+ * <p>If <code>maxSizeRollBackups</code> is equal to zero, then the <code>File</code> is truncated with no backup
+ * files created.
+ *
+ * <p>If <code>maxSizeRollBackups</code> < 0, then <code>File</code> is renamed if needed and no files are deleted.
+ */
+
+ // synchronization not necessary since doAppend is alreasy synched
+ protected void rollOverSize()
+ {
+ File file;
+
+ this.closeFile(); // keep windows happy.
+
+ LogLog.debug("rolling over count=" + ((CountingQuietWriter) qw).getCount());
+ LogLog.debug("maxSizeRollBackups = " + maxSizeRollBackups);
+ LogLog.debug("curSizeRollBackups = " + curSizeRollBackups);
+ LogLog.debug("countDirection = " + countDirection);
+
+ // If maxBackups <= 0, then there is no file renaming to be done.
+ if (maxSizeRollBackups != 0)
+ {
+
+ if (countDirection < 0)
+ {
+ // Delete the oldest file, to keep Windows happy.
+ if (curSizeRollBackups == maxSizeRollBackups)
+ {
+ deleteFile(fileName + '.' + maxSizeRollBackups);
+ curSizeRollBackups--;
+ }
+
+ // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
+ for (int i = curSizeRollBackups; i >= 1; i--)
+ {
+ rollFile((fileName + "." + i), (fileName + '.' + (i + 1)), false);
+ }
+
+ curSizeRollBackups++;
+ // Rename fileName to fileName.1
+ rollFile(fileName, fileName + ".1", compress);
+
+ } // REMOVE This code branching for Alexander Cerna's request
+ else if (countDirection == 0)
+ {
+ // rollFile based on date pattern
+ curSizeRollBackups++;
+ now.setTime(System.currentTimeMillis());
+ scheduledFilename = fileName + sdf.format(now);
+ rollFile(fileName, scheduledFilename, compress);
+ }
+ else
+ { // countDirection > 0
+ if ((curSizeRollBackups >= maxSizeRollBackups) && (maxSizeRollBackups > 0))
+ {
+ // delete the first and keep counting up.
+ int oldestFileIndex = curSizeRollBackups - maxSizeRollBackups + 1;
+ deleteFile(fileName + '.' + oldestFileIndex);
+ }
+
+ if (staticLogFileName)
+ {
+ curSizeRollBackups++;
+ rollFile(fileName, fileName + '.' + curSizeRollBackups, compress);
+ }
+ else
+ {
+ if (compress)
+ {
+ compress(fileName);
+ }
+ }
+ }
+ }
+
+ try
+ {
+ // This will also close the file. This is OK since multiple
+ // close operations are safe.
+ this.setFile(baseFileName, false);
+ }
+ catch (IOException e)
+ {
+ LogLog.error("setFile(" + fileName + ", false) call failed.", e);
+ }
+ }
+
+ protected synchronized void doCompress(File from, File to)
+ {
+ String toFile;
+ if (backupFilesToPath == null)
+ {
+ toFile = to.getPath() + ".gz";
+ }
+ else
+ {
+ toFile = backupFilesToPath + System.getProperty("file.separator") + to.getName() + ".gz";
+ }
+
+ File target = new File(toFile);
+ if (target.exists())
+ {
+ LogLog.debug("deleting existing target file: " + target);
+ target.delete();
+ }
+
+ try
+ {
+ // Create the GZIP output stream
+ GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(target));
+
+ // Open the input file
+ FileInputStream in = new FileInputStream(from);
+
+ // Transfer bytes from the input file to the GZIP output stream
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0)
+ {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+
+ // Complete the GZIP file
+ out.finish();
+ out.close();
+ // Remove old file.
+ from.delete();
+ }
+ catch (IOException e)
+ {
+ if (target.exists())
+ {
+ target.delete();
+ }
+
+ rollFile(from.getPath(), to.getPath(), false);
+ }
+ }
+
+ private class CompressJob
+ {
+ File _from, _to;
+
+ CompressJob(File from, File to)
+ {
+ _from = from;
+ _to = to;
+ }
+
+ File getFrom()
+ {
+ return _from;
+ }
+
+ File getTo()
+ {
+ return _to;
+ }
+ }
+
+ Compressor compressor = null;
+
+ Executor executor;
+
+ private class Compressor implements Runnable
+ {
+ public void run()
+ {
+ boolean running = true;
+ while (running)
+ {
+ CompressJob job = _compress.poll();
+
+ doCompress(job.getFrom(), job.getTo());
+
+ synchronized (_compress)
+ {
+ if (_compress.isEmpty())
+ {
+ running = false;
+ _compressing.set(false);
+ }
+ }
+ }
+
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java b/Final/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java
new file mode 100644
index 0000000000..40ff590a0a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.configuration;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PosixParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class Configuration
+{
+ public static final String QPID_HOME = "QPID_HOME";
+
+ final String QPIDHOME = System.getProperty(QPID_HOME);
+
+ private static Logger _devlog = LoggerFactory.getLogger(Configuration.class);
+
+ public static final String DEFAULT_LOG_CONFIG_FILENAME = "log4j.xml";
+ public static final String DEFAULT_CONFIG_FILE = "etc/config.xml";
+
+ protected final Options _options = new Options();
+ protected CommandLine _commandLine;
+ protected File _configFile;
+
+
+ public Configuration()
+ {
+
+ }
+
+ public void processCommandline(String[] args) throws InitException
+ {
+ try
+ {
+ _commandLine = new PosixParser().parse(_options, args);
+ }
+ catch (ParseException e)
+ {
+ throw new InitException("Unable to parse commmandline", e);
+ }
+
+ final File defaultConfigFile = new File(QPIDHOME, DEFAULT_CONFIG_FILE);
+ setConfig(new File(_commandLine.getOptionValue("c", defaultConfigFile.getPath())));
+ }
+
+ public void setConfig(File file)
+ {
+ _configFile = file;
+ }
+
+ /**
+ * @param option The option to set.
+ */
+ public void setOption(Option option)
+ {
+ _options.addOption(option);
+ }
+
+ /**
+ * getOptionValue from the configuration
+ * @param option variable argument, first string is option to get, second if present is the default value.
+ * @return the String for the given option or null if not present (if default value not specified)
+ */
+ public String getOptionValue(String... option)
+ {
+ if (option.length == 1)
+ {
+ return _commandLine.getOptionValue(option[0]);
+ }
+ else if (option.length == 2)
+ {
+ return _commandLine.getOptionValue(option[0], option[1]);
+ }
+ return null;
+ }
+
+ public void loadConfig(File file) throws InitException
+ {
+ setConfig(file);
+ loadConfig();
+ }
+
+ private void loadConfig() throws InitException
+ {
+ if (!_configFile.exists())
+ {
+ String error = "File " + _configFile + " could not be found. Check the file exists and is readable.";
+
+ if (QPIDHOME == null)
+ {
+ error = error + "\nNote: " + QPID_HOME + " is not set.";
+ }
+
+ throw new InitException(error, null);
+ }
+ else
+ {
+ _devlog.debug("Using configuration file " + _configFile.getAbsolutePath());
+ }
+
+// String logConfig = _commandLine.getOptionValue("l");
+// String logWatchConfig = _commandLine.getOptionValue("w", "0");
+// if (logConfig != null)
+// {
+// File logConfigFile = new File(logConfig);
+// configureLogging(logConfigFile, logWatchConfig);
+// }
+// else
+// {
+// File configFileDirectory = _configFile.getParentFile();
+// File logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME);
+// configureLogging(logConfigFile, logWatchConfig);
+// }
+ }
+
+
+// private void configureLogging(File logConfigFile, String logWatchConfig)
+// {
+// int logWatchTime = 0;
+// try
+// {
+// logWatchTime = Integer.parseInt(logWatchConfig);
+// }
+// catch (NumberFormatException e)
+// {
+// _devlog.error("Log watch configuration value of " + logWatchConfig + " is invalid. Must be "
+// + "a non-negative integer. Using default of zero (no watching configured");
+// }
+//
+// if (logConfigFile.exists() && logConfigFile.canRead())
+// {
+// _devlog.info("Configuring logger using configuration file " + logConfigFile.getAbsolutePath());
+// if (logWatchTime > 0)
+// {
+// _devlog.info("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every "
+// + logWatchTime + " seconds");
+// // log4j expects the watch interval in milliseconds
+// DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), logWatchTime * 1000);
+// }
+// else
+// {
+// DOMConfigurator.configure(logConfigFile.getAbsolutePath());
+// }
+// }
+// else
+// {
+// System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath());
+// System.err.println("Using basic log4j configuration");
+// BasicConfigurator.configure();
+// }
+// }
+
+ public File getConfigFile()
+ {
+ return _configFile;
+ }
+
+
+ public class InitException extends Exception
+ {
+ InitException(String msg, Throwable cause)
+ {
+ super(msg, cause);
+ }
+ }
+} \ No newline at end of file
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/Final/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
new file mode 100644
index 0000000000..9335723bc5
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
@@ -0,0 +1,244 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.commons.configuration.Configuration;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.configuration.Configurator;
+import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+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.management.AMQManagedObject;
+import org.apache.qpid.server.management.MBeanConstructor;
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.management.ManagedBroker;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+/**
+ * This MBean implements the broker management interface and exposes the
+ * Broker level management features like creating and deleting exchanges and queue.
+ */
+@MBeanDescription("This MBean exposes the broker level management features")
+public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBroker
+{
+ private final QueueRegistry _queueRegistry;
+ private final ExchangeRegistry _exchangeRegistry;
+ private final ExchangeFactory _exchangeFactory;
+ private final MessageStore _messageStore;
+
+ private final VirtualHost.VirtualHostMBean _virtualHostMBean;
+
+ @MBeanConstructor("Creates the Broker Manager MBean")
+ public AMQBrokerManagerMBean(VirtualHost.VirtualHostMBean virtualHostMBean) throws JMException
+ {
+ super(ManagedBroker.class, ManagedBroker.TYPE);
+
+ _virtualHostMBean = virtualHostMBean;
+ VirtualHost virtualHost = virtualHostMBean.getVirtualHost();
+
+ _queueRegistry = virtualHost.getQueueRegistry();
+ _exchangeRegistry = virtualHost.getExchangeRegistry();
+ _messageStore = virtualHost.getMessageStore();
+ _exchangeFactory = virtualHost.getExchangeFactory();
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _virtualHostMBean.getVirtualHost().getName();
+ }
+
+ /**
+ * Creates new exchange and registers it with the registry.
+ *
+ * @param exchangeName
+ * @param type
+ * @param durable
+ * @throws JMException
+ */
+ public void createNewExchange(String exchangeName, String type, boolean durable) throws JMException
+ {
+ try
+ {
+ synchronized (_exchangeRegistry)
+ {
+ Exchange exchange = _exchangeRegistry.getExchange(new AMQShortString(exchangeName));
+ if (exchange == null)
+ {
+ exchange = _exchangeFactory.createExchange(new AMQShortString(exchangeName), new AMQShortString(type),
+ durable, false, 0);
+ _exchangeRegistry.registerExchange(exchange);
+ }
+ else
+ {
+ throw new JMException("The exchange \"" + exchangeName + "\" already exists.");
+ }
+ }
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, "Error in creating exchange " + exchangeName);
+ }
+ }
+
+ /**
+ * Unregisters the exchange from registry.
+ *
+ * @param exchangeName
+ * @throws JMException
+ */
+ public void unregisterExchange(String exchangeName) throws JMException
+ {
+ // TODO
+ // Check if the exchange is in use.
+ // boolean inUse = false;
+ // Check if there are queue-bindings with the exchange and unregister
+ // when there are no bindings.
+ try
+ {
+ _exchangeRegistry.unregisterExchange(new AMQShortString(exchangeName), false);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, "Error in unregistering exchange " + exchangeName);
+ }
+ }
+
+ /**
+ * Creates a new queue and registers it with the registry and puts it
+ * in persistance storage if durable queue.
+ *
+ * @param queueName
+ * @param durable
+ * @param owner
+ * @throws JMException
+ */
+ public void createNewQueue(String queueName, String owner, boolean durable) throws JMException
+ {
+ AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName));
+ if (queue != null)
+ {
+ throw new JMException("The queue \"" + queueName + "\" already exists.");
+ }
+
+ try
+ {
+ AMQShortString ownerShortString = null;
+ if (owner != null)
+ {
+ ownerShortString = new AMQShortString(owner);
+ }
+
+ queue = new AMQQueue(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost());
+ if (queue.isDurable() && !queue.isAutoDelete())
+ {
+ _messageStore.createQueue(queue);
+ }
+
+ Configuration virtualHostDefaultQueueConfiguration =
+ VirtualHostConfiguration.getDefaultQueueConfiguration(queue);
+ if (virtualHostDefaultQueueConfiguration != null)
+ {
+ Configurator.configure(queue, virtualHostDefaultQueueConfiguration);
+ }
+
+ _queueRegistry.registerQueue(queue);
+ }
+ catch (AMQException ex)
+ {
+ JMException jme = new JMException(ex.getMessage());
+ jme.initCause(ex);
+ throw new MBeanException(jme, "Error in creating queue " + queueName);
+ }
+ }
+
+ private VirtualHost getVirtualHost()
+ {
+ return _virtualHostMBean.getVirtualHost();
+ }
+
+ /**
+ * Deletes the queue from queue registry and persistant storage.
+ *
+ * @param queueName
+ * @throws JMException
+ */
+ public void deleteQueue(String queueName) throws JMException
+ {
+ AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName));
+ if (queue == null)
+ {
+ throw new JMException("The Queue " + queueName + " is not a registerd queue.");
+ }
+
+ try
+ {
+ queue.delete();
+ _messageStore.removeQueue(new AMQShortString(queueName));
+
+ }
+ catch (AMQException ex)
+ {
+ JMException jme = new JMException(ex.getMessage());
+ jme.initCause(ex);
+ throw new MBeanException(jme, "Error in deleting queue " + queueName);
+ }
+ }
+
+ public ManagedObject getParentObject()
+ {
+ return _virtualHostMBean;
+ }
+
+ // This will have a single instance for a virtual host, so not having the name property in the ObjectName
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ return getObjectNameForSingleInstanceMBean();
+ }
+} // End of MBean class
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/Final/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
new file mode 100644
index 0000000000..d3b459c48a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
@@ -0,0 +1,989 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.ack.UnacknowledgedMessage;
+import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
+import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl;
+import org.apache.qpid.server.exchange.MessageRouter;
+import org.apache.qpid.server.exchange.NoRouteException;
+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.MessageHandleFactory;
+import org.apache.qpid.server.queue.Subscription;
+import org.apache.qpid.server.store.MessageStore;
+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 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.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class AMQChannel
+{
+ public static final int DEFAULT_PREFETCH = 5000;
+
+ private static final Logger _log = Logger.getLogger(AMQChannel.class);
+
+ private final int _channelId;
+
+ // private boolean _transactional;
+
+ private long _prefetch_HighWaterMark;
+
+ private long _prefetch_LowWaterMark;
+
+ private long _prefetchSize;
+
+ /**
+ * 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);
+
+ /** A channel has a default queue (the last declared) that is used when no queue name is explictily set */
+ private AMQQueue _defaultQueue;
+
+ /** This tag is unique per subscription to a queue. The server returns this in response to a basic.consume request. */
+ private int _consumerTag;
+
+ /**
+ * The current message - which may be partial in the sense that not all frames have been received yet - which has
+ * been received by this channel. As the frames are received the message gets updated and once all frames have been
+ * received the message can then be routed.
+ */
+ private AMQMessage _currentMessage;
+
+ /** 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 MessageStore _messageStore;
+
+ private UnacknowledgedMessageMap _unacknowledgedMessageMap = new UnacknowledgedMessageMapImpl(DEFAULT_PREFETCH);
+
+ private final AtomicBoolean _suspended = new AtomicBoolean(false);
+
+ private final MessageRouter _exchanges;
+
+ private TransactionalContext _txnContext, _nonTransactedContext;
+
+ /**
+ * A context used by the message store enabling it to track context for a given channel even across thread
+ * boundaries
+ */
+ private final StoreContext _storeContext;
+
+ private final List<RequiredDeliveryException> _returnMessages = new LinkedList<RequiredDeliveryException>();
+
+ private MessageHandleFactory _messageHandleFactory = new MessageHandleFactory();
+
+ private Set<Long> _browsedAcks = new HashSet<Long>();
+
+ // Why do we need this reference ? - ritchiem
+ private final AMQProtocolSession _session;
+ private boolean _closing;
+
+ public AMQChannel(AMQProtocolSession session, int channelId, MessageStore messageStore, MessageRouter exchanges)
+ throws AMQException
+ {
+ _session = session;
+ _channelId = channelId;
+ _storeContext = new StoreContext("Session: " + session.getClientIdentifier() + "; channel: " + channelId);
+ _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);
+ }
+
+ /** Sets this channel to be part of a local transaction */
+ public void setLocalTransactional()
+ {
+ _txnContext = new LocalTransactionalContext(_messageStore, _storeContext, _returnMessages);
+ }
+
+ public boolean isTransactional()
+ {
+ // this does not look great but there should only be one "non-transactional"
+ // transactional context, while there could be several transactional ones in
+ // theory
+ return !(_txnContext instanceof NonTransactionalContext);
+ }
+
+ public int getChannelId()
+ {
+ return _channelId;
+ }
+
+ public long getPrefetchCount()
+ {
+ return _prefetch_HighWaterMark;
+ }
+
+ public void setPrefetchCount(long prefetchCount)
+ {
+ _prefetch_HighWaterMark = prefetchCount;
+ }
+
+ public long getPrefetchSize()
+ {
+ return _prefetchSize;
+ }
+
+ public void setPrefetchSize(long prefetchSize)
+ {
+ _prefetchSize = prefetchSize;
+ }
+
+ public long getPrefetchLowMarkCount()
+ {
+ return _prefetch_LowWaterMark;
+ }
+
+ public void setPrefetchLowMarkCount(long prefetchCount)
+ {
+ _prefetch_LowWaterMark = prefetchCount;
+ }
+
+ public long getPrefetchHighMarkCount()
+ {
+ return _prefetch_HighWaterMark;
+ }
+
+ public void setPrefetchHighMarkCount(long prefetchCount)
+ {
+ _prefetch_HighWaterMark = prefetchCount;
+ }
+
+ public void setPublishFrame(MessagePublishInfo info, AMQProtocolSession publisher) throws AMQException
+ {
+
+ _currentMessage = new AMQMessage(_messageStore.getNewMessageId(), info, _txnContext);
+ _currentMessage.setPublisher(publisher);
+ }
+
+ public void publishContentHeader(ContentHeaderBody contentHeaderBody, AMQProtocolSession protocolSession)
+ throws AMQException
+ {
+ if (_currentMessage == null)
+ {
+ throw new AMQException("Received content header without previously receiving a BasicPublish frame");
+ }
+ else
+ {
+ if (_log.isTraceEnabled())
+ {
+ _log.trace(debugIdentity() + "Content header received on channel " + _channelId);
+ }
+
+ _currentMessage.setContentHeaderBody(contentHeaderBody);
+ _currentMessage.setExpiration();
+
+ routeCurrentMessage();
+ _currentMessage.routingComplete(_messageStore, _storeContext, _messageHandleFactory);
+
+ // check and deliver if header says body length is zero
+ if (contentHeaderBody.bodySize == 0)
+ {
+ _txnContext.messageProcessed(protocolSession);
+ _currentMessage = null;
+ }
+ }
+ }
+
+ public void publishContentBody(ContentBody contentBody, AMQProtocolSession protocolSession) throws AMQException
+ {
+ if (_currentMessage == null)
+ {
+ throw new AMQException("Received content body without previously receiving a JmsPublishBody");
+ }
+
+ if (_log.isTraceEnabled())
+ {
+ _log.trace(debugIdentity() + "Content body received on channel " + _channelId);
+ }
+
+ try
+ {
+
+ // returns true iff the message was delivered (i.e. if all data was
+ // received
+ if (_currentMessage.addContentBodyFrame(_storeContext,
+ protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToContentChunk(
+ contentBody)))
+ {
+ // callback to allow the context to do any post message processing
+ // primary use is to allow message return processing in the non-tx case
+ _txnContext.messageProcessed(protocolSession);
+ _currentMessage = null;
+ }
+ }
+ catch (AMQException e)
+ {
+ // we want to make sure we don't keep a reference to the message in the
+ // event of an error
+ _currentMessage = null;
+ throw e;
+ }
+ }
+
+ protected void routeCurrentMessage() throws AMQException
+ {
+ try
+ {
+ _exchanges.routeContent(_currentMessage);
+ }
+ catch (NoRouteException e)
+ {
+ _returnMessages.add(e);
+ }
+ }
+
+ public long getNextDeliveryTag()
+ {
+ return _deliveryTag.incrementAndGet();
+ }
+
+ public int getNextConsumerTag()
+ {
+ return ++_consumerTag;
+ }
+
+ /**
+ * Subscribe to a queue. We register all subscriptions in the channel so that if the channel is closed we can clean
+ * up all subscriptions, even if the client does not explicitly unsubscribe from all queues.
+ *
+ * @param tag the tag chosen by the client (if null, server will generate one)
+ * @param queue the queue to subscribe to
+ * @param session the protocol session of the subscriber
+ * @param noLocal Flag stopping own messages being receivied.
+ * @param exclusive Flag requesting exclusive access to the queue
+ * @param acks Are acks enabled for this subscriber
+ * @param filters Filters to apply to this subscriber
+ *
+ * @return the consumer tag. This is returned to the subscriber and used in subsequent unsubscribe requests
+ *
+ * @throws ConsumerTagNotUniqueException if the tag is not unique
+ * @throws AMQException if something goes wrong
+ */
+ public AMQShortString subscribeToQueue(AMQShortString tag, AMQQueue queue, AMQProtocolSession session, boolean acks,
+ FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException, ConsumerTagNotUniqueException
+ {
+ if (tag == null)
+ {
+ tag = new AMQShortString("sgen_" + getNextConsumerTag());
+ }
+
+ if (_consumerTag2QueueMap.containsKey(tag))
+ {
+ throw new ConsumerTagNotUniqueException();
+ }
+
+ queue.registerProtocolSession(session, _channelId, tag, acks, filters, noLocal, exclusive);
+ _consumerTag2QueueMap.put(tag, queue);
+
+ return tag;
+ }
+
+ public void unsubscribeConsumer(AMQProtocolSession session, AMQShortString consumerTag) throws AMQException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Unacked Map Dump size:" + _unacknowledgedMessageMap.size());
+ _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
+ {
+
+ public boolean callback(UnacknowledgedMessage message) throws AMQException
+ {
+ _log.debug(message);
+
+ return true;
+ }
+
+ public void visitComplete()
+ { }
+ });
+ }
+
+ AMQQueue q = _consumerTag2QueueMap.remove(consumerTag);
+ if (q != null)
+ {
+ q.unregisterProtocolSession(session, _channelId, consumerTag);
+ }
+ }
+
+ /**
+ * Called from the protocol session to close this channel and clean up. T
+ *
+ * @param session The session to close
+ *
+ * @throws AMQException if there is an error during closure
+ */
+ public void close(AMQProtocolSession session) throws AMQException
+ {
+ _txnContext.rollback();
+ unsubscribeAllConsumers(session);
+ requeue();
+
+ setClosing(true);
+ }
+
+ private void setClosing(boolean closing)
+ {
+ _closing = closing;
+ }
+
+ private void unsubscribeAllConsumers(AMQProtocolSession session) throws AMQException
+ {
+ if (_log.isInfoEnabled())
+ {
+ if (!_consumerTag2QueueMap.isEmpty())
+ {
+ _log.info("Unsubscribing all consumers on channel " + toString());
+ }
+ else
+ {
+ _log.info("No consumers to unsubscribe on channel " + toString());
+ }
+ }
+
+ for (Map.Entry<AMQShortString, AMQQueue> me : _consumerTag2QueueMap.entrySet())
+ {
+ if (_log.isInfoEnabled())
+ {
+ _log.info("Unsubscribing consumer '" + me.getKey() + "' on channel " + toString());
+ }
+
+ me.getValue().unregisterProtocolSession(session, _channelId, me.getKey());
+ }
+
+ _consumerTag2QueueMap.clear();
+ }
+
+ /**
+ * Add a message to the channel-based list of unacknowledged messages
+ *
+ * @param message the message that was delivered
+ * @param deliveryTag the delivery tag used when delivering the message (see protocol spec for description of the
+ * delivery tag)
+ * @param consumerTag The tag for the consumer that is to acknowledge this message.
+ * @param queue the queue from which the message was delivered
+ */
+ public void addUnacknowledgedMessage(AMQMessage message, long deliveryTag, AMQShortString consumerTag, AMQQueue queue)
+ {
+ if (_log.isDebugEnabled())
+ {
+ if (queue == null)
+ {
+ _log.debug("Adding unacked message with a null queue:" + message.debugIdentity());
+ }
+ else
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug(debugIdentity() + " Adding unacked message(" + message.toString() + " DT:" + deliveryTag
+ + ") with a queue(" + queue + ") for " + consumerTag);
+ }
+ }
+ }
+
+ synchronized (_unacknowledgedMessageMap.getLock())
+ {
+ _unacknowledgedMessageMap.add(deliveryTag, new UnacknowledgedMessage(queue, message, consumerTag, deliveryTag));
+ checkSuspension();
+ }
+ }
+
+ private final String id = "(" + System.identityHashCode(this) + ")";
+
+ public String debugIdentity()
+ {
+ return _channelId + id;
+ }
+
+ /**
+ * Called to attempt re-delivery all outstanding unacknowledged messages on the channel. May result in delivery to
+ * this same channel or to other subscribers.
+ *
+ * @throws org.apache.qpid.AMQException if the requeue fails
+ */
+ public void requeue() throws AMQException
+ {
+ // we must create a new map since all the messages will get a new delivery tag when they are redelivered
+ Collection<UnacknowledgedMessage> messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages();
+
+ // Deliver these messages out of the transaction as their delivery was never
+ // part of the transaction only the receive.
+ TransactionalContext deliveryContext = null;
+
+ if (!messagesToBeDelivered.isEmpty())
+ {
+ if (_log.isInfoEnabled())
+ {
+ _log.info("Requeuing " + messagesToBeDelivered.size() + " unacked messages. for " + toString());
+ }
+
+ if (!(_txnContext instanceof NonTransactionalContext))
+ {
+ // if (_nonTransactedContext == null)
+ {
+ _nonTransactedContext =
+ new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
+ }
+
+ deliveryContext = _nonTransactedContext;
+ }
+ else
+ {
+ deliveryContext = _txnContext;
+ }
+ }
+
+ for (UnacknowledgedMessage unacked : messagesToBeDelivered)
+ {
+ if (unacked.queue != null)
+ {
+ // Ensure message is released for redelivery
+ unacked.message.release(unacked.queue);
+
+ // Mark message redelivered
+ unacked.message.setRedelivered(true);
+
+ // Deliver Message
+ deliveryContext.deliver(unacked.message, unacked.queue, false);
+
+ // Should we allow access To the DM to directy deliver the message?
+ // As we don't need to check for Consumers or worry about incrementing the message count?
+ // unacked.queue.getDeliveryManager().deliver(_storeContext, unacked.queue.getName(), unacked.message, false);
+ }
+ }
+
+ }
+
+ /**
+ * Requeue a single message
+ *
+ * @param deliveryTag The message to requeue
+ *
+ * @throws AMQException If something goes wrong.
+ */
+ public void requeue(long deliveryTag) throws AMQException
+ {
+ UnacknowledgedMessage unacked = _unacknowledgedMessageMap.remove(deliveryTag);
+
+ if (unacked != null)
+ {
+
+ // Ensure message is released for redelivery
+ if (unacked.queue != null)
+ {
+ unacked.message.release(unacked.queue);
+ }
+
+ // Mark message redelivered
+ unacked.message.setRedelivered(true);
+
+ // Deliver these messages out of the transaction as their delivery was never
+ // part of the transaction only the receive.
+ TransactionalContext deliveryContext;
+ if (!(_txnContext instanceof NonTransactionalContext))
+ {
+ // if (_nonTransactedContext == null)
+ {
+ _nonTransactedContext =
+ new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
+ }
+
+ deliveryContext = _nonTransactedContext;
+ }
+ else
+ {
+ deliveryContext = _txnContext;
+ }
+
+ if (unacked.queue != null)
+ {
+ // Redeliver the messages to the front of the queue
+ deliveryContext.deliver(unacked.message, unacked.queue, true);
+ // Deliver increments the message count but we have already deliverted this once so don't increment it again
+ // this was because deliver did an increment changed this.
+ }
+ else
+ {
+ _log.warn(System.identityHashCode(this) + " Requested requeue of message(" + unacked.message.debugIdentity()
+ + "):" + deliveryTag + " but no queue defined and no DeadLetter queue so DROPPING message.");
+ // _log.error("Requested requeue of message:" + deliveryTag +
+ // " but no queue defined using DeadLetter queue:" + getDeadLetterQueue());
+ //
+ // deliveryContext.deliver(unacked.message, getDeadLetterQueue(), false);
+ //
+ }
+ }
+ else
+ {
+ _log.warn("Requested requeue of message:" + deliveryTag + " but no such delivery tag exists."
+ + _unacknowledgedMessageMap.size());
+
+ if (_log.isDebugEnabled())
+ {
+ _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
+ {
+ int count = 0;
+
+ public boolean callback(UnacknowledgedMessage message) throws AMQException
+ {
+ _log.debug(
+ (count++) + ": (" + message.message.debugIdentity() + ")" + "[" + message.deliveryTag + "]");
+
+ return false; // Continue
+ }
+
+ public void visitComplete()
+ { }
+ });
+ }
+ }
+
+ }
+
+ /**
+ * Called to resend all outstanding unacknowledged messages to this same channel.
+ *
+ * @param requeue Are the messages to be requeued or dropped.
+ *
+ * @throws AMQException When something goes wrong.
+ */
+ public void resend(final boolean requeue) throws AMQException
+ {
+ final List<UnacknowledgedMessage> msgToRequeue = new LinkedList<UnacknowledgedMessage>();
+ final List<UnacknowledgedMessage> msgToResend = new LinkedList<UnacknowledgedMessage>();
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("unacked map Size:" + _unacknowledgedMessageMap.size());
+ }
+
+ // Process the Unacked-Map.
+ // Marking messages who still have a consumer for to be resent
+ // and those that don't to be requeued.
+ _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
+ {
+ public boolean callback(UnacknowledgedMessage message) throws AMQException
+ {
+ AMQShortString consumerTag = message.consumerTag;
+ AMQMessage msg = message.message;
+ msg.setRedelivered(true);
+ if (consumerTag != null)
+ {
+ // Consumer exists
+ if (_consumerTag2QueueMap.containsKey(consumerTag))
+ {
+ msgToResend.add(message);
+ }
+ else // consumer has gone
+ {
+ msgToRequeue.add(message);
+ }
+ }
+ else
+ {
+ // Message has no consumer tag, so was "delivered" to a GET
+ // or consumer no longer registered
+ // cannot resend, so re-queue.
+ if (message.queue != null)
+ {
+ if (requeue)
+ {
+ msgToRequeue.add(message);
+ }
+ else
+ {
+ _log.info("No DeadLetter Queue and requeue not requested so dropping message:" + message);
+ }
+ }
+ else
+ {
+ _log.info("Message.queue is null and no DeadLetter Queue so dropping message:" + message);
+ }
+ }
+
+ // false means continue processing
+ return false;
+ }
+
+ public void visitComplete()
+ { }
+ });
+
+ // Process Messages to Resend
+ if (_log.isDebugEnabled())
+ {
+ if (!msgToResend.isEmpty())
+ {
+ _log.debug("Preparing (" + msgToResend.size() + ") message to resend.");
+ }
+ else
+ {
+ _log.debug("No message to resend.");
+ }
+ }
+
+ for (UnacknowledgedMessage message : msgToResend)
+ {
+ AMQMessage msg = message.message;
+
+ // Our Java Client will always suspend the channel when resending!
+ // If the client has requested the messages be resent then it is
+ // their responsibility to ensure that thay are capable of receiving them
+ // i.e. The channel hasn't been server side suspended.
+ // if (isSuspended())
+ // {
+ // _log.info("Channel is suspended so requeuing");
+ // //move this message to requeue
+ // msgToRequeue.add(message);
+ // }
+ // else
+ // {
+ // release to allow it to be delivered
+ msg.release(message.queue);
+
+ // Without any details from the client about what has been processed we have to mark
+ // all messages in the unacked map as redelivered.
+ msg.setRedelivered(true);
+
+ Subscription sub = msg.getDeliveredSubscription(message.queue);
+
+ if (sub != null)
+ {
+ // Get the lock so we can tell if the sub scription has closed.
+ // will stop delivery to this subscription until the lock is released.
+ // note: this approach would allow the use of a single queue if the
+ // PreDeliveryQueue would allow head additions.
+ // In the Java Qpid client we are suspended whilst doing this so it is all rather Mute..
+ // needs guidance from AMQP WG Model SIG
+ synchronized (sub.getSendLock())
+ {
+ if (sub.isClosed())
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Subscription(" + System.identityHashCode(sub)
+ + ") closed during resend so requeuing message");
+ }
+ // move this message to requeue
+ msgToRequeue.add(message);
+ }
+ else
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Requeuing " + msg.debugIdentity() + " for resend via sub:"
+ + System.identityHashCode(sub));
+ }
+
+ sub.addToResendQueue(msg);
+ _unacknowledgedMessageMap.remove(message.deliveryTag);
+ }
+ } // sync(sub.getSendLock)
+ }
+ else
+ {
+
+ if (_log.isInfoEnabled())
+ {
+ _log.info("DeliveredSubscription not recorded so just requeueing(" + message.toString()
+ + ")to prevent loss");
+ }
+ // move this message to requeue
+ msgToRequeue.add(message);
+ }
+ } // for all messages
+ // } else !isSuspend
+
+ if (_log.isInfoEnabled())
+ {
+ if (!msgToRequeue.isEmpty())
+ {
+ _log.info("Preparing (" + msgToRequeue.size() + ") message to requeue to.");
+ }
+ }
+
+ // Deliver these messages out of the transaction as their delivery was never
+ // part of the transaction only the receive.
+ TransactionalContext deliveryContext;
+ if (!(_txnContext instanceof NonTransactionalContext))
+ {
+ if (_nonTransactedContext == null)
+ {
+ _nonTransactedContext =
+ new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
+ }
+
+ deliveryContext = _nonTransactedContext;
+ }
+ else
+ {
+ deliveryContext = _txnContext;
+ }
+
+ // Process Messages to Requeue at the front of the queue
+ for (UnacknowledgedMessage message : msgToRequeue)
+ {
+ message.message.release(message.queue);
+ message.message.setRedelivered(true);
+
+ deliveryContext.deliver(message.message, message.queue, true);
+
+ _unacknowledgedMessageMap.remove(message.deliveryTag);
+ }
+ }
+
+ /**
+ * Callback indicating that a queue has been deleted. We must update the structure of unacknowledged messages to
+ * remove the queue reference and also decrement any message reference counts, without actually removing the item
+ * since we may get an ack for a delivery tag that was generated from the deleted queue.
+ *
+ * @param queue the queue that has been deleted
+ *
+ * @throws org.apache.qpid.AMQException if there is an error processing the unacked messages
+ */
+ public void queueDeleted(final AMQQueue queue) throws AMQException
+ {
+ _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
+ {
+ public boolean callback(UnacknowledgedMessage message) throws AMQException
+ {
+ if (message.queue == queue)
+ {
+ try
+ {
+ message.discard(_storeContext);
+ message.queue = null;
+ }
+ catch (AMQException e)
+ {
+ _log.error(
+ "Error decrementing ref count on message " + message.message.getMessageId() + ": " + e, e);
+ }
+ }
+
+ return false;
+ }
+
+ public void visitComplete()
+ { }
+ });
+ }
+
+ /**
+ * Acknowledge one or more messages.
+ *
+ * @param deliveryTag the last delivery tag
+ * @param multiple if true will acknowledge all messages up to an including the delivery tag. if false only
+ * acknowledges the single message specified by the delivery tag
+ *
+ * @throws AMQException if the delivery tag is unknown (e.g. not outstanding) on this channel
+ */
+ public void acknowledgeMessage(long deliveryTag, boolean multiple) throws AMQException
+ {
+ synchronized (_unacknowledgedMessageMap.getLock())
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Unacked (PreAck) Size:" + _unacknowledgedMessageMap.size());
+ }
+
+ _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext);
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Unacked (PostAck) Size:" + _unacknowledgedMessageMap.size());
+ }
+
+ }
+
+ checkSuspension();
+ }
+
+ /**
+ * Used only for testing purposes.
+ *
+ * @return the map of unacknowledged messages
+ */
+ public UnacknowledgedMessageMap getUnacknowledgedMessageMap()
+ {
+ return _unacknowledgedMessageMap;
+ }
+
+ private void checkSuspension()
+ {
+ boolean suspend;
+
+ suspend =
+ ((_prefetch_HighWaterMark != 0) && (_unacknowledgedMessageMap.size() >= _prefetch_HighWaterMark))
+ || ((_prefetchSize != 0) && (_prefetchSize < _unacknowledgedMessageMap.getUnacknowledgeBytes()));
+
+ setSuspended(suspend);
+ }
+
+ public void setSuspended(boolean suspended)
+ {
+ boolean isSuspended = _suspended.get();
+
+ if (isSuspended && !suspended)
+ {
+ // Continue being suspended if we are above the _prefetch_LowWaterMark
+ suspended = _unacknowledgedMessageMap.size() > _prefetch_LowWaterMark;
+ }
+
+ boolean wasSuspended = _suspended.getAndSet(suspended);
+ if (wasSuspended != suspended)
+ {
+ if (wasSuspended)
+ {
+ _log.debug("Unsuspending channel " + this);
+ // may need to deliver queued messages
+ for (AMQQueue q : _consumerTag2QueueMap.values())
+ {
+ q.deliverAsync();
+ }
+ }
+ else
+ {
+ _log.debug("Suspending channel " + this);
+ }
+ }
+ }
+
+ public boolean isSuspended()
+ {
+ return _suspended.get();
+ }
+
+ public void commit() throws AMQException
+ {
+ if (!isTransactional())
+ {
+ throw new AMQException("Fatal error: commit called on non-transactional channel");
+ }
+
+ _txnContext.commit();
+ }
+
+ public void rollback() throws AMQException
+ {
+ _txnContext.rollback();
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(30);
+ sb.append("Channel: id ").append(_channelId).append(", transaction mode: ").append(isTransactional());
+ sb.append(", prefetch marks: ").append(_prefetch_LowWaterMark);
+ sb.append("/").append(_prefetch_HighWaterMark);
+
+ return sb.toString();
+ }
+
+ public void setDefaultQueue(AMQQueue queue)
+ {
+ _defaultQueue = queue;
+ }
+
+ public AMQQueue getDefaultQueue()
+ {
+ return _defaultQueue;
+ }
+
+ public StoreContext getStoreContext()
+ {
+ return _storeContext;
+ }
+
+ public void processReturns(AMQProtocolSession session) throws AMQException
+ {
+ for (RequiredDeliveryException bouncedMessage : _returnMessages)
+ {
+ AMQMessage message = bouncedMessage.getAMQMessage();
+ session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(),
+ new AMQShortString(bouncedMessage.getMessage()));
+
+ message.decrementReference(_storeContext);
+ }
+
+ _returnMessages.clear();
+ }
+
+ public boolean wouldSuspend(AMQMessage msg)
+ {
+ if (isSuspended())
+ {
+ return true;
+ }
+ else
+ {
+ boolean willSuspend =
+ ((_prefetch_HighWaterMark != 0) && ((_unacknowledgedMessageMap.size() + 1) > _prefetch_HighWaterMark));
+ if (!willSuspend)
+ {
+ final long unackedSize = _unacknowledgedMessageMap.getUnacknowledgeBytes();
+
+ willSuspend = (_prefetchSize != 0) && (unackedSize != 0) && (_prefetchSize < (msg.getSize() + unackedSize));
+ }
+
+ if (willSuspend)
+ {
+ setSuspended(true);
+ }
+
+ return willSuspend;
+ }
+
+ }
+
+ public TransactionalContext getTransactionalContext()
+ {
+ return _txnContext;
+ }
+
+ public boolean isClosing()
+ {
+ return _closing;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java
new file mode 100644
index 0000000000..9a98af5689
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java
@@ -0,0 +1,25 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+public class ConsumerTagNotUniqueException extends Exception
+{
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/Main.java b/Final/java/broker/src/main/java/org/apache/qpid/server/Main.java
new file mode 100644
index 0000000000..a87c704cf8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/Main.java
@@ -0,0 +1,521 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import org.apache.commons.cli.CommandLine;
+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.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+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.transport.socket.nio.SocketAcceptorConfig;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.common.QpidProperties;
+import org.apache.qpid.framing.ProtocolVersion;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+import org.apache.qpid.server.management.JMXManagedObjectRegistry;
+import org.apache.qpid.server.protocol.AMQPFastProtocolHandler;
+import org.apache.qpid.server.protocol.AMQPProtocolProvider;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry;
+import org.apache.qpid.server.transport.ConnectorConfiguration;
+import org.apache.qpid.url.URLSyntaxException;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.BindException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Main entry point for AMQPD.
+ *
+ */
+@SuppressWarnings({"AccessStaticViaInstance"})
+public class Main
+{
+ private static final Logger _logger = Logger.getLogger(Main.class);
+ public static final Logger _brokerLogger = Logger.getLogger("Qpid.Broker");
+
+ private static final String DEFAULT_CONFIG_FILE = "etc/config.xml";
+
+ private static final String DEFAULT_LOG_CONFIG_FILENAME = "log4j.xml";
+ public static final String QPID_HOME = "QPID_HOME";
+ private static final int IPV4_ADDRESS_LENGTH = 4;
+
+ private static final char IPV4_LITERAL_SEPARATOR = '.';
+
+ protected static class InitException extends Exception
+ {
+ InitException(String msg, Throwable cause)
+ {
+ super(msg, cause);
+ }
+ }
+
+ protected final Options options = new Options();
+ protected CommandLine commandLine;
+
+ protected Main(String[] args)
+ {
+ setOptions(options);
+ if (parseCommandline(args))
+ {
+ execute();
+ }
+ }
+
+ protected boolean parseCommandline(String[] args)
+ {
+ try
+ {
+ commandLine = new PosixParser().parse(options, args);
+
+ return true;
+ }
+ catch (ParseException e)
+ {
+ System.err.println("Error: " + e.getMessage());
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("Qpid", options, true);
+
+ return false;
+ }
+ }
+
+ protected void setOptions(Options options)
+ {
+ Option help = new Option("h", "help", false, "print this message");
+ Option version = new Option("v", "version", false, "print the version information and exit");
+ Option configFile =
+ OptionBuilder.withArgName("file").hasArg().withDescription("use given configuration file").withLongOpt("config")
+ .create("c");
+ Option port =
+ OptionBuilder.withArgName("port").hasArg()
+ .withDescription("listen on the specified port. Overrides any value in the config file")
+ .withLongOpt("port").create("p");
+ Option mport =
+ OptionBuilder.withArgName("mport").hasArg()
+ .withDescription("listen on the specified management port. Overrides any value in the config file")
+ .withLongOpt("mport").create("m");
+
+
+ Option bind =
+ OptionBuilder.withArgName("bind").hasArg()
+ .withDescription("bind to the specified address. Overrides any value in the config file")
+ .withLongOpt("bind").create("b");
+ Option logconfig =
+ OptionBuilder.withArgName("logconfig").hasArg()
+ .withDescription("use the specified log4j xml configuration file. By "
+ + "default looks for a file named " + DEFAULT_LOG_CONFIG_FILENAME
+ + " in the same directory as the configuration file").withLongOpt("logconfig").create("l");
+ Option logwatchconfig =
+ OptionBuilder.withArgName("logwatch").hasArg()
+ .withDescription("monitor the log file configuration file for changes. Units are seconds. "
+ + "Zero means do not check for changes.").withLongOpt("logwatch").create("w");
+
+ options.addOption(help);
+ options.addOption(version);
+ options.addOption(configFile);
+ options.addOption(logconfig);
+ options.addOption(logwatchconfig);
+ options.addOption(port);
+ options.addOption(mport);
+ options.addOption(bind);
+ }
+
+ protected void execute()
+ {
+ // note this understands either --help or -h. If an option only has a long name you can use that but if
+ // an option has a short name and a long name you must use the short name here.
+ if (commandLine.hasOption("h"))
+ {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("Qpid", options, true);
+ }
+ else if (commandLine.hasOption("v"))
+ {
+ String ver = QpidProperties.getVersionString();
+
+ StringBuilder protocol = new StringBuilder("AMQP version(s) [major.minor]: ");
+
+ boolean first = true;
+ for (ProtocolVersion pv : ProtocolVersion.getSupportedProtocolVersions())
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ protocol.append(", ");
+ }
+
+ protocol.append(pv.getMajorVersion()).append('-').append(pv.getMinorVersion());
+
+ }
+
+ System.out.println(ver + " (" + protocol + ")");
+ }
+ else
+ {
+ try
+ {
+ startup();
+ }
+ catch (InitException e)
+ {
+ System.out.println(e.getMessage());
+ _brokerLogger.error("Initialisation Error : " + e.getMessage());
+
+ }
+ catch (ConfigurationException e)
+ {
+ System.out.println("Error configuring message broker: " + e);
+ _brokerLogger.error("Error configuring message broker: " + e);
+ e.printStackTrace();
+ }
+ catch (Exception e)
+ {
+ System.out.println("Error intialising message broker: " + e);
+ _brokerLogger.error("Error intialising message broker: " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected void startup() throws InitException, ConfigurationException, Exception
+ {
+ final String QpidHome = System.getProperty(QPID_HOME);
+ final File defaultConfigFile = new File(QpidHome, DEFAULT_CONFIG_FILE);
+ final File configFile = new File(commandLine.getOptionValue("c", defaultConfigFile.getPath()));
+ if (!configFile.exists())
+ {
+ String error = "File " + configFile + " could not be found. Check the file exists and is readable.";
+
+ if (QpidHome == null)
+ {
+ error = error + "\nNote: " + QPID_HOME + " is not set.";
+ }
+
+ throw new InitException(error, null);
+ }
+ else
+ {
+ System.out.println("Using configuration file " + configFile.getAbsolutePath());
+ }
+
+ String logConfig = commandLine.getOptionValue("l");
+ String logWatchConfig = commandLine.getOptionValue("w", "0");
+ if (logConfig != null)
+ {
+ File logConfigFile = new File(logConfig);
+ configureLogging(logConfigFile, logWatchConfig);
+ }
+ else
+ {
+ File configFileDirectory = configFile.getParentFile();
+ File logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME);
+ configureLogging(logConfigFile, logWatchConfig);
+ }
+
+ ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(configFile);
+
+
+ updateManagementPort(config.getConfiguration(), commandLine.getOptionValue("m"));
+
+
+
+ ApplicationRegistry.initialise(config);
+
+
+ //fixme .. use QpidProperties.getVersionString when we have fixed the classpath issues
+ // that are causing the broker build to pick up the wrong properties file and hence say
+ // Starting Qpid Client
+ _brokerLogger.info("Starting Qpid Broker " + QpidProperties.getReleaseVersion()
+ + " build: " + QpidProperties.getBuildVersion());
+
+ ConnectorConfiguration connectorConfig =
+ ApplicationRegistry.getInstance().getConfiguredObject(ConnectorConfiguration.class);
+
+ ByteBuffer.setUseDirectBuffers(connectorConfig.enableDirectBuffers);
+
+ // the MINA default is currently to use the pooled allocator although this may change in future
+ // once more testing of the performance of the simple allocator has been done
+ if (!connectorConfig.enablePooledAllocator)
+ {
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+ }
+
+ int port = connectorConfig.port;
+
+ String portStr = commandLine.getOptionValue("p");
+ if (portStr != null)
+ {
+ try
+ {
+ port = Integer.parseInt(portStr);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new InitException("Invalid port: " + portStr, e);
+ }
+ }
+
+ String VIRTUAL_HOSTS = "virtualhosts";
+
+ Object virtualHosts = ApplicationRegistry.getInstance().getConfiguration().getProperty(VIRTUAL_HOSTS);
+
+ if (virtualHosts != null)
+ {
+ if (virtualHosts instanceof Collection)
+ {
+ int totalVHosts = ((Collection) virtualHosts).size();
+ for (int vhost = 0; vhost < totalVHosts; vhost++)
+ {
+ setupVirtualHosts(configFile.getParent(), (String) ((List) virtualHosts).get(vhost));
+ }
+ }
+ else
+ {
+ setupVirtualHosts(configFile.getParent(), (String) virtualHosts);
+ }
+ }
+
+ bind(port, connectorConfig);
+
+ }
+
+ /**
+ * Update the configuration data with the management port.
+ * @param configuration
+ * @param managementPort The string from the command line
+ */
+ private void updateManagementPort(Configuration configuration, String managementPort)
+ {
+ if (managementPort != null)
+ {
+ int mport;
+ int defaultMPort = configuration.getInt(JMXManagedObjectRegistry.MANAGEMENT_PORT_CONFIG_PATH);
+ try
+ {
+ mport = Integer.parseInt(managementPort);
+ configuration.setProperty(JMXManagedObjectRegistry.MANAGEMENT_PORT_CONFIG_PATH, mport);
+ }
+ catch (NumberFormatException e)
+ {
+ _logger.warn("Invalid management port: " + managementPort + " will use default:" + defaultMPort, e);
+ }
+ }
+ }
+
+ protected void setupVirtualHosts(String configFileParent, String configFilePath)
+ throws ConfigurationException, AMQException, URLSyntaxException
+ {
+ String configVar = "${conf}";
+
+ if (configFilePath.startsWith(configVar))
+ {
+ configFilePath = configFileParent + configFilePath.substring(configVar.length());
+ }
+
+ if (configFilePath.indexOf(".xml") != -1)
+ {
+ VirtualHostConfiguration vHostConfig = new VirtualHostConfiguration(configFilePath);
+ vHostConfig.performBindings();
+ }
+ else
+ {
+ // the virtualhosts value is a path. Search it for XML files.
+
+ File virtualHostDir = new File(configFilePath);
+
+ String[] fileNames = virtualHostDir.list();
+
+ for (int each = 0; each < fileNames.length; each++)
+ {
+ if (fileNames[each].endsWith(".xml"))
+ {
+ VirtualHostConfiguration vHostConfig =
+ new VirtualHostConfiguration(configFilePath + "/" + fileNames[each]);
+ vHostConfig.performBindings();
+ }
+ }
+ }
+ }
+
+ protected void bind(int port, ConnectorConfiguration connectorConfig) throws BindException
+ {
+ String bindAddr = commandLine.getOptionValue("b");
+ if (bindAddr == null)
+ {
+ bindAddr = connectorConfig.bindAddress;
+ }
+
+ try
+ {
+ // IoAcceptor acceptor = new SocketAcceptor(connectorConfig.processors);
+ IoAcceptor acceptor = connectorConfig.createAcceptor();
+ SocketAcceptorConfig sconfig = (SocketAcceptorConfig) acceptor.getDefaultConfig();
+ SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig();
+
+ sc.setReceiveBufferSize(connectorConfig.socketReceiveBufferSize);
+ sc.setSendBufferSize(connectorConfig.socketWriteBuferSize);
+ sc.setTcpNoDelay(connectorConfig.tcpNoDelay);
+
+ // 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());
+ }
+
+ if (!connectorConfig.enableSSL || !connectorConfig.sslOnly)
+ {
+ AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler();
+ InetSocketAddress bindAddress;
+ if (bindAddr.equals("wildcard"))
+ {
+ bindAddress = new InetSocketAddress(port);
+ }
+ else
+ {
+ bindAddress = new InetSocketAddress(InetAddress.getByAddress(parseIP(bindAddr)), port);
+ }
+
+ acceptor.bind(bindAddress, handler, sconfig);
+ //fixme qpid.AMQP should be using qpidproperties to get value
+ _brokerLogger.info("Qpid.AMQP listening on non-SSL address " + bindAddress);
+ }
+
+ if (connectorConfig.enableSSL)
+ {
+ AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler();
+ try
+ {
+
+ acceptor.bind(new InetSocketAddress(connectorConfig.sslPort), handler, sconfig);
+ //fixme qpid.AMQP should be using qpidproperties to get value
+ _brokerLogger.info("Qpid.AMQP listening on SSL port " + connectorConfig.sslPort);
+
+ }
+ catch (IOException e)
+ {
+ _brokerLogger.error("Unable to listen on SSL port: " + e, e);
+ }
+ }
+
+ //fixme qpid.AMQP should be using qpidproperties to get value
+ _brokerLogger.info("Qpid Broker Ready :" + QpidProperties.getReleaseVersion()
+ + " build: " + QpidProperties.getBuildVersion());
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to bind service to registry: " + e, e);
+ //fixme this need tidying up
+ throw new BindException(e.getMessage());
+ }
+ }
+
+ public static void main(String[] args)
+ {
+
+ new Main(args);
+ }
+
+ private byte[] parseIP(String address) throws Exception
+ {
+ char[] literalBuffer = address.toCharArray();
+ int byteCount = 0;
+ int currByte = 0;
+ byte[] ip = new byte[IPV4_ADDRESS_LENGTH];
+ for (int i = 0; i < literalBuffer.length; i++)
+ {
+ char currChar = literalBuffer[i];
+ if ((currChar >= '0') && (currChar <= '9'))
+ {
+ currByte = (currByte * 10) + (Character.digit(currChar, 10) & 0xFF);
+ }
+
+ if (currChar == IPV4_LITERAL_SEPARATOR || (i + 1 == literalBuffer.length))
+ {
+ ip[byteCount++] = (byte) currByte;
+ currByte = 0;
+ }
+ }
+
+ if (byteCount != 4)
+ {
+ throw new Exception("Invalid IP address: " + address);
+ }
+ return ip;
+ }
+
+ private void configureLogging(File logConfigFile, String logWatchConfig)
+ {
+ int logWatchTime = 0;
+ try
+ {
+ logWatchTime = Integer.parseInt(logWatchConfig);
+ }
+ catch (NumberFormatException e)
+ {
+ System.err.println("Log watch configuration value of " + logWatchConfig + " is invalid. Must be "
+ + "a non-negative integer. Using default of zero (no watching configured");
+ }
+
+ if (logConfigFile.exists() && logConfigFile.canRead())
+ {
+ System.out.println("Configuring logger using configuration file " + logConfigFile.getAbsolutePath());
+ if (logWatchTime > 0)
+ {
+ System.out.println("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every "
+ + logWatchTime + " seconds");
+ // log4j expects the watch interval in milliseconds
+ DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), logWatchTime * 1000);
+ }
+ else
+ {
+ DOMConfigurator.configure(logConfigFile.getAbsolutePath());
+ }
+ }
+ else
+ {
+ System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath());
+ System.err.println("Using basic log4j configuration");
+ BasicConfigurator.configure();
+ }
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java b/Final/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java
new file mode 100644
index 0000000000..e76f9c3f6c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.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;
+
+import java.io.IOException;
+
+import javax.management.JMException;
+
+/**
+ * The managed interface exposed to allow management of channels.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedChannel
+{
+ static final String TYPE = "Channel";
+
+ /**
+ * Tells whether the channel is transactional.
+ * @return true if the channel is transactional.
+ * @throws IOException
+ */
+ boolean isTransactional() throws IOException;
+
+ /**
+ * Tells the number of unacknowledged messages in this channel.
+ * @return number of unacknowledged messages.
+ * @throws IOException
+ */
+ int getUnacknowledgedMessageCount() throws IOException;
+
+
+ //********** Operations *****************//
+
+ /**
+ * Commits the transactions if the channel is transactional.
+ * @throws IOException
+ * @throws JMException
+ */
+ void commitTransactions() throws IOException, JMException;
+
+ /**
+ * Rollsback the transactions if the channel is transactional.
+ * @throws IOException
+ * @throws JMException
+ */
+ void rollbackTransactions() throws IOException, JMException;
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java
new file mode 100644
index 0000000000..d61bb8916a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.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;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * Signals that a required delivery could not be made. This could be bacuse of the immediate flag being set and the
+ * queue having no consumers, or the mandatory flag being set and the exchange having no valid bindings.
+ *
+ * <p/>The failed message is associated with this error condition, by taking a reference to it. This enables the
+ * correct compensating action to be taken against the message, for example, bouncing it back to the sender.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to deliver a message that must be delivered.
+ * <tr><td> Associate the failed message with the error condition. <td> {@link AMQMessage}
+ * </table>
+ */
+public abstract class RequiredDeliveryException extends AMQException
+{
+ private final AMQMessage _amqMessage;
+
+ public RequiredDeliveryException(String message, AMQMessage payload)
+ {
+ super(message);
+
+ // Increment the reference as this message is in the routing phase
+ // and so will have the ref decremented as routing fails.
+ // we need to keep this message around so we can return it in the
+ // handler. So increment here.
+ _amqMessage = payload.takeReference();
+
+ // payload.incrementReference();
+ }
+
+ public AMQMessage getAMQMessage()
+ {
+ return _amqMessage;
+ }
+
+ public AMQConstant getErrorCode()
+ {
+ return getReplyCode();
+ }
+
+ public abstract AMQConstant getReplyCode();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
new file mode 100644
index 0000000000..5ca8d57f7c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
@@ -0,0 +1,139 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.ack;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.txn.TxnOp;
+
+/**
+ * A TxnOp implementation for handling accumulated acks
+ */
+public class TxAck implements TxnOp
+{
+ private final UnacknowledgedMessageMap _map;
+ private final List <UnacknowledgedMessage> _unacked = new LinkedList<UnacknowledgedMessage>();
+ private final List<Long> _individual = new LinkedList<Long>();
+ private long _deliveryTag;
+ private boolean _multiple;
+
+ public TxAck(UnacknowledgedMessageMap map)
+ {
+ _map = map;
+ }
+
+ public void update(long deliveryTag, boolean multiple)
+ {
+ if (!multiple)
+ {
+ //have acked a single message that is not part of
+ //the previously acked region so record
+ //individually
+ _individual.add(deliveryTag);//_multiple && !multiple
+ }
+ else if (deliveryTag > _deliveryTag)
+ {
+ //have simply moved the last acked message on a
+ //bit
+ _deliveryTag = deliveryTag;
+ _multiple = true;
+ }
+ }
+
+ public void consolidate()
+ {
+ //lookup all the unacked messages that have been acked in this transaction
+ if (_multiple)
+ {
+ //get all the unacked messages for the accumulated
+ //multiple acks
+ _map.collect(_deliveryTag, true, _unacked);
+ }
+ //get any unacked messages for individual acks outside the
+ //range covered by multiple acks
+ for (long tag : _individual)
+ {
+ if(_deliveryTag < tag)
+ {
+ _map.collect(tag, false, _unacked);
+ }
+ }
+ }
+
+ public boolean checkPersistent() throws AMQException
+ {
+ //if any of the messages in unacked are persistent the txn
+ //buffer must be marked as persistent:
+ for (UnacknowledgedMessage msg : _unacked)
+ {
+ if (msg.message.isPersistent())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void prepare(StoreContext storeContext) throws AMQException
+ {
+ //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);
+ }
+ }
+
+ public void undoPrepare()
+ {
+ //decrementReference is annoyingly untransactional (due to
+ //in memory counter) so if we failed in prepare for full
+ //txn, this op will have to compensate by fixing the count
+ //in memory (persistent changes will be rolled back by store)
+ for (UnacknowledgedMessage msg : _unacked)
+ {
+ msg.clearTransientMessageData();
+ msg.message.takeReference();
+ }
+ }
+
+ public void commit(StoreContext storeContext)
+ {
+ //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/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java
new file mode 100644
index 0000000000..7088c704ed
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.ack;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.store.StoreContext;
+
+public class UnacknowledgedMessage
+{
+ public final AMQMessage message;
+ public final AMQShortString consumerTag;
+ public final long deliveryTag;
+ public AMQQueue queue;
+
+ public UnacknowledgedMessage(AMQQueue queue, AMQMessage message, AMQShortString consumerTag, long deliveryTag)
+ {
+ this.queue = queue;
+ this.message = message;
+ this.consumerTag = consumerTag;
+ this.deliveryTag = deliveryTag;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Q:");
+ sb.append(queue);
+ sb.append(" M:");
+ sb.append(message);
+ sb.append(" CT:");
+ sb.append(consumerTag);
+ sb.append(" DT:");
+ sb.append(deliveryTag);
+
+ return sb.toString();
+ }
+
+ public void discard(StoreContext storeContext) throws AMQException
+ {
+ if (queue != null)
+ {
+ queue.dequeue(storeContext, message);
+ }
+ //if the queue is null then the message is waiting to be acked, but has been removed.
+ message.decrementReference(storeContext);
+ }
+
+ public void restoreTransientMessageData() throws AMQException
+ {
+ message.restoreTransientMessageData();
+ }
+
+ public void clearTransientMessageData()
+ {
+ message.clearTransientMessageData();
+ }
+}
+
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java
new file mode 100644
index 0000000000..b69a917081
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.ack;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.txn.TransactionalContext;
+
+public interface UnacknowledgedMessageMap
+{
+ public interface Visitor
+ {
+ /**
+ * @param message the message being iterated over
+ * @return true to stop iteration, false to continue
+ * @throws AMQException
+ */
+ boolean callback(UnacknowledgedMessage message) throws AMQException;
+
+ void visitComplete();
+ }
+
+ void visit(Visitor visitor) throws AMQException;
+
+ Object getLock();
+
+ void add(long deliveryTag, UnacknowledgedMessage message);
+
+ void collect(long deliveryTag, boolean multiple, List<UnacknowledgedMessage> msgs);
+
+ boolean contains(long deliveryTag) throws AMQException;
+
+ void remove(List<UnacknowledgedMessage> msgs);
+
+ UnacknowledgedMessage remove(long deliveryTag);
+
+ void drainTo(Collection<UnacknowledgedMessage> destination, long deliveryTag) throws AMQException;
+
+ Collection<UnacknowledgedMessage> cancelAllMessages();
+
+ void acknowledgeMessage(long deliveryTag, boolean multiple, TransactionalContext txnContext) throws AMQException;
+
+ int size();
+
+ void clear();
+
+ UnacknowledgedMessage get(long deliveryTag);
+
+ /**
+ * Get the set of delivery tags that are outstanding.
+ *
+ * @return a set of delivery tags
+ */
+ Set<Long> getDeliveryTags();
+
+ public long getUnacknowledgeBytes();
+}
+
+
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java
new file mode 100644
index 0000000000..30bbdea2ef
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java
@@ -0,0 +1,235 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.ack;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.txn.TransactionalContext;
+
+public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
+{
+ private final Object _lock = new Object();
+
+ private long _unackedSize;
+
+ private Map<Long, UnacknowledgedMessage> _map;
+
+ private long _lastDeliveryTag;
+
+ private final int _prefetchLimit;
+
+ public UnacknowledgedMessageMapImpl(int prefetchLimit)
+ {
+ _prefetchLimit = prefetchLimit;
+ _map = new LinkedHashMap<Long, UnacknowledgedMessage>(prefetchLimit);
+ }
+
+ /*public UnacknowledgedMessageMapImpl(Object lock, Map<Long, UnacknowledgedMessage> map)
+ {
+ _lock = lock;
+ _map = map;
+ } */
+
+ public void collect(long deliveryTag, boolean multiple, List<UnacknowledgedMessage> msgs)
+ {
+ if (multiple)
+ {
+ collect(deliveryTag, msgs);
+ }
+ else
+ {
+ msgs.add(get(deliveryTag));
+ }
+
+ }
+
+ public boolean contains(long deliveryTag) throws AMQException
+ {
+ synchronized (_lock)
+ {
+ return _map.containsKey(deliveryTag);
+ }
+ }
+
+ public void remove(List<UnacknowledgedMessage> msgs)
+ {
+ synchronized (_lock)
+ {
+ for (UnacknowledgedMessage msg : msgs)
+ {
+ remove(msg.deliveryTag);
+ }
+ }
+ }
+
+ public UnacknowledgedMessage remove(long deliveryTag)
+ {
+ synchronized (_lock)
+ {
+
+ UnacknowledgedMessage message = _map.remove(deliveryTag);
+ if(message != null)
+ {
+ _unackedSize -= message.message.getSize();
+ }
+
+ return message;
+ }
+ }
+
+ public void visit(Visitor visitor) throws AMQException
+ {
+ synchronized (_lock)
+ {
+ Collection<UnacknowledgedMessage> currentEntries = _map.values();
+ for (UnacknowledgedMessage msg : currentEntries)
+ {
+ visitor.callback(msg);
+ }
+ visitor.visitComplete();
+ }
+ }
+
+ public Object getLock()
+ {
+ return _lock;
+ }
+
+ public void add(long deliveryTag, UnacknowledgedMessage message)
+ {
+ synchronized (_lock)
+ {
+ _map.put(deliveryTag, message);
+ _unackedSize += message.message.getSize();
+ _lastDeliveryTag = deliveryTag;
+ }
+ }
+
+ public Collection<UnacknowledgedMessage> cancelAllMessages()
+ {
+ synchronized (_lock)
+ {
+ Collection<UnacknowledgedMessage> currentEntries = _map.values();
+ _map = new LinkedHashMap<Long, UnacknowledgedMessage>(_prefetchLimit);
+ _unackedSize = 0l;
+ return currentEntries;
+ }
+ }
+
+ public void acknowledgeMessage(long deliveryTag, boolean multiple, TransactionalContext txnContext)
+ throws AMQException
+ {
+ synchronized (_lock)
+ {
+ txnContext.acknowledgeMessage(deliveryTag, _lastDeliveryTag, multiple, this);
+ }
+ }
+
+ public int size()
+ {
+ synchronized (_lock)
+ {
+ return _map.size();
+ }
+ }
+
+ public void clear()
+ {
+ synchronized (_lock)
+ {
+ _map.clear();
+ _unackedSize = 0l;
+ }
+ }
+
+ public void drainTo(Collection<UnacknowledgedMessage> destination, long deliveryTag) throws AMQException
+ {
+ synchronized (_lock)
+ {
+ Iterator<Map.Entry<Long, UnacknowledgedMessage>> it = _map.entrySet().iterator();
+ while (it.hasNext())
+ {
+ Map.Entry<Long, UnacknowledgedMessage> unacked = it.next();
+
+ if (unacked.getKey() > deliveryTag)
+ {
+ //This should not occur now.
+ throw new AMQException("UnacknowledgedMessageMap is out of order:" + unacked.getKey() +
+ " When deliveryTag is:" + deliveryTag + "ES:" + _map.entrySet().toString());
+ }
+
+ it.remove();
+ _unackedSize -= unacked.getValue().message.getSize();
+
+ destination.add(unacked.getValue());
+ if (unacked.getKey() == deliveryTag)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ public UnacknowledgedMessage get(long key)
+ {
+ synchronized (_lock)
+ {
+ return _map.get(key);
+ }
+ }
+
+ public Set<Long> getDeliveryTags()
+ {
+ synchronized (_lock)
+ {
+ return _map.keySet();
+ }
+ }
+
+ private void collect(long key, List<UnacknowledgedMessage> msgs)
+ {
+ synchronized (_lock)
+ {
+ for (Map.Entry<Long, UnacknowledgedMessage> entry : _map.entrySet())
+ {
+ msgs.add(entry.getValue());
+ if (entry.getKey() == key)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ public long getUnacknowledgeBytes()
+ {
+ return _unackedSize;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java b/Final/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java
new file mode 100644
index 0000000000..31c1b61a21
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java
@@ -0,0 +1,118 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.configuration;
+
+import java.lang.reflect.Field;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.configuration.PropertyException;
+import org.apache.qpid.configuration.PropertyUtils;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+/**
+ * This class contains utilities for populating classes automatically from values pulled from configuration
+ * files.
+ */
+public class Configurator
+{
+ private static final Logger _logger = Logger.getLogger(Configurator.class);
+
+
+ /**
+ * Configure a given instance using the supplied configuration. Note that superclasses are <b>not</b>
+ * currently configured but this could easily be added if required.
+ * @param instance the instance to configure
+ * @param config the configuration to use to configure the object
+ */
+ public static void configure(Object instance, Configuration config)
+ {
+
+ for (Field f : instance.getClass().getDeclaredFields())
+ {
+ Configured annotation = f.getAnnotation(Configured.class);
+ if (annotation != null)
+ {
+ setValueInField(f, instance, config, annotation);
+ }
+ }
+ }
+
+
+
+ /**
+ * Configure a given instance using the application configuration. Note that superclasses are <b>not</b>
+ * currently configured but this could easily be added if required.
+ * @param instance the instance to configure
+ */
+ public static void configure(Object instance)
+ {
+ configure(instance, ApplicationRegistry.getInstance().getConfiguration());
+ }
+
+ private static void setValueInField(Field f, Object instance, Configuration config, Configured annotation)
+ {
+ Class fieldClass = f.getType();
+ String configPath = annotation.path();
+ try
+ {
+ if (fieldClass == String.class)
+ {
+ String val = config.getString(configPath, annotation.defaultValue());
+ val = PropertyUtils.replaceProperties(val);
+ f.set(instance, val);
+ }
+ else if (fieldClass == int.class)
+ {
+ int val = config.getInt(configPath, Integer.parseInt(annotation.defaultValue()));
+ f.setInt(instance, val);
+ }
+ else if (fieldClass == long.class)
+ {
+ long val = config.getLong(configPath, Long.parseLong(annotation.defaultValue()));
+ f.setLong(instance, val);
+ }
+ else if (fieldClass == double.class)
+ {
+ double val = config.getDouble(configPath, Double.parseDouble(annotation.defaultValue()));
+ f.setDouble(instance, val);
+ }
+ else if (fieldClass == boolean.class)
+ {
+ boolean val = config.getBoolean(configPath, Boolean.parseBoolean(annotation.defaultValue()));
+ f.setBoolean(instance, val);
+ }
+ else
+ {
+ _logger.error("Unsupported field type " + fieldClass + " for " + f + " IGNORING configured value");
+ }
+ }
+ catch (PropertyException e)
+ {
+ _logger.error("Unable to expand property: " + e + " INGORING field " + f, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ _logger.error("Unable to access field " + f + " IGNORING configured value");
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/Final/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
new file mode 100644
index 0000000000..8573902af4
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
@@ -0,0 +1,269 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.configuration;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class VirtualHostConfiguration
+{
+ private static final Logger _logger = Logger.getLogger(VirtualHostConfiguration.class);
+
+ private static XMLConfiguration _config;
+
+ private static final String VIRTUALHOST_PROPERTY_BASE = "virtualhost.";
+
+
+ public VirtualHostConfiguration(String configFile) throws ConfigurationException
+ {
+ _logger.info("Loading Config file:" + configFile);
+
+ _config = new XMLConfiguration(configFile);
+
+ }
+
+
+
+ private void configureVirtualHost(String virtualHostName, Configuration configuration) throws ConfigurationException, AMQException
+ {
+ _logger.debug("Loding configuration for virtaulhost: "+virtualHostName);
+
+
+ VirtualHost virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(virtualHostName);
+
+
+
+ if(virtualHost == null)
+ {
+ throw new ConfigurationException("Unknown virtual host: " + virtualHostName);
+ }
+
+ List exchangeNames = configuration.getList("exchanges.exchange.name");
+
+ for(Object exchangeNameObj : exchangeNames)
+ {
+ String exchangeName = String.valueOf(exchangeNameObj);
+ configureExchange(virtualHost, exchangeName, configuration);
+ }
+
+
+ List queueNames = configuration.getList("queues.queue.name");
+
+ for(Object queueNameObj : queueNames)
+ {
+ String queueName = String.valueOf(queueNameObj);
+ configureQueue(virtualHost, queueName, configuration);
+ }
+
+ }
+
+ private void configureExchange(VirtualHost virtualHost, String exchangeNameString, Configuration configuration) throws AMQException
+ {
+
+ CompositeConfiguration exchangeConfiguration = new CompositeConfiguration();
+
+ exchangeConfiguration.addConfiguration(configuration.subset("exchanges.exchange."+ exchangeNameString));
+ exchangeConfiguration.addConfiguration(configuration.subset("exchanges"));
+
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+ MessageStore messageStore = virtualHost.getMessageStore();
+ ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+ ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory();
+
+ AMQShortString exchangeName = new AMQShortString(exchangeNameString);
+
+
+ Exchange exchange;
+
+
+
+ synchronized (exchangeRegistry)
+ {
+ exchange = exchangeRegistry.getExchange(exchangeName);
+ if(exchange == null)
+ {
+
+ AMQShortString type = new AMQShortString(exchangeConfiguration.getString("type","direct"));
+ boolean durable = exchangeConfiguration.getBoolean("durable",false);
+ boolean autodelete = exchangeConfiguration.getBoolean("autodelete",false);
+
+ Exchange newExchange = exchangeFactory.createExchange(exchangeName,type,durable,autodelete,0);
+ exchangeRegistry.registerExchange(newExchange);
+ }
+
+ }
+ }
+
+ public static CompositeConfiguration getDefaultQueueConfiguration(AMQQueue queue)
+ {
+ CompositeConfiguration queueConfiguration = null;
+ if (_config == null)
+ return null;
+
+ Configuration vHostConfiguration = _config.subset(VIRTUALHOST_PROPERTY_BASE + queue.getVirtualHost().getName());
+
+ if (vHostConfiguration == null)
+ return null;
+
+ Configuration defaultQueueConfiguration = vHostConfiguration.subset("queues");
+ if (defaultQueueConfiguration != null)
+ {
+ queueConfiguration = new CompositeConfiguration();
+ queueConfiguration.addConfiguration(defaultQueueConfiguration);
+ }
+
+ return queueConfiguration;
+ }
+
+ private void configureQueue(VirtualHost virtualHost, String queueNameString, Configuration configuration) throws AMQException, ConfigurationException
+ {
+ CompositeConfiguration queueConfiguration = new CompositeConfiguration();
+
+ queueConfiguration.addConfiguration(configuration.subset("queues.queue."+ queueNameString));
+ queueConfiguration.addConfiguration(configuration.subset("queues"));
+
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+ MessageStore messageStore = virtualHost.getMessageStore();
+ ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+
+
+ AMQShortString queueName = new AMQShortString(queueNameString);
+
+ AMQQueue queue;
+
+ synchronized (queueRegistry)
+ {
+ queue = queueRegistry.getQueue(queueName);
+
+ if (queue == null)
+ {
+ _logger.info("Creating queue '" + queueName + "' on virtual host " + virtualHost.getName());
+
+ boolean durable = queueConfiguration.getBoolean("durable" ,false);
+ boolean autodelete = queueConfiguration.getBoolean("autodelete", false);
+ String owner = queueConfiguration.getString("owner", null);
+
+ queue = new AMQQueue(queueName,
+ durable,
+ owner == null ? null : new AMQShortString(owner) /* These queues will have no owner */,
+ autodelete /* Therefore autodelete makes no sence */, virtualHost);
+
+ if (queue.isDurable())
+ {
+ messageStore.createQueue(queue);
+ }
+
+ queueRegistry.registerQueue(queue);
+ }
+ else
+ {
+ _logger.info("Queue '" + queueNameString + "' already exists on virtual host "+virtualHost.getName()+", not creating.");
+ }
+
+ String exchangeName = queueConfiguration.getString("exchange", null);
+
+ Exchange exchange = exchangeRegistry.getExchange(exchangeName == null ? null : new AMQShortString(exchangeName));
+
+ if(exchange == null)
+ {
+ exchange = virtualHost.getExchangeRegistry().getDefaultExchange();
+ }
+
+ if (exchange == null)
+ {
+ throw new ConfigurationException("Attempt to bind queue to unknown exchange:" + exchangeName);
+ }
+
+ synchronized (exchange)
+ {
+ List routingKeys = queueConfiguration.getList("routingKey");
+ if(routingKeys == null || routingKeys.isEmpty())
+ {
+ routingKeys = Collections.singletonList(queue.getName());
+ }
+
+ for(Object routingKeyNameObj : routingKeys)
+ {
+ AMQShortString routingKey = new AMQShortString(String.valueOf(routingKeyNameObj));
+
+
+ queue.bind(routingKey, null, exchange);
+
+
+ _logger.info("Queue '" + queue.getName() + "' bound to exchange:" + exchangeName + " RK:'" + routingKey + "'");
+ }
+
+ if(exchange != virtualHost.getExchangeRegistry().getDefaultExchange())
+ {
+ queue.bind(queue.getName(), null, virtualHost.getExchangeRegistry().getDefaultExchange());
+ }
+ }
+
+ }
+
+
+
+ Configurator.configure(queue, queueConfiguration);
+ }
+
+
+ public void performBindings() throws AMQException, ConfigurationException
+ {
+ List virtualHostNames = _config.getList(VIRTUALHOST_PROPERTY_BASE + "name");
+ String defaultVirtualHostName = _config.getString("default");
+ if(defaultVirtualHostName != null)
+ {
+ ApplicationRegistry.getInstance().getVirtualHostRegistry().setDefaultVirtualHostName(defaultVirtualHostName);
+ }
+ _logger.info("Configuring " + virtualHostNames == null ? 0 : virtualHostNames.size() + " virtual hosts: " + virtualHostNames);
+
+ for(Object nameObject : virtualHostNames)
+ {
+ String name = String.valueOf(nameObject);
+ configureVirtualHost(name, _config.subset(VIRTUALHOST_PROPERTY_BASE + name));
+ }
+
+ if (virtualHostNames == null || virtualHostNames.isEmpty())
+ {
+ throw new ConfigurationException(
+ "Virtualhost Configuration document does not contain a valid virtualhost.");
+ }
+ }
+
+
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java
new file mode 100644
index 0000000000..9ebb893362
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java
@@ -0,0 +1,217 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.TabularType;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.ArrayType;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.management.AMQManagedObject;
+import org.apache.qpid.server.management.Managable;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+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.virtualhost.VirtualHost;
+
+import java.util.List;
+import java.util.Map;
+
+public abstract class AbstractExchange implements Exchange, Managable
+{
+ private AMQShortString _name;
+
+
+
+ protected boolean _durable;
+ protected String _exchangeType;
+ protected int _ticket;
+
+ private VirtualHost _virtualHost;
+
+ protected ExchangeMBean _exchangeMbean;
+
+ /**
+ * Whether the exchange is automatically deleted once all queues have detached from it
+ */
+ protected boolean _autoDelete;
+
+ /**
+ * 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.
+ */
+ protected abstract class ExchangeMBean extends AMQManagedObject implements ManagedExchange
+ {
+ // open mbean data types for representing exchange bindings
+ protected String[] _bindingItemNames;
+ protected String[] _bindingItemIndexNames;
+ protected OpenType[] _bindingItemTypes;
+ protected CompositeType _bindingDataType;
+ protected TabularType _bindinglistDataType;
+ protected TabularDataSupport _bindingList;
+
+ public ExchangeMBean() throws NotCompliantMBeanException
+ {
+ super(ManagedExchange.class, ManagedExchange.TYPE);
+ }
+
+ protected void init() throws OpenDataException
+ {
+ _bindingItemNames = new String[]{"Binding Key", "Queue Names"};
+ _bindingItemIndexNames = new String[]{_bindingItemNames[0]};
+
+ _bindingItemTypes = new OpenType[2];
+ _bindingItemTypes[0] = SimpleType.STRING;
+ _bindingItemTypes[1] = new ArrayType(1, SimpleType.STRING);
+ _bindingDataType = new CompositeType("Exchange Binding", "Binding key and Queue names",
+ _bindingItemNames, _bindingItemNames, _bindingItemTypes);
+ _bindinglistDataType = new TabularType("Exchange Bindings", "Exchange Bindings for " + getName(),
+ _bindingDataType, _bindingItemIndexNames);
+ }
+
+ public ManagedObject getParentObject()
+ {
+ return _virtualHost.getManagedObject();
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _name.toString();
+ }
+
+ public String getName()
+ {
+ return _name.toString();
+ }
+
+ public String getExchangeType()
+ {
+ return _exchangeType;
+ }
+
+ public Integer getTicketNo()
+ {
+ return _ticket;
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ // Added exchangetype in the object name lets maangement apps to do any customization required
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ String objNameString = super.getObjectName().toString();
+ objNameString = objNameString + ",ExchangeType=" + _exchangeType;
+ return new ObjectName(objNameString);
+ }
+
+ protected ManagedObjectRegistry getManagedObjectRegistry()
+ {
+ return ApplicationRegistry.getInstance().getManagedObjectRegistry();
+ }
+ } // End of MBean class
+
+ public AMQShortString getName()
+ {
+ return _name;
+ }
+
+ /**
+ * Concrete exchanges must implement this method in order to create the managed representation. This is
+ * called during initialisation (template method pattern).
+ * @return the MBean
+ */
+ protected abstract ExchangeMBean createMBean() throws AMQException;
+
+ public void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException
+ {
+ _virtualHost = host;
+ _name = name;
+ _durable = durable;
+ _autoDelete = autoDelete;
+ _ticket = ticket;
+ _exchangeMbean = createMBean();
+ _exchangeMbean.register();
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ public int getTicket()
+ {
+ return _ticket;
+ }
+
+ public void close() throws AMQException
+ {
+ if (_exchangeMbean != null)
+ {
+ _exchangeMbean.unregister();
+ }
+ }
+
+ abstract public Map<AMQShortString, List<AMQQueue>> getBindings();
+
+ public String toString()
+ {
+ return getClass().getName() + "[" + getName() +"]";
+ }
+
+ public ManagedObject getManagedObject()
+ {
+ return _exchangeMbean;
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public QueueRegistry getQueueRegistry()
+ {
+ return getVirtualHost().getQueueRegistry();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
new file mode 100644
index 0000000000..c349b44d6d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQUnknownExchangeType;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class DefaultExchangeFactory implements ExchangeFactory
+{
+ private static final Logger _logger = Logger.getLogger(DefaultExchangeFactory.class);
+
+ private Map<AMQShortString, Class<? extends Exchange>> _exchangeClassMap = new HashMap<AMQShortString, Class<? extends Exchange>>();
+ private final VirtualHost _host;
+
+ public DefaultExchangeFactory(VirtualHost host)
+ {
+ _host = host;
+ _exchangeClassMap.put(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, org.apache.qpid.server.exchange.DestNameExchange.class);
+ _exchangeClassMap.put(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, org.apache.qpid.server.exchange.DestWildExchange.class);
+ _exchangeClassMap.put(ExchangeDefaults.HEADERS_EXCHANGE_CLASS, org.apache.qpid.server.exchange.HeadersExchange.class);
+ _exchangeClassMap.put(ExchangeDefaults.FANOUT_EXCHANGE_CLASS, org.apache.qpid.server.exchange.FanoutExchange.class);
+
+ }
+
+ public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete,
+ int ticket)
+ throws AMQException
+ {
+ Class<? extends Exchange> exchClass = _exchangeClassMap.get(type);
+ if (exchClass == null)
+ {
+
+ throw new AMQUnknownExchangeType("Unknown exchange type: " + type);
+ }
+ try
+ {
+ Exchange e = exchClass.newInstance();
+ e.initialise(_host, exchange, durable, ticket, autoDelete);
+ return e;
+ }
+ catch (InstantiationException e)
+ {
+ throw new AMQException("Unable to create exchange: " + e, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AMQException("Unable to create exchange: " + e, e);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java
new file mode 100644
index 0000000000..98abf7977a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java
@@ -0,0 +1,138 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.protocol.ExchangeInitialiser;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class DefaultExchangeRegistry implements ExchangeRegistry
+{
+ private static final Logger _log = Logger.getLogger(DefaultExchangeRegistry.class);
+
+ /**
+ * Maps from exchange name to exchange instance
+ */
+ private ConcurrentMap<AMQShortString, Exchange> _exchangeMap = new ConcurrentHashMap<AMQShortString, Exchange>();
+
+ private Exchange _defaultExchange;
+ private VirtualHost _host;
+
+ public DefaultExchangeRegistry(VirtualHost host)
+ {
+ //create 'standard' exchanges:
+ _host = host;
+
+ }
+
+ public void initialise() throws AMQException
+ {
+ new ExchangeInitialiser().initialise(_host.getExchangeFactory(), this);
+ }
+
+ public MessageStore getMessageStore()
+ {
+ return _host.getMessageStore();
+ }
+
+ public void registerExchange(Exchange exchange) throws AMQException
+ {
+ _exchangeMap.put(exchange.getName(), exchange);
+ if (exchange.isDurable())
+ {
+ getMessageStore().createExchange(exchange);
+ }
+ }
+
+ public void setDefaultExchange(Exchange exchange)
+ {
+ _defaultExchange = exchange;
+ }
+
+ public Exchange getDefaultExchange()
+ {
+ return _defaultExchange;
+ }
+
+ public Collection<AMQShortString> getExchangeNames()
+ {
+ return _exchangeMap.keySet();
+ }
+
+ public void unregisterExchange(AMQShortString name, boolean inUse) throws AMQException
+ {
+ // TODO: check inUse argument
+ Exchange e = _exchangeMap.remove(name);
+ if (e != null)
+ {
+ if (e.isDurable())
+ {
+ getMessageStore().removeExchange(e);
+ }
+ e.close();
+ }
+ else
+ {
+ throw new AMQException("Unknown exchange " + name);
+ }
+ }
+
+ public Exchange getExchange(AMQShortString name)
+ {
+ if ((name == null) || name.length() == 0)
+ {
+ return getDefaultExchange();
+ }
+ else
+ {
+ return _exchangeMap.get(name);
+ }
+
+ }
+
+ /**
+ * Routes content through exchanges, delivering it to 1 or more queues.
+ * @param payload
+ * @throws AMQException if something goes wrong delivering data
+ */
+ public void routeContent(AMQMessage payload) throws AMQException
+ {
+ final AMQShortString exchange = payload.getMessagePublishInfo().getExchange();
+ final Exchange exch = getExchange(exchange);
+ // there is a small window of opportunity for the exchange to be deleted in between
+ // the BasicPublish being received (where the exchange is validated) and the final
+ // content body being received (which triggers this method)
+ // TODO: check where the exchange is validated
+ if (exch == null)
+ {
+ throw new AMQException("Exchange '" + exchange + "' does not exist");
+ }
+ exch.route(payload);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java
new file mode 100644
index 0000000000..5edffc19ed
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java
@@ -0,0 +1,229 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.management.MBeanConstructor;
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+
+public class DestNameExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(DestNameExchange.class);
+
+ /**
+ * Maps from queue name to queue instances
+ */
+ private final Index _index = new Index();
+
+ /**
+ * MBean class implementing the management interfaces.
+ */
+ @MBeanDescription("Management Bean for Direct Exchange")
+ private final class DestNameExchangeMBean extends ExchangeMBean
+ {
+ @MBeanConstructor("Creates an MBean for AMQ direct exchange")
+ public DestNameExchangeMBean() throws JMException
+ {
+ super();
+ _exchangeType = "direct";
+ init();
+ }
+
+ public TabularData bindings() throws OpenDataException
+ {
+ Map<AMQShortString, List<AMQQueue>> bindings = _index.getBindingsMap();
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+
+ for (Map.Entry<AMQShortString, List<AMQQueue>> entry : bindings.entrySet())
+ {
+ AMQShortString key = entry.getKey();
+ List<String> queueList = new ArrayList<String>();
+
+ List<AMQQueue> queues = entry.getValue();
+ for (AMQQueue q : queues)
+ {
+ queueList.add(q.getName().toString());
+ }
+
+ Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ public void createNewBinding(String queueName, String binding) throws JMException
+ {
+ AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName));
+ if (queue == null)
+ {
+ throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
+ }
+
+ try
+ {
+ queue.bind(new AMQShortString(binding), null, DestNameExchange.this);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex);
+ }
+ }
+
+ }// End of MBean class
+
+
+ protected ExchangeMBean createMBean() throws AMQException
+ {
+ try
+ {
+ return new DestNameExchangeMBean();
+ }
+ catch (JMException ex)
+ {
+ _logger.error("Exception occured in creating the direct exchange mbean", ex);
+ throw new AMQException("Exception occured in creating the direct exchange mbean", ex);
+ }
+ }
+
+ public AMQShortString getType()
+ {
+ return ExchangeDefaults.DIRECT_EXCHANGE_CLASS;
+ }
+
+ public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+ assert routingKey != null;
+ if (!_index.add(routingKey, queue))
+ {
+ _logger.debug("Queue " + queue + " is already registered with routing key " + routingKey);
+ }
+ else
+ {
+ _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this);
+ }
+ }
+
+ public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+ assert routingKey != null;
+
+ if (!_index.remove(routingKey, queue))
+ {
+ throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() +
+ " with routing key " + routingKey + ". No queue was registered with that _routing key");
+ }
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ final MessagePublishInfo info = payload.getMessagePublishInfo();
+ final AMQShortString routingKey = info.getRoutingKey();
+ final List<AMQQueue> queues = (routingKey == null) ? null : _index.get(routingKey);
+ if (queues == null || queues.isEmpty())
+ {
+ String msg = "Routing key " + routingKey + " is not known to " + this;
+ if (info.isMandatory() || info.isImmediate())
+ {
+ throw new NoRouteException(msg, payload);
+ }
+ else
+ {
+ _logger.error("MESSAGE LOSS: Message should be sent on a Dead Letter Queue");
+ _logger.warn(msg);
+ }
+ }
+ else
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Publishing message to queue " + queues);
+ }
+
+ for (AMQQueue q : queues)
+ {
+ payload.enqueue(q);
+ }
+ }
+ }
+
+ public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
+ {
+ return isBound(routingKey,queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey, AMQQueue queue)
+ {
+ final List<AMQQueue> queues = _index.get(routingKey);
+ return queues != null && queues.contains(queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey)
+ {
+ final List<AMQQueue> queues = _index.get(routingKey);
+ return queues != null && !queues.isEmpty();
+ }
+
+ public boolean isBound(AMQQueue queue)
+ {
+ Map<AMQShortString, List<AMQQueue>> bindings = _index.getBindingsMap();
+ for (List<AMQQueue> queues : bindings.values())
+ {
+ if (queues.contains(queue))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasBindings()
+ {
+ return !_index.getBindingsMap().isEmpty();
+ }
+
+ public Map<AMQShortString, List<AMQQueue>> getBindings()
+ {
+ return _index.getBindingsMap();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java
new file mode 100644
index 0000000000..b55dbcc792
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java
@@ -0,0 +1,426 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.management.MBeanConstructor;
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class DestWildExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(DestWildExchange.class);
+
+ private ConcurrentHashMap<AMQShortString, List<AMQQueue>> _routingKey2queues =
+ 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 = "#";
+
+ /** DestWildExchangeMBean class implements the management interface for the Topic exchanges. */
+ @MBeanDescription("Management Bean for Topic Exchange")
+ private final class DestWildExchangeMBean extends ExchangeMBean
+ {
+ @MBeanConstructor("Creates an MBean for AMQ topic exchange")
+ public DestWildExchangeMBean() throws JMException
+ {
+ super();
+ _exchangeType = "topic";
+ init();
+ }
+
+ /** returns exchange bindings in tabular form */
+ public TabularData bindings() throws OpenDataException
+ {
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+ for (Map.Entry<AMQShortString, List<AMQQueue>> entry : _routingKey2queues.entrySet())
+ {
+ AMQShortString key = entry.getKey();
+ List<String> queueList = new ArrayList<String>();
+
+ List<AMQQueue> queues = getMatchedQueues(key);
+ for (AMQQueue q : queues)
+ {
+ queueList.add(q.getName().toString());
+ }
+
+ Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ public void createNewBinding(String queueName, String binding) throws JMException
+ {
+ AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName));
+ if (queue == null)
+ {
+ throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
+ }
+
+ try
+ {
+ queue.bind(new AMQShortString(binding), null, DestWildExchange.this);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex);
+ }
+ }
+
+ } // End of MBean class
+
+ public AMQShortString getType()
+ {
+ return ExchangeDefaults.TOPIC_EXCHANGE_CLASS;
+ }
+
+ public synchronized void registerQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+ assert rKey != null;
+
+ AMQShortString routingKey = normalize(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>());
+ // 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);
+ }
+
+ if (!queueList.contains(queue))
+ {
+ queueList.add(queue);
+ }
+ else if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Queue " + queue + " is already registered with routing key " + routingKey);
+ }
+
+ }
+
+ private AMQShortString normalize(AMQShortString routingKey)
+ {
+ StringTokenizer routingTokens = new StringTokenizer(routingKey.toString(), TOPIC_SEPARATOR);
+ List<String> _subscription = new ArrayList<String>();
+
+ while (routingTokens.hasMoreTokens())
+ {
+ _subscription.add(routingTokens.nextToken());
+ }
+
+ int size = _subscription.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 (_subscription.get(index + 1).equals(AMQP_HASH))
+ {
+ // we don't need #.# delete this one
+ _subscription.remove(index);
+ size--;
+ // redo this normalisation
+ index--;
+ }
+
+ if (_subscription.get(index + 1).equals(AMQP_STAR))
+ {
+ // we don't want #.* swap to *.#
+ // remove it and put it in at index + 1
+ _subscription.add(index + 1, _subscription.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);
+
+ return new AMQShortString(sb.toString());
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ MessagePublishInfo info = payload.getMessagePublishInfo();
+
+ final AMQShortString routingKey = normalize(info.getRoutingKey());
+
+ List<AMQQueue> queues = getMatchedQueues(routingKey);
+ // if we have no registered queues we have nothing to do
+ // TODO: add support for the immediate flag
+ if ((queues == null) || queues.isEmpty())
+ {
+ if (info.isMandatory() || info.isImmediate())
+ {
+ String msg = "Topic " + routingKey + " is not known to " + this;
+ throw new NoRouteException(msg, payload);
+ }
+ else
+ {
+ _logger.warn("No queues found for routing key " + routingKey);
+ _logger.warn("Routing map contains: " + _routingKey2queues);
+
+ 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);
+ }
+ }
+
+ public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
+ {
+ return isBound(routingKey, queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey, AMQQueue queue)
+ {
+ List<AMQQueue> queues = _routingKey2queues.get(normalize(routingKey));
+
+ return (queues != null) && queues.contains(queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey)
+ {
+ List<AMQQueue> queues = _routingKey2queues.get(normalize(routingKey));
+
+ return (queues != null) && !queues.isEmpty();
+ }
+
+ public boolean isBound(AMQQueue queue)
+ {
+ for (List<AMQQueue> queues : _routingKey2queues.values())
+ {
+ if (queues.contains(queue))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean hasBindings()
+ {
+ return !_routingKey2queues.isEmpty();
+ }
+
+ public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+ assert rKey != null;
+
+ AMQShortString routingKey = normalize(rKey);
+
+ List<AMQQueue> queues = _routingKey2queues.get(routingKey);
+ if (queues == null)
+ {
+ throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName()
+ + " with routing key " + routingKey + ". No queue was registered with that _routing key");
+
+ }
+
+ boolean removedQ = queues.remove(queue);
+ if (!removedQ)
+ {
+ throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName()
+ + " with routing key " + routingKey);
+ }
+
+ if (queues.isEmpty())
+ {
+ _routingKey2queues.remove(routingKey);
+ }
+ }
+
+ protected ExchangeMBean createMBean() throws AMQException
+ {
+ try
+ {
+ return new DestWildExchangeMBean();
+ }
+ catch (JMException ex)
+ {
+ _logger.error("Exception occured in creating the topic exchenge mbean", ex);
+ throw new AMQException("Exception occured in creating the topic exchenge mbean", ex);
+ }
+ }
+
+ public Map<AMQShortString, List<AMQQueue>> getBindings()
+ {
+ return _routingKey2queues;
+ }
+
+ 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>();
+
+ while (routingTokens.hasMoreTokens())
+ {
+ 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);
+
+ ArrayList<String> queueList = new ArrayList<String>();
+
+ while (queTok.hasMoreTokens())
+ {
+ queueList.add(queTok.nextToken());
+ }
+
+ 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)))
+ {
+ 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;
+ }
+
+ continue;
+ }
+
+ // if the values on the two topics don't match
+ if (!queueList.get(depth + queueskip).equals(routingkeyList.get(depth + routingskip)))
+ {
+ if (queueList.get(depth + queueskip).equals(AMQP_STAR))
+ {
+ depth++;
+
+ continue;
+ }
+ else if (queueList.get(depth + queueskip).equals(AMQP_HASH))
+ {
+ // Is this a # at the end
+ if (queueList.size() == (depth + queueskip + 1))
+ {
+ done = true;
+
+ continue;
+ }
+
+ // otherwise # in the middle
+ while (routingkeyList.size() > (depth + routingskip))
+ {
+ if (routingkeyList.get(depth + routingskip).equals(queueList.get(depth + queueskip + 1)))
+ {
+ queueskip++;
+ depth++;
+
+ break;
+ }
+
+ routingskip++;
+ }
+
+ continue;
+ }
+
+ matching = false;
+ }
+
+ depth++;
+ }
+
+ if (matching)
+ {
+ list.addAll(_routingKey2queues.get(queue));
+ }
+ }
+
+ return list;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java
new file mode 100644
index 0000000000..37cd85a8f8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import java.util.List;
+import java.util.Map;
+
+public interface Exchange
+{
+ AMQShortString getName();
+
+ AMQShortString getType();
+
+ void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException;
+
+ boolean isDurable();
+
+ /**
+ * @return true if the exchange will be deleted after all queues have been detached
+ */
+ boolean isAutoDelete();
+
+ int getTicket();
+
+ void close() throws AMQException;
+
+ void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException;
+
+ void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException;
+
+ void route(AMQMessage message) throws AMQException;
+
+
+ /**
+ * Determines whether a message would be isBound to a particular queue using a specific routing key and arguments
+ * @param routingKey
+ * @param arguments
+ * @param queue
+ * @return
+ * @throws AMQException
+ */
+ boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue);
+
+ /**
+ * Determines whether a message would be isBound to a particular queue using a specific routing key
+ * @param routingKey
+ * @param queue
+ * @return
+ * @throws AMQException
+ */
+ boolean isBound(AMQShortString routingKey, AMQQueue queue);
+
+ /**
+ * Determines whether a message is routing to any queue using a specific _routing key
+ * @param routingKey
+ * @return
+ * @throws AMQException
+ */
+ boolean isBound(AMQShortString routingKey);
+
+ boolean isBound(AMQQueue queue);
+
+ /**
+ * Returns true if this exchange has at least one binding associated with it.
+ * @return
+ * @throws AMQException
+ */
+ boolean hasBindings();
+
+ Map<AMQShortString, List<AMQQueue>> getBindings();
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java
new file mode 100644
index 0000000000..e07fd0b8fc
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+
+
+public interface ExchangeFactory
+{
+ Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete,
+ int ticket)
+ throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java
new file mode 100644
index 0000000000..c77f114428
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+
+/**
+ * ExchangeInUseRegistry indicates that an exchange cannot be unregistered because it is currently being used.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to unregister exchange that is in use.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo This exception is not used. However, it is part of the ExchangeRegistry interface, and looks like code is
+ * going to need to be added to throw/deal with this. Alternatively ExchangeResitries may be able to handle the
+ * issue internally.
+ */
+public class ExchangeInUseException extends AMQException
+{
+ public ExchangeInUseException(String exchangeName)
+ {
+ super("Exchange " + exchangeName + " is currently in use");
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java
new file mode 100644
index 0000000000..fe3b19e74e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.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.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+
+import java.util.Collection;
+
+
+public interface ExchangeRegistry extends MessageRouter
+{
+ void registerExchange(Exchange exchange) throws AMQException;
+
+ /**
+ * Unregister an exchange
+ * @param name name of the exchange to delete
+ * @param inUse if true, do NOT delete the exchange if it is in use (has queues bound to it)
+ * @throws ExchangeInUseException when the exchange cannot be deleted because it is in use
+ * @throws AMQException
+ */
+ void unregisterExchange(AMQShortString name, boolean inUse) throws ExchangeInUseException, AMQException;
+
+ Exchange getExchange(AMQShortString name);
+
+ void setDefaultExchange(Exchange exchange);
+
+ Exchange getDefaultExchange();
+
+ Collection<AMQShortString> getExchangeNames();
+
+ void initialise() throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
new file mode 100644
index 0000000000..aa13f1d8ee
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.management.MBeanConstructor;
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class FanoutExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(FanoutExchange.class);
+
+ /**
+ * Maps from queue name to queue instances
+ */
+ private final CopyOnWriteArraySet<AMQQueue> _queues = new CopyOnWriteArraySet<AMQQueue>();
+
+ /**
+ * MBean class implementing the management interfaces.
+ */
+ @MBeanDescription("Management Bean for Fanout Exchange")
+ private final class FanoutExchangeMBean extends ExchangeMBean
+ {
+ @MBeanConstructor("Creates an MBean for AMQ fanout exchange")
+ public FanoutExchangeMBean() throws JMException
+ {
+ super();
+ _exchangeType = "fanout";
+ init();
+ }
+
+ public TabularData bindings() throws OpenDataException
+ {
+
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+
+ for (AMQQueue queue : _queues)
+ {
+ String queueName = queue.getName().toString();
+
+ Object[] bindingItemValues = {queueName, new String[]{queueName}};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ public void createNewBinding(String queueName, String binding) throws JMException
+ {
+ AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName));
+ if (queue == null)
+ {
+ throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
+ }
+
+ try
+ {
+ queue.bind(new AMQShortString(binding), null, FanoutExchange.this);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex);
+ }
+ }
+
+ } // End of MBean class
+
+ protected ExchangeMBean createMBean() throws AMQException
+ {
+ try
+ {
+ return new FanoutExchange.FanoutExchangeMBean();
+ }
+ catch (JMException ex)
+ {
+ _logger.error("Exception occured in creating the direct exchange mbean", ex);
+ throw new AMQException("Exception occured in creating the direct exchange mbean", ex);
+ }
+ }
+
+ public Map<AMQShortString, List<AMQQueue>> getBindings()
+ {
+ return null;
+ }
+
+ public AMQShortString getType()
+ {
+ return ExchangeDefaults.FANOUT_EXCHANGE_CLASS;
+ }
+
+ public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+
+ if (_queues.contains(queue))
+ {
+ _logger.debug("Queue " + queue + " is already registered");
+ }
+ else
+ {
+ _queues.add(queue);
+ _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this);
+ }
+ }
+
+ public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+
+ if (!_queues.remove(queue))
+ {
+ throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() + ". ");
+ }
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ final MessagePublishInfo publishInfo = payload.getMessagePublishInfo();
+ final AMQShortString routingKey = publishInfo.getRoutingKey();
+ if ((_queues == null) || _queues.isEmpty())
+ {
+ String msg = "No queues bound to " + this;
+ if (publishInfo.isMandatory() || publishInfo.isImmediate())
+ {
+ throw new NoRouteException(msg, payload);
+ }
+ else
+ {
+ _logger.warn(msg);
+ }
+ }
+ else
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Publishing message to queue " + _queues);
+ }
+
+ for (AMQQueue q : _queues)
+ {
+ payload.enqueue(q);
+ }
+ }
+ }
+
+ public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
+ {
+ return isBound(routingKey, queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey, AMQQueue queue)
+ {
+ return _queues.contains(queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey)
+ {
+
+ return (_queues != null) && !_queues.isEmpty();
+ }
+
+ public boolean isBound(AMQQueue queue)
+ {
+
+ return _queues.contains(queue);
+ }
+
+ public boolean hasBindings()
+ {
+ return !_queues.isEmpty();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java
new file mode 100644
index 0000000000..2b7df4361a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java
@@ -0,0 +1,219 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.framing.AMQTypedValue;
+import org.apache.qpid.framing.FieldTable;
+
+/**
+ * Defines binding and matching based on a set of headers.
+ */
+class HeadersBinding
+{
+ private static final Logger _logger = Logger.getLogger(HeadersBinding.class);
+
+ private final FieldTable _mappings;
+ private final Set<String> required = new HashSet<String>();
+ private final Map<String,Object> matches = new HashMap<String,Object>();
+ private boolean matchAny;
+
+ private final class MatchesOrProcessor implements FieldTable.FieldTableElementProcessor
+ {
+ private Boolean _result = Boolean.FALSE;
+
+ public boolean processElement(String propertyName, AMQTypedValue value)
+ {
+ if((value != null) && (value.getValue() != null) && value.getValue().equals(matches.get(propertyName)))
+ {
+ _result = Boolean.TRUE;
+ return false;
+ }
+ return true;
+ }
+
+ public Object getResult()
+ {
+ return _result;
+ }
+ }
+
+ private final class RequiredOrProcessor implements FieldTable.FieldTableElementProcessor
+ {
+ Boolean _result = Boolean.FALSE;
+
+ public boolean processElement(String propertyName, AMQTypedValue value)
+ {
+ if(required.contains(propertyName))
+ {
+ _result = Boolean.TRUE;
+ return false;
+ }
+ return true;
+ }
+
+ public Object getResult()
+ {
+ return _result;
+ }
+ }
+
+
+
+ /**
+ * Creates a binding for a set of mappings. Those mappings whose value is
+ * null or the empty string are assumed only to be required headers, with
+ * no constraint on the value. Those with a non-null value are assumed to
+ * define a required match of value.
+ * @param mappings the defined mappings this binding should use
+ */
+
+ HeadersBinding(FieldTable mappings)
+ {
+ _mappings = mappings;
+ initMappings();
+ }
+
+ private void initMappings()
+ {
+
+ _mappings.processOverElements(new FieldTable.FieldTableElementProcessor()
+ {
+
+ public boolean processElement(String propertyName, AMQTypedValue value)
+ {
+ if (isSpecial(propertyName))
+ {
+ processSpecial(propertyName, value.getValue());
+ }
+ else if (value.getValue() == null || value.getValue().equals(""))
+ {
+ required.add(propertyName);
+ }
+ else
+ {
+ matches.put(propertyName,value.getValue());
+ }
+
+ return true;
+ }
+
+ public Object getResult()
+ {
+ return null;
+ }
+ });
+ }
+
+ protected FieldTable getMappings()
+ {
+ return _mappings;
+ }
+
+ /**
+ * Checks whether the supplied headers match the requirements of this binding
+ * @param headers the headers to check
+ * @return true if the headers define any required keys and match any required
+ * values
+ */
+ public boolean matches(FieldTable headers)
+ {
+ if(headers == null)
+ {
+ return required.isEmpty() && matches.isEmpty();
+ }
+ else
+ {
+ return matchAny ? or(headers) : and(headers);
+ }
+ }
+
+ private boolean and(FieldTable headers)
+ {
+ if(headers.keys().containsAll(required))
+ {
+ for(Map.Entry<String, Object> e : matches.entrySet())
+ {
+ if(!e.getValue().equals(headers.getObject(e.getKey())))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+ private boolean or(final FieldTable headers)
+ {
+ if(required.isEmpty() || !(Boolean) headers.processOverElements(new RequiredOrProcessor()))
+ {
+ return ((!matches.isEmpty()) && (Boolean) headers.processOverElements(new MatchesOrProcessor()))
+ || (required.isEmpty() && matches.isEmpty());
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ private void processSpecial(String key, Object value)
+ {
+ if("X-match".equalsIgnoreCase(key))
+ {
+ matchAny = isAny(value);
+ }
+ else
+ {
+ _logger.warn("Ignoring special header: " + key);
+ }
+ }
+
+ private boolean isAny(Object value)
+ {
+ if(value instanceof String)
+ {
+ if("any".equalsIgnoreCase((String) value)) return true;
+ if("all".equalsIgnoreCase((String) value)) return false;
+ }
+ _logger.warn("Ignoring unrecognised match type: " + value);
+ return false;//default to all
+ }
+
+ static boolean isSpecial(Object key)
+ {
+ return key instanceof String && isSpecial((String) key);
+ }
+
+ static boolean isSpecial(String key)
+ {
+ return key.startsWith("X-") || key.startsWith("x-");
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
new file mode 100644
index 0000000000..3544e9d1f8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
@@ -0,0 +1,323 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.AMQTypedValue;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.management.MBeanConstructor;
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+
+import javax.management.JMException;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * An exchange that binds queues based on a set of required headers and header values
+ * and routes messages to these queues by matching the headers of the message against
+ * those with which the queues were bound.
+ * <p/>
+ * <pre>
+ * The Headers Exchange
+ *
+ * Routes messages according to the value/presence of fields in the message header table.
+ * (Basic and JMS content has a content header field called "headers" that is a table of
+ * message header fields).
+ *
+ * class = "headers"
+ * routing key is not used
+ *
+ * Has the following binding arguments:
+ *
+ * the X-match field - if "all", does an AND match (used for GRM), if "any", does an OR match.
+ * other fields prefixed with "X-" are ignored (and generate a console warning message).
+ * a field with no value or empty value indicates a match on presence only.
+ * a field with a value indicates match on field presence and specific value.
+ *
+ * Standard instances:
+ *
+ * amq.match - pub/sub on field content/value
+ * </pre>
+ */
+public class HeadersExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(HeadersExchange.class);
+
+ private final List<Registration> _bindings = new CopyOnWriteArrayList<Registration>();
+
+ /**
+ * HeadersExchangeMBean class implements the management interface for the
+ * Header Exchanges.
+ */
+ @MBeanDescription("Management Bean for Headers Exchange")
+ private final class HeadersExchangeMBean extends ExchangeMBean
+ {
+ @MBeanConstructor("Creates an MBean for AMQ Headers exchange")
+ public HeadersExchangeMBean() throws JMException
+ {
+ super();
+ _exchangeType = "headers";
+ init();
+ }
+
+ /**
+ * initialises the OpenType objects.
+ */
+ protected void init() throws OpenDataException
+ {
+ _bindingItemNames = new String[]{"Binding No", "Queue Name", "Queue Bindings"};
+ _bindingItemIndexNames = new String[]{_bindingItemNames[0]};
+
+ _bindingItemTypes = new OpenType[3];
+ _bindingItemTypes[0] = SimpleType.INTEGER;
+ _bindingItemTypes[1] = SimpleType.STRING;
+ _bindingItemTypes[2] = new ArrayType(1, SimpleType.STRING);
+ _bindingDataType = new CompositeType("Exchange Binding", "Queue name and header bindings",
+ _bindingItemNames, _bindingItemNames, _bindingItemTypes);
+ _bindinglistDataType = new TabularType("Exchange Bindings", "List of exchange bindings for " + getName(),
+ _bindingDataType, _bindingItemIndexNames);
+ }
+
+ public TabularData bindings() throws OpenDataException
+ {
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+ int count = 1;
+ for (Iterator<Registration> itr = _bindings.iterator(); itr.hasNext();)
+ {
+ Registration registration = itr.next();
+ String queueName = registration.queue.getName().toString();
+
+ HeadersBinding headers = registration.binding;
+ FieldTable headerMappings = headers.getMappings();
+ final List<String> mappingList = new ArrayList<String>();
+
+ headerMappings.processOverElements(new FieldTable.FieldTableElementProcessor()
+ {
+
+ public boolean processElement(String propertyName, AMQTypedValue value)
+ {
+ mappingList.add(propertyName + "=" + value.getValue());
+ return true;
+ }
+
+ public Object getResult()
+ {
+ return mappingList;
+ }
+ });
+
+
+ Object[] bindingItemValues = {count++, queueName, mappingList.toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ /**
+ * Creates bindings. Binding pattern is as follows-
+ * <attributename>=<value>,<attributename>=<value>,...
+ * @param queueName
+ * @param binding
+ * @throws javax.management.JMException
+ */
+ public void createNewBinding(String queueName, String binding) throws JMException
+ {
+ AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName));
+
+ if (queue == null)
+ {
+ throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
+ }
+
+ String[] bindings = binding.split(",");
+ FieldTable bindingMap = new FieldTable();
+ for (int i = 0; i < bindings.length; i++)
+ {
+ String[] keyAndValue = bindings[i].split("=");
+ if (keyAndValue == null || keyAndValue.length < 2)
+ {
+ throw new JMException("Format for headers binding should be \"<attribute1>=<value1>,<attribute2>=<value2>\" ");
+ }
+ bindingMap.setString(keyAndValue[0], keyAndValue[1]);
+ }
+
+ _bindings.add(new Registration(new HeadersBinding(bindingMap), queue));
+ }
+
+ } // End of MBean class
+
+ public AMQShortString getType()
+ {
+ return ExchangeDefaults.HEADERS_EXCHANGE_CLASS;
+ }
+
+ public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ _logger.debug("Exchange " + getName() + ": Binding " + queue.getName() + " with " + args);
+ _bindings.add(new Registration(new HeadersBinding(args), queue));
+ }
+
+ public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ _logger.debug("Exchange " + getName() + ": Unbinding " + queue.getName());
+ _bindings.remove(new Registration(new HeadersBinding(args), queue));
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ FieldTable headers = getHeaders(payload.getContentHeaderBody());
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Exchange " + getName() + ": routing message with headers " + headers);
+ }
+ boolean routed = false;
+ for (Registration e : _bindings)
+ {
+ if (e.binding.matches(headers))
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Exchange " + getName() + ": delivering message with headers " +
+ headers + " to " + e.queue.getName());
+ }
+ payload.enqueue(e.queue);
+ routed = true;
+ }
+ }
+ if (!routed)
+ {
+
+ String msg = "Exchange " + getName() + ": message not routable.";
+
+ if (payload.getMessagePublishInfo().isMandatory() || payload.getMessagePublishInfo().isImmediate())
+ {
+ throw new NoRouteException(msg, payload);
+ }
+ else
+ {
+ _logger.warn(msg);
+ }
+
+ }
+ }
+
+ public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
+ {
+ //fixme isBound here should take the arguements in to consideration.
+ return isBound(routingKey, queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey, AMQQueue queue)
+ {
+ return isBound(queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey)
+ {
+ return hasBindings();
+ }
+
+ public boolean isBound(AMQQueue queue)
+ {
+ for (Registration r : _bindings)
+ {
+ if (r.queue.equals(queue))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasBindings()
+ {
+ return !_bindings.isEmpty();
+ }
+
+ protected FieldTable getHeaders(ContentHeaderBody contentHeaderFrame)
+ {
+ //what if the content type is not 'basic'? 'file' and 'stream' content classes also define headers,
+ //but these are not yet implemented.
+ return ((BasicContentHeaderProperties) contentHeaderFrame.properties).getHeaders();
+ }
+
+ protected ExchangeMBean createMBean() throws AMQException
+ {
+ try
+ {
+ return new HeadersExchangeMBean();
+ }
+ catch (JMException ex)
+ {
+ _logger.error("Exception occured in creating the HeadersExchangeMBean", ex);
+ throw new AMQException("Exception occured in creating the HeadersExchangeMBean", ex);
+ }
+ }
+
+ public Map<AMQShortString, List<AMQQueue>> getBindings()
+ {
+ return null;
+ }
+
+ private static class Registration
+ {
+ private final HeadersBinding binding;
+ private final AMQQueue queue;
+
+ Registration(HeadersBinding binding, AMQQueue queue)
+ {
+ this.binding = binding;
+ this.queue = queue;
+ }
+
+ public int hashCode()
+ {
+ return queue.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return o instanceof Registration && ((Registration) o).queue.equals(queue);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java
new file mode 100644
index 0000000000..eacdad8a8e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.queue.AMQQueue;
+
+/**
+ * An index of queues against routing key. Allows multiple queues to be stored
+ * against the same key. Used in the DestNameExchange.
+ */
+class Index
+{
+ private ConcurrentMap<AMQShortString, List<AMQQueue>> _index
+ = new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
+
+ synchronized boolean add(AMQShortString key, AMQQueue queue)
+ {
+ List<AMQQueue> queues = _index.get(key);
+ if(queues == null)
+ {
+ queues = new CopyOnWriteArrayList<AMQQueue>();
+ //next call is atomic, so there is no race to create the list
+ List<AMQQueue> active = _index.putIfAbsent(key, queues);
+ if(active != null)
+ {
+ //someone added the new one in faster than we did, so use theirs
+ queues = active;
+ }
+ }
+ if(queues.contains(queue))
+ {
+ return false;
+ }
+ else
+ {
+ return queues.add(queue);
+ }
+ }
+
+ synchronized boolean remove(AMQShortString key, AMQQueue queue)
+ {
+ List<AMQQueue> queues = _index.get(key);
+ if (queues != null)
+ {
+ boolean removed = queues.remove(queue);
+ if (queues.size() == 0)
+ {
+ _index.remove(key);
+ }
+ return removed;
+ }
+ return false;
+ }
+
+ List<AMQQueue> get(AMQShortString key)
+ {
+ return _index.get(key);
+ }
+
+ Map<AMQShortString, List<AMQQueue>> getBindingsMap()
+ {
+ return new HashMap<AMQShortString, List<AMQQueue>>(_index);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java
new file mode 100644
index 0000000000..5d6d68b3c8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.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.server.exchange;
+
+import java.io.IOException;
+
+import javax.management.JMException;
+import javax.management.MBeanOperationInfo;
+import javax.management.openmbean.TabularData;
+
+import org.apache.qpid.server.management.MBeanAttribute;
+import org.apache.qpid.server.management.MBeanOperation;
+import org.apache.qpid.server.management.MBeanOperationParameter;
+import org.apache.qpid.server.queue.ManagedQueue;
+
+/**
+ * The management interface exposed to allow management of an Exchange.
+ * @author Robert J. Greig
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedExchange
+{
+ static final String TYPE = "Exchange";
+
+ /**
+ * Returns the name of the managed exchange.
+ * @return the name of the exchange.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="Name", description=TYPE + " Name")
+ String getName() throws IOException;
+
+ @MBeanAttribute(name="ExchangeType", description="Exchange Type")
+ String getExchangeType() throws IOException;
+
+ @MBeanAttribute(name="TicketNo", description="Exchange Ticket No")
+ Integer getTicketNo() throws IOException;
+
+ /**
+ * Tells if the exchange is durable or not.
+ * @return true if the exchange is durable.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="Durable", description="true if Exchange is durable")
+ boolean isDurable() throws IOException;
+
+ /**
+ * Tells if the exchange is set for autodelete or not.
+ * @return true if the exchange is set as autodelete.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="AutoDelete", description="true if Exchange is AutoDelete")
+ boolean isAutoDelete() throws IOException;
+
+ // Operations
+
+ /**
+ * Returns all the bindings this exchange has with the queues.
+ * @return the bindings with the exchange.
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="bindings", description="view the queue bindings for this exchange")
+ TabularData bindings() throws IOException, JMException;
+
+ /**
+ * Creates new binding with the given queue and binding.
+ * @param queueName
+ * @param binding
+ * @throws JMException
+ */
+ @MBeanOperation(name="createNewBinding",
+ description="create a new binding with this exchange",
+ impact= MBeanOperationInfo.ACTION)
+ void createNewBinding(@MBeanOperationParameter(name= ManagedQueue.TYPE, description="Queue name") String queueName,
+ @MBeanOperationParameter(name="Binding", description="New binding")String binding)
+ throws JMException;
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java
new file mode 100644
index 0000000000..7508e80f7f
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * Separated out from the ExchangeRegistry interface to allow components
+ * that use only this part to have a dependency with a reduced footprint.
+ *
+ */
+public interface MessageRouter
+{
+ /**
+ * Routes content through exchanges, delivering it to 1 or more queues.
+ * @param message the message to be routed
+ *
+ * @throws org.apache.qpid.AMQException if something goes wrong delivering data
+ */
+ void routeContent(AMQMessage message) throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java
new file mode 100644
index 0000000000..1d6ab3842d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * NoRouteException is a {@link RequiredDeliveryException} that represents the failure case where a manadatory message
+ * cannot be delivered because there is no route for the message. The AMQP status code, 312, is always used to report
+ * this condition.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to deliver a message that must be delivered.
+ * </table>
+ */
+public class NoRouteException extends RequiredDeliveryException
+{
+ public NoRouteException(String msg, AMQMessage message)
+ {
+ super(msg, message);
+ }
+
+ public AMQConstant getReplyCode()
+ {
+ return AMQConstant.NO_ROUTE;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java
new file mode 100644
index 0000000000..fb5220f4da
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java
@@ -0,0 +1,275 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.server.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * An expression which performs an operation on two expression values
+ */
+public abstract class ArithmeticExpression extends BinaryExpression
+{
+
+ protected static final int INTEGER = 1;
+ protected static final int LONG = 2;
+ protected static final int DOUBLE = 3;
+
+ /**
+ * @param left
+ * @param right
+ */
+ public ArithmeticExpression(Expression left, Expression right)
+ {
+ super(left, right);
+ }
+
+ public static Expression createPlus(Expression left, Expression right)
+ {
+ return new ArithmeticExpression(left, right)
+ {
+ protected Object evaluate(Object lvalue, Object rvalue)
+ {
+ if (lvalue instanceof String)
+ {
+ String text = (String) lvalue;
+ String answer = text + rvalue;
+
+ return answer;
+ }
+ else if (lvalue instanceof Number)
+ {
+ return plus((Number) lvalue, asNumber(rvalue));
+ }
+
+ throw new RuntimeException("Cannot call plus operation on: " + lvalue + " and: " + rvalue);
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "+";
+ }
+ };
+ }
+
+ public static Expression createMinus(Expression left, Expression right)
+ {
+ return new ArithmeticExpression(left, right)
+ {
+ protected Object evaluate(Object lvalue, Object rvalue)
+ {
+ if (lvalue instanceof Number)
+ {
+ return minus((Number) lvalue, asNumber(rvalue));
+ }
+
+ throw new RuntimeException("Cannot call minus operation on: " + lvalue + " and: " + rvalue);
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "-";
+ }
+ };
+ }
+
+ public static Expression createMultiply(Expression left, Expression right)
+ {
+ return new ArithmeticExpression(left, right)
+ {
+
+ protected Object evaluate(Object lvalue, Object rvalue)
+ {
+ if (lvalue instanceof Number)
+ {
+ return multiply((Number) lvalue, asNumber(rvalue));
+ }
+
+ throw new RuntimeException("Cannot call multiply operation on: " + lvalue + " and: " + rvalue);
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "*";
+ }
+ };
+ }
+
+ public static Expression createDivide(Expression left, Expression right)
+ {
+ return new ArithmeticExpression(left, right)
+ {
+
+ protected Object evaluate(Object lvalue, Object rvalue)
+ {
+ if (lvalue instanceof Number)
+ {
+ return divide((Number) lvalue, asNumber(rvalue));
+ }
+
+ throw new RuntimeException("Cannot call divide operation on: " + lvalue + " and: " + rvalue);
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "/";
+ }
+ };
+ }
+
+ public static Expression createMod(Expression left, Expression right)
+ {
+ return new ArithmeticExpression(left, right)
+ {
+
+ protected Object evaluate(Object lvalue, Object rvalue)
+ {
+ if (lvalue instanceof Number)
+ {
+ return mod((Number) lvalue, asNumber(rvalue));
+ }
+
+ throw new RuntimeException("Cannot call mod operation on: " + lvalue + " and: " + rvalue);
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "%";
+ }
+ };
+ }
+
+ protected Number plus(Number left, Number right)
+ {
+ switch (numberType(left, right))
+ {
+
+ case INTEGER:
+ return new Integer(left.intValue() + right.intValue());
+
+ case LONG:
+ return new Long(left.longValue() + right.longValue());
+
+ default:
+ return new Double(left.doubleValue() + right.doubleValue());
+ }
+ }
+
+ protected Number minus(Number left, Number right)
+ {
+ switch (numberType(left, right))
+ {
+
+ case INTEGER:
+ return new Integer(left.intValue() - right.intValue());
+
+ case LONG:
+ return new Long(left.longValue() - right.longValue());
+
+ default:
+ return new Double(left.doubleValue() - right.doubleValue());
+ }
+ }
+
+ protected Number multiply(Number left, Number right)
+ {
+ switch (numberType(left, right))
+ {
+
+ case INTEGER:
+ return new Integer(left.intValue() * right.intValue());
+
+ case LONG:
+ return new Long(left.longValue() * right.longValue());
+
+ default:
+ return new Double(left.doubleValue() * right.doubleValue());
+ }
+ }
+
+ protected Number divide(Number left, Number right)
+ {
+ return new Double(left.doubleValue() / right.doubleValue());
+ }
+
+ protected Number mod(Number left, Number right)
+ {
+ return new Double(left.doubleValue() % right.doubleValue());
+ }
+
+ private int numberType(Number left, Number right)
+ {
+ if (isDouble(left) || isDouble(right))
+ {
+ return DOUBLE;
+ }
+ else if ((left instanceof Long) || (right instanceof Long))
+ {
+ return LONG;
+ }
+ else
+ {
+ return INTEGER;
+ }
+ }
+
+ private boolean isDouble(Number n)
+ {
+ return (n instanceof Float) || (n instanceof Double);
+ }
+
+ protected Number asNumber(Object value)
+ {
+ if (value instanceof Number)
+ {
+ return (Number) value;
+ }
+ else
+ {
+ throw new RuntimeException("Cannot convert value: " + value + " into a number");
+ }
+ }
+
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+ Object lvalue = left.evaluate(message);
+ if (lvalue == null)
+ {
+ return null;
+ }
+
+ Object rvalue = right.evaluate(message);
+ if (rvalue == null)
+ {
+ return null;
+ }
+
+ return evaluate(lvalue, rvalue);
+ }
+
+ /**
+ * @param lvalue
+ * @param rvalue
+ * @return
+ */
+ protected abstract Object evaluate(Object lvalue, Object rvalue);
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java
new file mode 100644
index 0000000000..024257bea9
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java
@@ -0,0 +1,106 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+/**
+ * An expression which performs an operation on two expression values.
+ */
+public abstract class BinaryExpression implements Expression
+{
+ protected Expression left;
+ protected Expression right;
+
+ public BinaryExpression(Expression left, Expression right)
+ {
+ this.left = left;
+ this.right = right;
+ }
+
+ public Expression getLeft()
+ {
+ return left;
+ }
+
+ public Expression getRight()
+ {
+ return right;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "(" + left.toString() + " " + getExpressionSymbol() + " " + right.toString() + ")";
+ }
+
+ /**
+ * TODO: more efficient hashCode()
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return toString().hashCode();
+ }
+
+ /**
+ * TODO: more efficient hashCode()
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object o)
+ {
+
+ if ((o == null) || !this.getClass().equals(o.getClass()))
+ {
+ return false;
+ }
+
+ return toString().equals(o.toString());
+
+ }
+
+ /**
+ * Returns the symbol that represents this binary expression. For example, addition is
+ * represented by "+"
+ *
+ * @return
+ */
+ public abstract String getExpressionSymbol();
+
+ /**
+ * @param expression
+ */
+ public void setRight(Expression expression)
+ {
+ right = expression;
+ }
+
+ /**
+ * @param expression
+ */
+ public void setLeft(Expression expression)
+ {
+ left = expression;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java
new file mode 100644
index 0000000000..e28ff79820
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java
@@ -0,0 +1,40 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.server.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * A BooleanExpression is an expression that always
+ * produces a Boolean result.
+ */
+public interface BooleanExpression extends Expression
+{
+
+ /**
+ * @param message
+ * @return true if the expression evaluates to Boolean.TRUE.
+ * @throws AMQException
+ */
+ public boolean matches(AMQMessage message) throws AMQException;
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
new file mode 100644
index 0000000000..72a9ef7969
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
@@ -0,0 +1,595 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * A filter performing a comparison of two objects
+ */
+public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression
+{
+
+ public static BooleanExpression createBetween(Expression value, Expression left, Expression right)
+ {
+ return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right));
+ }
+
+ public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right)
+ {
+ return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right));
+ }
+
+ private static final HashSet REGEXP_CONTROL_CHARS = new HashSet();
+
+ static
+ {
+ REGEXP_CONTROL_CHARS.add(new Character('.'));
+ REGEXP_CONTROL_CHARS.add(new Character('\\'));
+ REGEXP_CONTROL_CHARS.add(new Character('['));
+ REGEXP_CONTROL_CHARS.add(new Character(']'));
+ REGEXP_CONTROL_CHARS.add(new Character('^'));
+ REGEXP_CONTROL_CHARS.add(new Character('$'));
+ REGEXP_CONTROL_CHARS.add(new Character('?'));
+ REGEXP_CONTROL_CHARS.add(new Character('*'));
+ REGEXP_CONTROL_CHARS.add(new Character('+'));
+ REGEXP_CONTROL_CHARS.add(new Character('{'));
+ REGEXP_CONTROL_CHARS.add(new Character('}'));
+ REGEXP_CONTROL_CHARS.add(new Character('|'));
+ REGEXP_CONTROL_CHARS.add(new Character('('));
+ REGEXP_CONTROL_CHARS.add(new Character(')'));
+ REGEXP_CONTROL_CHARS.add(new Character(':'));
+ REGEXP_CONTROL_CHARS.add(new Character('&'));
+ REGEXP_CONTROL_CHARS.add(new Character('<'));
+ REGEXP_CONTROL_CHARS.add(new Character('>'));
+ REGEXP_CONTROL_CHARS.add(new Character('='));
+ REGEXP_CONTROL_CHARS.add(new Character('!'));
+ }
+
+ static class LikeExpression extends UnaryExpression implements BooleanExpression
+ {
+
+ Pattern likePattern;
+
+ /**
+ * @param right
+ */
+ public LikeExpression(Expression right, String like, int escape)
+ {
+ super(right);
+
+ StringBuffer regexp = new StringBuffer(like.length() * 2);
+ regexp.append("\\A"); // The beginning of the input
+ for (int i = 0; i < like.length(); i++)
+ {
+ char c = like.charAt(i);
+ if (escape == (0xFFFF & c))
+ {
+ i++;
+ if (i >= like.length())
+ {
+ // nothing left to escape...
+ break;
+ }
+
+ char t = like.charAt(i);
+ regexp.append("\\x");
+ regexp.append(Integer.toHexString(0xFFFF & t));
+ }
+ else if (c == '%')
+ {
+ regexp.append(".*?"); // Do a non-greedy match
+ }
+ else if (c == '_')
+ {
+ regexp.append("."); // match one
+ }
+ else if (REGEXP_CONTROL_CHARS.contains(new Character(c)))
+ {
+ regexp.append("\\x");
+ regexp.append(Integer.toHexString(0xFFFF & c));
+ }
+ else
+ {
+ regexp.append(c);
+ }
+ }
+
+ regexp.append("\\z"); // The end of the input
+
+ likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL);
+ }
+
+ /**
+ * org.apache.activemq.filter.UnaryExpression#getExpressionSymbol()
+ */
+ public String getExpressionSymbol()
+ {
+ return "LIKE";
+ }
+
+ /**
+ * org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext)
+ */
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+
+ Object rv = this.getRight().evaluate(message);
+
+ if (rv == null)
+ {
+ return null;
+ }
+
+ if (!(rv instanceof String))
+ {
+ return
+ Boolean.FALSE;
+ // throw new RuntimeException("LIKE can only operate on String identifiers. LIKE attemped on: '" + rv.getClass());
+ }
+
+ return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public boolean matches(AMQMessage message) throws AMQException
+ {
+ Object object = evaluate(message);
+
+ return (object != null) && (object == Boolean.TRUE);
+ }
+ }
+
+ public static BooleanExpression createLike(Expression left, String right, String escape)
+ {
+ if ((escape != null) && (escape.length() != 1))
+ {
+ throw new RuntimeException(
+ "The ESCAPE string litteral is invalid. It can only be one character. Litteral used: " + escape);
+ }
+
+ int c = -1;
+ if (escape != null)
+ {
+ c = 0xFFFF & escape.charAt(0);
+ }
+
+ return new LikeExpression(left, right, c);
+ }
+
+ public static BooleanExpression createNotLike(Expression left, String right, String escape)
+ {
+ return UnaryExpression.createNOT(createLike(left, right, escape));
+ }
+
+ public static BooleanExpression createInFilter(Expression left, List elements)
+ {
+
+ if (!(left instanceof PropertyExpression))
+ {
+ throw new RuntimeException("Expected a property for In expression, got: " + left);
+ }
+
+ return UnaryExpression.createInExpression((PropertyExpression) left, elements, false);
+
+ }
+
+ public static BooleanExpression createNotInFilter(Expression left, List elements)
+ {
+
+ if (!(left instanceof PropertyExpression))
+ {
+ throw new RuntimeException("Expected a property for In expression, got: " + left);
+ }
+
+ return UnaryExpression.createInExpression((PropertyExpression) left, elements, true);
+
+ }
+
+ public static BooleanExpression createIsNull(Expression left)
+ {
+ return doCreateEqual(left, ConstantExpression.NULL);
+ }
+
+ public static BooleanExpression createIsNotNull(Expression left)
+ {
+ return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL));
+ }
+
+ public static BooleanExpression createNotEqual(Expression left, Expression right)
+ {
+ return UnaryExpression.createNOT(createEqual(left, right));
+ }
+
+ public static BooleanExpression createEqual(Expression left, Expression right)
+ {
+ checkEqualOperand(left);
+ checkEqualOperand(right);
+ checkEqualOperandCompatability(left, right);
+
+ return doCreateEqual(left, right);
+ }
+
+ private static BooleanExpression doCreateEqual(Expression left, Expression right)
+ {
+ return new ComparisonExpression(left, right)
+ {
+
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+ Object lv = left.evaluate(message);
+ Object rv = right.evaluate(message);
+
+ // Iff one of the values is null
+ if ((lv == null) ^ (rv == null))
+ {
+ return Boolean.FALSE;
+ }
+
+ if ((lv == rv) || lv.equals(rv))
+ {
+ return Boolean.TRUE;
+ }
+
+ if ((lv instanceof Comparable) && (rv instanceof Comparable))
+ {
+ return compare((Comparable) lv, (Comparable) rv);
+ }
+
+ return Boolean.FALSE;
+ }
+
+ protected boolean asBoolean(int answer)
+ {
+ return answer == 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "=";
+ }
+ };
+ }
+
+ public static BooleanExpression createGreaterThan(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+ protected boolean asBoolean(int answer)
+ {
+ return answer > 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return ">";
+ }
+ };
+ }
+
+ public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+ protected boolean asBoolean(int answer)
+ {
+ return answer >= 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return ">=";
+ }
+ };
+ }
+
+ public static BooleanExpression createLessThan(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+
+ protected boolean asBoolean(int answer)
+ {
+ return answer < 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "<";
+ }
+
+ };
+ }
+
+ public static BooleanExpression createLessThanEqual(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+
+ protected boolean asBoolean(int answer)
+ {
+ return answer <= 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "<=";
+ }
+ };
+ }
+
+ /**
+ * Only Numeric expressions can be used in >, >=, < or <= expressions.s
+ *
+ * @param expr
+ */
+ public static void checkLessThanOperand(Expression expr)
+ {
+ if (expr instanceof ConstantExpression)
+ {
+ Object value = ((ConstantExpression) expr).getValue();
+ if (value instanceof Number)
+ {
+ return;
+ }
+
+ // Else it's boolean or a String..
+ throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+ }
+
+ if (expr instanceof BooleanExpression)
+ {
+ throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+ }
+ }
+
+ /**
+ * Validates that the expression can be used in == or <> expression.
+ * Cannot not be NULL TRUE or FALSE litterals.
+ *
+ * @param expr
+ */
+ public static void checkEqualOperand(Expression expr)
+ {
+ if (expr instanceof ConstantExpression)
+ {
+ Object value = ((ConstantExpression) expr).getValue();
+ if (value == null)
+ {
+ throw new RuntimeException("'" + expr + "' cannot be compared.");
+ }
+ }
+ }
+
+ /**
+ *
+ * @param left
+ * @param right
+ */
+ private static void checkEqualOperandCompatability(Expression left, Expression right)
+ {
+ if ((left instanceof ConstantExpression) && (right instanceof ConstantExpression))
+ {
+ if ((left instanceof BooleanExpression) && !(right instanceof BooleanExpression))
+ {
+ throw new RuntimeException("'" + left + "' cannot be compared with '" + right + "'");
+ }
+ }
+ }
+
+ /**
+ * @param left
+ * @param right
+ */
+ public ComparisonExpression(Expression left, Expression right)
+ {
+ super(left, right);
+ }
+
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+ Comparable lv = (Comparable) left.evaluate(message);
+ if (lv == null)
+ {
+ return null;
+ }
+
+ Comparable rv = (Comparable) right.evaluate(message);
+ if (rv == null)
+ {
+ return null;
+ }
+
+ return compare(lv, rv);
+ }
+
+ protected Boolean compare(Comparable lv, Comparable rv)
+ {
+ Class lc = lv.getClass();
+ Class rc = rv.getClass();
+ // If the the objects are not of the same type,
+ // try to convert up to allow the comparison.
+ if (lc != rc)
+ {
+ if (lc == Byte.class)
+ {
+ if (rc == Short.class)
+ {
+ lv = new Short(((Number) lv).shortValue());
+ }
+ else if (rc == Integer.class)
+ {
+ lv = new Integer(((Number) lv).intValue());
+ }
+ else if (rc == Long.class)
+ {
+ lv = new Long(((Number) lv).longValue());
+ }
+ else if (rc == Float.class)
+ {
+ lv = new Float(((Number) lv).floatValue());
+ }
+ else if (rc == Double.class)
+ {
+ lv = new Double(((Number) lv).doubleValue());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Short.class)
+ {
+ if (rc == Integer.class)
+ {
+ lv = new Integer(((Number) lv).intValue());
+ }
+ else if (rc == Long.class)
+ {
+ lv = new Long(((Number) lv).longValue());
+ }
+ else if (rc == Float.class)
+ {
+ lv = new Float(((Number) lv).floatValue());
+ }
+ else if (rc == Double.class)
+ {
+ lv = new Double(((Number) lv).doubleValue());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Integer.class)
+ {
+ if (rc == Long.class)
+ {
+ lv = new Long(((Number) lv).longValue());
+ }
+ else if (rc == Float.class)
+ {
+ lv = new Float(((Number) lv).floatValue());
+ }
+ else if (rc == Double.class)
+ {
+ lv = new Double(((Number) lv).doubleValue());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Long.class)
+ {
+ if (rc == Integer.class)
+ {
+ rv = new Long(((Number) rv).longValue());
+ }
+ else if (rc == Float.class)
+ {
+ lv = new Float(((Number) lv).floatValue());
+ }
+ else if (rc == Double.class)
+ {
+ lv = new Double(((Number) lv).doubleValue());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Float.class)
+ {
+ if (rc == Integer.class)
+ {
+ rv = new Float(((Number) rv).floatValue());
+ }
+ else if (rc == Long.class)
+ {
+ rv = new Float(((Number) rv).floatValue());
+ }
+ else if (rc == Double.class)
+ {
+ lv = new Double(((Number) lv).doubleValue());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Double.class)
+ {
+ if (rc == Integer.class)
+ {
+ rv = new Double(((Number) rv).doubleValue());
+ }
+ else if (rc == Long.class)
+ {
+ rv = new Double(((Number) rv).doubleValue());
+ }
+ else if (rc == Float.class)
+ {
+ rv = new Float(((Number) rv).doubleValue());
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+
+ return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ protected abstract boolean asBoolean(int answer);
+
+ public boolean matches(AMQMessage message) throws AMQException
+ {
+ Object object = evaluate(message);
+
+ return (object != null) && (object == Boolean.TRUE);
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java
new file mode 100644
index 0000000000..0e729cc521
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java
@@ -0,0 +1,210 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import java.math.BigDecimal;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * Represents a constant expression
+ */
+public class ConstantExpression implements Expression
+{
+
+ static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression
+ {
+ public BooleanConstantExpression(Object value)
+ {
+ super(value);
+ }
+
+ public boolean matches(AMQMessage message) throws AMQException
+ {
+ Object object = evaluate(message);
+
+ return (object != null) && (object == Boolean.TRUE);
+ }
+ }
+
+ public static final BooleanConstantExpression NULL = new BooleanConstantExpression(null);
+ public static final BooleanConstantExpression TRUE = new BooleanConstantExpression(Boolean.TRUE);
+ public static final BooleanConstantExpression FALSE = new BooleanConstantExpression(Boolean.FALSE);
+
+ private Object value;
+
+ public static ConstantExpression createFromDecimal(String text)
+ {
+
+ // Strip off the 'l' or 'L' if needed.
+ if (text.endsWith("l") || text.endsWith("L"))
+ {
+ text = text.substring(0, text.length() - 1);
+ }
+
+ Number value;
+ try
+ {
+ value = new Long(text);
+ }
+ catch (NumberFormatException e)
+ {
+ // The number may be too big to fit in a long.
+ value = new BigDecimal(text);
+ }
+
+ long l = value.longValue();
+ if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE))
+ {
+ value = new Integer(value.intValue());
+ }
+
+ return new ConstantExpression(value);
+ }
+
+ public static ConstantExpression createFromHex(String text)
+ {
+ Number value = new Long(Long.parseLong(text.substring(2), 16));
+ long l = value.longValue();
+ if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE))
+ {
+ value = new Integer(value.intValue());
+ }
+
+ return new ConstantExpression(value);
+ }
+
+ public static ConstantExpression createFromOctal(String text)
+ {
+ Number value = new Long(Long.parseLong(text, 8));
+ long l = value.longValue();
+ if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE))
+ {
+ value = new Integer(value.intValue());
+ }
+
+ return new ConstantExpression(value);
+ }
+
+ public static ConstantExpression createFloat(String text)
+ {
+ Number value = new Double(text);
+
+ return new ConstantExpression(value);
+ }
+
+ public ConstantExpression(Object value)
+ {
+ this.value = value;
+ }
+
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+ return value;
+ }
+
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ if (value == null)
+ {
+ return "NULL";
+ }
+
+ if (value instanceof Boolean)
+ {
+ return ((Boolean) value).booleanValue() ? "TRUE" : "FALSE";
+ }
+
+ if (value instanceof String)
+ {
+ return encodeString((String) value);
+ }
+
+ return value.toString();
+ }
+
+ /**
+ * TODO: more efficient hashCode()
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return toString().hashCode();
+ }
+
+ /**
+ * TODO: more efficient hashCode()
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object o)
+ {
+
+ if ((o == null) || !this.getClass().equals(o.getClass()))
+ {
+ return false;
+ }
+
+ return toString().equals(o.toString());
+
+ }
+
+ /**
+ * Encodes the value of string so that it looks like it would look like
+ * when it was provided in a selector.
+ *
+ * @param s
+ * @return
+ */
+ public static String encodeString(String s)
+ {
+ StringBuffer b = new StringBuffer();
+ b.append('\'');
+ for (int i = 0; i < s.length(); i++)
+ {
+ char c = s.charAt(i);
+ if (c == '\'')
+ {
+ b.append(c);
+ }
+
+ b.append(c);
+ }
+
+ b.append('\'');
+
+ return b.toString();
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java
new file mode 100644
index 0000000000..5f646c15db
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java
@@ -0,0 +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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * Represents an expression
+ */
+public interface Expression
+{
+
+ /**
+ * @return the value of this expression
+ */
+ public Object evaluate(AMQMessage message) throws AMQException;
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java
new file mode 100644
index 0000000000..c82de9fa15
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java
@@ -0,0 +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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+public interface FilterManager
+{
+ void add(MessageFilter filter);
+
+ void remove(MessageFilter filter);
+
+ boolean allAllow(AMQMessage msg);
+
+ boolean hasFilters();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java
new file mode 100644
index 0000000000..311f0680ec
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.common.AMQPFilterTypes;
+import org.apache.qpid.framing.FieldTable;
+
+
+public class FilterManagerFactory
+{
+ //private final static Logger _logger = LoggerFactory.getLogger(FilterManagerFactory.class);
+ private final static org.apache.log4j.Logger _logger = org.apache.log4j.Logger.getLogger(FilterManagerFactory.class);
+
+ //fixme move to a common class so it can be refered to from client code.
+
+ public static FilterManager createManager(FieldTable filters) throws AMQException
+ {
+ FilterManager manager = null;
+
+ if (filters != null)
+ {
+
+ manager = new SimpleFilterManager();
+
+ if(filters.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()))
+ {
+ String selector = filters.getString(AMQPFilterTypes.JMS_SELECTOR.getValue());
+
+ if (selector != null && !selector.equals(""))
+ {
+ manager.add(new JMSSelectorFilter(selector));
+ }
+
+ }
+
+ if (filters.containsKey(AMQPFilterTypes.NO_CONSUME.getValue()))
+ {
+ manager.add(new NoConsumerFilter());
+ }
+
+
+
+ //If we added no filters don't bear the overhead of having an filter manager
+ if (!manager.hasFilters())
+ {
+ manager = null;
+ }
+ }
+ else
+ {
+ _logger.debug("No Filters found.");
+ }
+
+
+ return manager;
+
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java
new file mode 100644
index 0000000000..2061803d65
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.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.filter;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.filter.jms.selector.SelectorParser;
+import org.apache.qpid.server.queue.AMQMessage;
+
+
+
+public class JMSSelectorFilter implements MessageFilter
+{
+ private final static Logger _logger = org.apache.log4j.Logger.getLogger(JMSSelectorFilter.class);
+
+ private String _selector;
+ private BooleanExpression _matcher;
+
+ public JMSSelectorFilter(String selector) throws AMQException
+ {
+ _selector = selector;
+ _logger.info("Created JMSSelectorFilter with selector:" + _selector);
+
+
+ _matcher = new SelectorParser().parse(selector);
+
+
+ }
+
+ public boolean matches(AMQMessage message)
+ {
+ try
+ {
+ boolean match = _matcher.matches(message);
+ _logger.info(message + " match(" + match + ") selector(" + System.identityHashCode(_selector) + "):" + _selector);
+ return match;
+ }
+ 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.
+ }
+ return false;
+ }
+
+ public String getSelector()
+ {
+ return _selector;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java
new file mode 100644
index 0000000000..c8cbdb2125
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java
@@ -0,0 +1,110 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * A filter performing a comparison of two objects
+ */
+public abstract class LogicExpression extends BinaryExpression implements BooleanExpression
+{
+
+ public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue)
+ {
+ return new LogicExpression(lvalue, rvalue)
+ {
+
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+
+ Boolean lv = (Boolean) left.evaluate(message);
+ // Can we do an OR shortcut??
+ if ((lv != null) && lv.booleanValue())
+ {
+ return Boolean.TRUE;
+ }
+
+ Boolean rv = (Boolean) right.evaluate(message);
+
+ return (rv == null) ? null : rv;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "OR";
+ }
+ };
+ }
+
+ public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue)
+ {
+ return new LogicExpression(lvalue, rvalue)
+ {
+
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+
+ Boolean lv = (Boolean) left.evaluate(message);
+
+ // Can we do an AND shortcut??
+ if (lv == null)
+ {
+ return null;
+ }
+
+ if (!lv.booleanValue())
+ {
+ return Boolean.FALSE;
+ }
+
+ Boolean rv = (Boolean) right.evaluate(message);
+
+ return (rv == null) ? null : rv;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "AND";
+ }
+ };
+ }
+
+ /**
+ * @param left
+ * @param right
+ */
+ public LogicExpression(BooleanExpression left, BooleanExpression right)
+ {
+ super(left, right);
+ }
+
+ public abstract Object evaluate(AMQMessage message) throws AMQException;
+
+ public boolean matches(AMQMessage message) throws AMQException
+ {
+ Object object = evaluate(message);
+
+ return (object != null) && (object == Boolean.TRUE);
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java
new file mode 100644
index 0000000000..e6bfe974d5
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.filter;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+public interface MessageFilter
+{
+ boolean matches(AMQMessage message) throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java
new file mode 100644
index 0000000000..47ca930d12
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.filter;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+public class NoConsumerFilter implements MessageFilter
+{
+ private final static Logger _logger = org.apache.log4j.Logger.getLogger(NoConsumerFilter.class);
+
+
+ public NoConsumerFilter() throws AMQException
+ {
+ _logger.info("Created NoConsumerFilter");
+ }
+
+ public boolean matches(AMQMessage message)
+ {
+ return true;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java
new file mode 100644
index 0000000000..5ab360ca19
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java
@@ -0,0 +1,322 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.CommonContentHeaderProperties;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * Represents a property expression
+ */
+public class PropertyExpression implements Expression
+{
+ // Constants - defined the same as JMS
+ private static final int NON_PERSISTENT = 1;
+ private static final int PERSISTENT = 2;
+ private static final int DEFAULT_PRIORITY = 4;
+
+ private static final Logger _logger = org.apache.log4j.Logger.getLogger(PropertyExpression.class);
+
+ private static final HashMap<String, Expression> JMS_PROPERTY_EXPRESSIONS = new HashMap<String, Expression>();
+
+ static
+ {
+ JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+ //TODO
+ return null;
+ }
+ });
+ JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+ try
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString replyTo = _properties.getReplyTo();
+
+ return (replyTo == null) ? null : replyTo.toString();
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+
+ return null;
+ }
+
+ }
+
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("JMSType", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+ try
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString type = _properties.getType();
+
+ return (type == null) ? null : type.toString();
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+
+ return null;
+ }
+
+ }
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+ try
+ {
+ int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT;
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("JMSDeliveryMode is :" + mode);
+ }
+
+ return mode;
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+ }
+
+ return NON_PERSISTENT;
+ }
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+ try
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+
+ return (int) _properties.getPriority();
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+ }
+
+ return DEFAULT_PRIORITY;
+ }
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+
+ try
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString messageId = _properties.getMessageId();
+
+ return (messageId == null) ? null : messageId;
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+
+ return null;
+ }
+
+ }
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+
+ try
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+
+ return _properties.getTimestamp();
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+
+ return null;
+ }
+
+ }
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+
+ try
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString correlationId = _properties.getCorrelationId();
+
+ return (correlationId == null) ? null : correlationId.toString();
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+
+ return null;
+ }
+
+ }
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+
+ try
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+
+ return _properties.getExpiration();
+ }
+ catch (AMQException e)
+ {
+ _logger.warn(e);
+
+ return null;
+ }
+
+ }
+ });
+
+ JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression()
+ {
+ public Object evaluate(AMQMessage message)
+ {
+ return message.isRedelivered();
+ }
+ });
+
+ }
+
+ private final String name;
+ private final Expression jmsPropertyExpression;
+
+ public PropertyExpression(String name)
+ {
+ this.name = name;
+ jmsPropertyExpression = JMS_PROPERTY_EXPRESSIONS.get(name);
+ }
+
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+
+ if (jmsPropertyExpression != null)
+ {
+ return jmsPropertyExpression.evaluate(message);
+ }
+ else
+ {
+
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties) message.getContentHeaderBody().properties;
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Looking up property:" + name);
+ _logger.debug("Properties are:" + _properties.getHeaders().keySet());
+ }
+
+ return _properties.getHeaders().getObject(name);
+ }
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object o)
+ {
+
+ if ((o == null) || !this.getClass().equals(o.getClass()))
+ {
+ return false;
+ }
+
+ return name.equals(((PropertyExpression) o).name);
+
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java
new file mode 100644
index 0000000000..62a45f5420
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.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.server.filter;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+public class SimpleFilterManager implements FilterManager
+{
+ private final Logger _logger = Logger.getLogger(SimpleFilterManager.class);
+
+ private final ConcurrentLinkedQueue<MessageFilter> _filters;
+
+ public SimpleFilterManager()
+ {
+ _logger.debug("Creating SimpleFilterManager");
+ _filters = new ConcurrentLinkedQueue<MessageFilter>();
+ }
+
+ public void add(MessageFilter filter)
+ {
+ _filters.add(filter);
+ }
+
+ public void remove(MessageFilter filter)
+ {
+ _filters.remove(filter);
+ }
+
+ public boolean allAllow(AMQMessage msg)
+ {
+ for (MessageFilter filter : _filters)
+ {
+ try
+ {
+ if (!filter.matches(msg))
+ {
+ return false;
+ }
+ }
+ catch (AMQException e)
+ {
+ //fixme
+ e.printStackTrace();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean hasFilters()
+ {
+ return !_filters.isEmpty();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java
new file mode 100644
index 0000000000..83b4ed5358
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java
@@ -0,0 +1,337 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+/**
+ * An expression which performs an operation on two expression values
+ */
+public abstract class UnaryExpression implements Expression
+{
+
+ private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE);
+ protected Expression right;
+
+ public static Expression createNegate(Expression left)
+ {
+ return new UnaryExpression(left)
+ {
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+ Object rvalue = right.evaluate(message);
+ if (rvalue == null)
+ {
+ return null;
+ }
+
+ if (rvalue instanceof Number)
+ {
+ return negate((Number) rvalue);
+ }
+
+ return null;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "-";
+ }
+ };
+ }
+
+ public static BooleanExpression createInExpression(PropertyExpression right, List elements, final boolean not)
+ {
+
+ // Use a HashSet if there are many elements.
+ Collection t;
+ if (elements.size() == 0)
+ {
+ t = null;
+ }
+ else if (elements.size() < 5)
+ {
+ t = elements;
+ }
+ else
+ {
+ t = new HashSet(elements);
+ }
+
+ final Collection inList = t;
+
+ return new BooleanUnaryExpression(right)
+ {
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+
+ Object rvalue = right.evaluate(message);
+ if (rvalue == null)
+ {
+ return null;
+ }
+
+ if (rvalue.getClass() != String.class)
+ {
+ return null;
+ }
+
+ if (((inList != null) && inList.contains(rvalue)) ^ not)
+ {
+ return Boolean.TRUE;
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer answer = new StringBuffer();
+ answer.append(right);
+ answer.append(" ");
+ answer.append(getExpressionSymbol());
+ answer.append(" ( ");
+
+ int count = 0;
+ for (Iterator i = inList.iterator(); i.hasNext();)
+ {
+ Object o = (Object) i.next();
+ if (count != 0)
+ {
+ answer.append(", ");
+ }
+
+ answer.append(o);
+ count++;
+ }
+
+ answer.append(" )");
+
+ return answer.toString();
+ }
+
+ public String getExpressionSymbol()
+ {
+ if (not)
+ {
+ return "NOT IN";
+ }
+ else
+ {
+ return "IN";
+ }
+ }
+ };
+ }
+
+ abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression
+ {
+ public BooleanUnaryExpression(Expression left)
+ {
+ super(left);
+ }
+
+ public boolean matches(AMQMessage message) throws AMQException
+ {
+ Object object = evaluate(message);
+
+ return (object != null) && (object == Boolean.TRUE);
+ }
+ }
+ ;
+
+ public static BooleanExpression createNOT(BooleanExpression left)
+ {
+ return new BooleanUnaryExpression(left)
+ {
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+ Boolean lvalue = (Boolean) right.evaluate(message);
+ if (lvalue == null)
+ {
+ return null;
+ }
+
+ return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "NOT";
+ }
+ };
+ }
+
+ public static BooleanExpression createXPath(final String xpath)
+ {
+ return new XPathExpression(xpath);
+ }
+
+ public static BooleanExpression createXQuery(final String xpath)
+ {
+ return new XQueryExpression(xpath);
+ }
+
+ public static BooleanExpression createBooleanCast(Expression left)
+ {
+ return new BooleanUnaryExpression(left)
+ {
+ public Object evaluate(AMQMessage message) throws AMQException
+ {
+ Object rvalue = right.evaluate(message);
+ if (rvalue == null)
+ {
+ return null;
+ }
+
+ if (!rvalue.getClass().equals(Boolean.class))
+ {
+ return Boolean.FALSE;
+ }
+
+ return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public String toString()
+ {
+ return right.toString();
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "";
+ }
+ };
+ }
+
+ private static Number negate(Number left)
+ {
+ Class clazz = left.getClass();
+ if (clazz == Integer.class)
+ {
+ return new Integer(-left.intValue());
+ }
+ else if (clazz == Long.class)
+ {
+ return new Long(-left.longValue());
+ }
+ else if (clazz == Float.class)
+ {
+ return new Float(-left.floatValue());
+ }
+ else if (clazz == Double.class)
+ {
+ return new Double(-left.doubleValue());
+ }
+ else if (clazz == BigDecimal.class)
+ {
+ // We ussually get a big deciamal when we have Long.MIN_VALUE constant in the
+ // Selector. Long.MIN_VALUE is too big to store in a Long as a positive so we store it
+ // as a Big decimal. But it gets Negated right away.. to here we try to covert it back
+ // to a Long.
+ BigDecimal bd = (BigDecimal) left;
+ bd = bd.negate();
+
+ if (BD_LONG_MIN_VALUE.compareTo(bd) == 0)
+ {
+ return new Long(Long.MIN_VALUE);
+ }
+
+ return bd;
+ }
+ else
+ {
+ throw new RuntimeException("Don't know how to negate: " + left);
+ }
+ }
+
+ public UnaryExpression(Expression left)
+ {
+ this.right = left;
+ }
+
+ public Expression getRight()
+ {
+ return right;
+ }
+
+ public void setRight(Expression expression)
+ {
+ right = expression;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "(" + getExpressionSymbol() + " " + right.toString() + ")";
+ }
+
+ /**
+ * TODO: more efficient hashCode()
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return toString().hashCode();
+ }
+
+ /**
+ * TODO: more efficient hashCode()
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object o)
+ {
+
+ if ((o == null) || !this.getClass().equals(o.getClass()))
+ {
+ return false;
+ }
+
+ return toString().equals(o.toString());
+
+ }
+
+ /**
+ * Returns the symbol that represents this binary expression. For example, addition is
+ * represented by "+"
+ *
+ * @return
+ */
+ public abstract String getExpressionSymbol();
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java
new file mode 100644
index 0000000000..f5454afae5
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java
@@ -0,0 +1,126 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.server.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Used to evaluate an XPath Expression in a JMS selector.
+ */
+public final class XPathExpression implements BooleanExpression {
+
+ private static final Logger log = Logger.getLogger(XPathExpression.class);
+ private static final String EVALUATOR_SYSTEM_PROPERTY = "org.apache.qpid.server.filter.XPathEvaluatorClassName";
+ private static final String DEFAULT_EVALUATOR_CLASS_NAME=XalanXPathEvaluator.class.getName();
+
+ private static final Constructor EVALUATOR_CONSTRUCTOR;
+
+ static {
+ String cn = System.getProperty(EVALUATOR_SYSTEM_PROPERTY, DEFAULT_EVALUATOR_CLASS_NAME);
+ Constructor m = null;
+ try {
+ try {
+ m = getXPathEvaluatorConstructor(cn);
+ } catch (Throwable e) {
+ log.warn("Invalid "+XPathEvaluator.class.getName()+" implementation: "+cn+", reason: "+e,e);
+ cn = DEFAULT_EVALUATOR_CLASS_NAME;
+ try {
+ m = getXPathEvaluatorConstructor(cn);
+ } catch (Throwable e2) {
+ log.error("Default XPath evaluator could not be loaded",e);
+ }
+ }
+ } finally {
+ EVALUATOR_CONSTRUCTOR = m;
+ }
+ }
+
+ private static Constructor getXPathEvaluatorConstructor(String cn) throws ClassNotFoundException, SecurityException, NoSuchMethodException {
+ Class c = XPathExpression.class.getClassLoader().loadClass(cn);
+ if( !XPathEvaluator.class.isAssignableFrom(c) ) {
+ throw new ClassCastException(""+c+" is not an instance of "+XPathEvaluator.class);
+ }
+ return c.getConstructor(new Class[]{String.class});
+ }
+
+ private final String xpath;
+ private final XPathEvaluator evaluator;
+
+ static public interface XPathEvaluator {
+ public boolean evaluate(AMQMessage message) throws AMQException;
+ }
+
+ XPathExpression(String xpath) {
+ this.xpath = xpath;
+ this.evaluator = createEvaluator(xpath);
+ }
+
+ private XPathEvaluator createEvaluator(String xpath2) {
+ try {
+ return (XPathEvaluator)EVALUATOR_CONSTRUCTOR.newInstance(new Object[]{xpath});
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if( cause instanceof RuntimeException ) {
+ throw (RuntimeException)cause;
+ }
+ throw new RuntimeException("Invalid XPath Expression: "+xpath+" reason: "+e.getMessage(), e);
+ } catch (Throwable e) {
+ throw new RuntimeException("Invalid XPath Expression: "+xpath+" reason: "+e.getMessage(), e);
+ }
+ }
+
+ public Object evaluate(AMQMessage message) throws AMQException {
+// try {
+//FIXME this is flow to disk work
+// if( message.isDropped() )
+// return null;
+ return evaluator.evaluate(message) ? Boolean.TRUE : Boolean.FALSE;
+// } catch (IOException e) {
+//
+// JMSException exception = new JMSException(e.getMessage());
+// exception.initCause(e);
+// throw exception;
+//
+// }
+
+ }
+
+ public String toString() {
+ return "XPATH "+ConstantExpression.encodeString(xpath);
+ }
+
+ /**
+ * @param message
+ * @return true if the expression evaluates to Boolean.TRUE.
+ * @throws AMQException
+ */
+ public boolean matches(AMQMessage message) throws AMQException
+ {
+ Object object = evaluate(message);
+ return object!=null && object==Boolean.TRUE;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java
new file mode 100644
index 0000000000..f5debb607a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.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.filter;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+/**
+ * Used to evaluate an XQuery Expression in a JMS selector.
+ */
+public final class XQueryExpression implements BooleanExpression {
+ private final String xpath;
+
+ XQueryExpression(String xpath) {
+ super();
+ this.xpath = xpath;
+ }
+
+ public Object evaluate(AMQMessage message) throws AMQException {
+ return Boolean.FALSE;
+ }
+
+ public String toString() {
+ return "XQUERY "+ConstantExpression.encodeString(xpath);
+ }
+
+ /**
+ * @param message
+ * @return true if the expression evaluates to Boolean.TRUE.
+ * @throws AMQException
+ */
+ public boolean matches(AMQMessage message) throws AMQException
+ {
+ Object object = evaluate(message);
+ return object!=null && object==Boolean.TRUE;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java
new file mode 100644
index 0000000000..35d770fd5d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java
@@ -0,0 +1,102 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import java.io.ByteArrayInputStream;
+import java.io.StringReader;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.xpath.CachedXPathAPI;
+import org.w3c.dom.Document;
+import org.w3c.dom.traversal.NodeIterator;
+import org.xml.sax.InputSource;
+
+public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator {
+
+ private final String xpath;
+
+ public XalanXPathEvaluator(String xpath) {
+ this.xpath = xpath;
+ }
+
+ public boolean evaluate(AMQMessage m) throws AMQException
+ {
+ // TODO - we would have to check the content type and then evaluate the content
+ // here... is this really a feature we wish to implement? - RobG
+ /*
+
+ if( m instanceof TextMessage ) {
+ String text = ((TextMessage)m).getText();
+ return evaluate(text);
+ } else if ( m instanceof BytesMessage ) {
+ BytesMessage bm = (BytesMessage) m;
+ byte data[] = new byte[(int) bm.getBodyLength()];
+ bm.readBytes(data);
+ return evaluate(data);
+ }
+ */
+ return false;
+
+ }
+
+ private boolean evaluate(byte[] data) {
+ try {
+
+ InputSource inputSource = new InputSource(new ByteArrayInputStream(data));
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ DocumentBuilder dbuilder = factory.newDocumentBuilder();
+ Document doc = dbuilder.parse(inputSource);
+
+ CachedXPathAPI cachedXPathAPI = new CachedXPathAPI();
+ NodeIterator iterator = cachedXPathAPI.selectNodeIterator(doc,xpath);
+ return iterator.nextNode()!=null;
+
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ private boolean evaluate(String text) {
+ try {
+ InputSource inputSource = new InputSource(new StringReader(text));
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ DocumentBuilder dbuilder = factory.newDocumentBuilder();
+ Document doc = dbuilder.parse(inputSource);
+
+ // We should associated the cachedXPathAPI object with the message being evaluated
+ // since that should speedup subsequent xpath expressions.
+ CachedXPathAPI cachedXPathAPI = new CachedXPathAPI();
+ NodeIterator iterator = cachedXPathAPI.selectNodeIterator(doc,xpath);
+ return iterator.nextNode()!=null;
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java
new file mode 100644
index 0000000000..a6972475a6
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java
@@ -0,0 +1,67 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicAckBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class BasicAckMethodHandler implements StateAwareMethodListener<BasicAckBody>
+{
+ private static final Logger _log = Logger.getLogger(BasicAckMethodHandler.class);
+
+ private static final BasicAckMethodHandler _instance = new BasicAckMethodHandler();
+
+ public static BasicAckMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicAckMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicAckBody> evt) throws AMQException
+ {
+ AMQProtocolSession protocolSession = stateManager.getProtocolSession();
+ BasicAckBody body = evt.getMethod();
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Ack(Tag:" + body.deliveryTag + ":Mult:" + body.multiple + ") received on channel " + evt.getChannelId());
+ }
+
+ final AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ // this method throws an AMQException if the delivery tag is not known
+ channel.acknowledgeMessage(body.deliveryTag, body.multiple);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java
new file mode 100644
index 0000000000..8bab96a11b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicCancelBody;
+import org.apache.qpid.framing.BasicCancelOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.log4j.Logger;
+
+public class BasicCancelMethodHandler implements StateAwareMethodListener<BasicCancelBody>
+{
+ private static final Logger _log = Logger.getLogger(BasicCancelMethodHandler.class);
+
+ private static final BasicCancelMethodHandler _instance = new BasicCancelMethodHandler();
+
+ public static BasicCancelMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicCancelMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicCancelBody> evt) throws AMQException
+ {
+ AMQProtocolSession protocolSession = stateManager.getProtocolSession();
+
+ final AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+ final BasicCancelBody body = evt.getMethod();
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("BasicCancel: for:" + body.consumerTag +
+ " nowait:" + body.nowait);
+ }
+
+ channel.unsubscribeConsumer(protocolSession, body.consumerTag);
+ if (!body.nowait)
+ {
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ final AMQFrame responseFrame = BasicCancelOkBody.createAMQFrame(evt.getChannelId(),
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ body.consumerTag); // consumerTag
+ protocolSession.writeFrame(responseFrame);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
new file mode 100644
index 0000000000..d1e4ae6aa6
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
@@ -0,0 +1,173 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicConsumeBody;
+import org.apache.qpid.framing.BasicConsumeOkBody;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.ConnectionCloseBody;
+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.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 BasicConsumeMethodHandler _instance = new BasicConsumeMethodHandler();
+
+ public static BasicConsumeMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicConsumeMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicConsumeBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ BasicConsumeBody body = evt.getMethod();
+ final int channelId = evt.getChannelId();
+
+ AMQChannel channel = session.getChannel(channelId);
+
+ VirtualHost vHost = session.getVirtualHost();
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+ else
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("BasicConsume: from '" + body.queue +
+ "' for:" + body.consumerTag +
+ " nowait:" + body.nowait +
+ " args:" + body.arguments);
+ }
+
+ AMQQueue queue = body.queue == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.queue);
+
+ if (queue == null)
+ {
+ if (_log.isTraceEnabled())
+ {
+ _log.trace("No queue for '" + body.queue + "'");
+ }
+ if (body.queue != null)
+ {
+ String msg = "No such queue, '" + body.queue + "'";
+ throw body.getChannelException(AMQConstant.NOT_FOUND, msg);
+ }
+ else
+ {
+ String msg = "No queue name provided, no default queue defined.";
+ throw body.getConnectionException(AMQConstant.NOT_ALLOWED, msg);
+ }
+ }
+ else
+ {
+
+ if (body.consumerTag != null)
+ {
+ body.consumerTag = body.consumerTag.intern();
+ }
+
+ try
+ {
+ AMQShortString consumerTag = channel.subscribeToQueue(body.consumerTag, queue, session, !body.noAck,
+ body.arguments, body.noLocal, body.exclusive);
+ if (!body.nowait)
+ {
+ // 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(channelId,
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ consumerTag)); // consumerTag
+ }
+
+ //now allow queue to start async processing of any backlog of messages
+ queue.deliverAsync();
+ }
+ catch (org.apache.qpid.AMQInvalidArgumentException ise)
+ {
+ _log.debug("Closing connection due to invalid selector");
+ // Why doesn't this ChannelException work.
+// throw body.getChannelException(AMQConstant.INVALID_ARGUMENT, ise.getMessage());
+ // 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(ChannelCloseBody.createAMQFrame(channelId,
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ BasicConsumeBody.getClazz((byte) 8, (byte) 0), // classId
+ BasicConsumeBody.getMethod((byte) 8, (byte) 0), // methodId
+ AMQConstant.INVALID_ARGUMENT.getCode(), // replyCode
+ new AMQShortString(ise.getMessage()))); // replyText
+ }
+ catch (ConsumerTagNotUniqueException e)
+ {
+ AMQShortString msg = new AMQShortString("Non-unique consumer tag, '" + body.consumerTag + "'");
+ // If the above doesn't work then perhaps this is wrong too.
+// throw body.getConnectionException(AMQConstant.NOT_ALLOWED,
+// "Non-unique consumer tag, '" + body.consumerTag + "'");
+ // 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(ConnectionCloseBody.createAMQFrame(channelId,
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ BasicConsumeBody.getClazz((byte) 8, (byte) 0), // classId
+ BasicConsumeBody.getMethod((byte) 8, (byte) 0), // methodId
+ AMQConstant.NOT_ALLOWED.getCode(), // replyCode
+ msg)); // replyText
+ }
+ catch (AMQQueue.ExistingExclusiveSubscription e)
+ {
+ throw body.getChannelException(AMQConstant.ACCESS_REFUSED,
+ "Cannot subscribe to queue "
+ + queue.getName()
+ + " as it already has an existing exclusive consumer");
+ }
+ catch (AMQQueue.ExistingSubscriptionPreventsExclusive e)
+ {
+ throw body.getChannelException(AMQConstant.ACCESS_REFUSED,
+ "Cannot subscribe to queue "
+ + queue.getName()
+ + " exclusively as it already has a consumer");
+ }
+
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
new file mode 100644
index 0000000000..782a89c704
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicGetBody;
+import org.apache.qpid.framing.BasicGetEmptyBody;
+import org.apache.qpid.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.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetBody>
+{
+ private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class);
+
+ private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler();
+
+ public static BasicGetMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicGetMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicGetBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ BasicGetBody body = evt.getMethod();
+ final int channelId = evt.getChannelId();
+ VirtualHost vHost = session.getVirtualHost();
+
+ AMQChannel channel = session.getChannel(channelId);
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+ else
+ {
+ AMQQueue queue = body.queue == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.queue);
+
+ if (queue == null)
+ {
+ _log.info("No queue for '" + body.queue + "'");
+ if(body.queue!=null)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_FOUND,
+ "No such queue, '" + body.queue + "'");
+ }
+ else
+ {
+ throw body.getConnectionException(AMQConstant.NOT_ALLOWED,
+ "No queue name provided, no default queue defined.");
+ }
+ }
+ else
+ {
+ if(!queue.performGet(session, channel, !body.noAck))
+ {
+
+
+ // TODO - set clusterId
+ session.writeFrame(BasicGetEmptyBody.createAMQFrame(channelId, body.getMajor(), body.getMinor(), null));
+ }
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
new file mode 100644
index 0000000000..68d4483df3
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.BasicPublishBody;
+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.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class BasicPublishMethodHandler implements StateAwareMethodListener<BasicPublishBody>
+{
+ private static final Logger _log = Logger.getLogger(BasicPublishMethodHandler.class);
+
+ private static final BasicPublishMethodHandler _instance = new BasicPublishMethodHandler();
+
+
+ public static BasicPublishMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicPublishMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicPublishBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ final BasicPublishBody body = evt.getMethod();
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Publish received on channel " + evt.getChannelId());
+ }
+
+ // TODO: check the delivery tag field details - is it unique across the broker or per subscriber?
+ if (body.exchange == null)
+ {
+ body.exchange = ExchangeDefaults.DEFAULT_EXCHANGE_NAME;
+
+ }
+ else
+ {
+ body.exchange = body.exchange.intern();
+ }
+ VirtualHost vHost = session.getVirtualHost();
+ Exchange e = vHost.getExchangeRegistry().getExchange(body.exchange);
+ // if the exchange does not exist we raise a channel exception
+ if (e == null)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange name");
+ }
+ else
+ {
+ // The partially populated BasicDeliver frame plus the received route body
+ // is stored in the channel. Once the final body frame has been received
+ // it is routed to the exchange.
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ if(body.routingKey != null)
+ {
+ body.routingKey = body.routingKey.intern();
+ }
+
+ MessagePublishInfo info = session.getRegistry().getProtocolVersionMethodConverter().convertToInfo(body);
+ channel.setPublishFrame(info, session);
+ }
+ }
+}
+
+
+
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java
new file mode 100644
index 0000000000..3cd6a87f64
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.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.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicQosBody;
+import org.apache.qpid.framing.BasicQosOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.AMQChannel;
+
+public class BasicQosHandler implements StateAwareMethodListener<BasicQosBody>
+{
+ private static final BasicQosHandler _instance = new BasicQosHandler();
+
+ public static BasicQosHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicQosBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+ if (channel == null)
+ {
+ throw evt.getMethod().getChannelNotFoundException(evt.getChannelId());
+ }
+
+ channel.setPrefetchCount(evt.getMethod().prefetchCount);
+ channel.setPrefetchSize(evt.getMethod().prefetchSize);
+
+ // 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(BasicQosOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0));
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java
new file mode 100644
index 0000000000..a436c35473
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicRecoverBody;
+import org.apache.qpid.framing.BasicRecoverOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class BasicRecoverMethodHandler implements StateAwareMethodListener<BasicRecoverBody>
+{
+ private static final Logger _logger = Logger.getLogger(BasicRecoverMethodHandler.class);
+
+ private static final BasicRecoverMethodHandler _instance = new BasicRecoverMethodHandler();
+
+ public static BasicRecoverMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicRecoverBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ _logger.debug("Recover received on protocol session " + session + " and channel " + evt.getChannelId());
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+ BasicRecoverBody body = evt.getMethod();
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ channel.resend(body.requeue);
+
+ // 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(BasicRecoverOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0));
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
new file mode 100644
index 0000000000..aca485f62a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
@@ -0,0 +1,130 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicRejectBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.ack.UnacknowledgedMessage;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.log4j.Logger;
+
+public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicRejectBody>
+{
+ private static final Logger _logger = Logger.getLogger(BasicRejectMethodHandler.class);
+
+ private static BasicRejectMethodHandler _instance = new BasicRejectMethodHandler();
+
+ public static BasicRejectMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicRejectMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<BasicRejectBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ int channelId = evt.getChannelId();
+
+// if (_logger.isDebugEnabled())
+// {
+// _logger.debug("Rejecting:" + evt.getMethod().deliveryTag +
+// ": Requeue:" + evt.getMethod().requeue +
+//// ": Resend:" + evt.getMethod().resend +
+// " on channel:" + channelId);
+// }
+
+ AMQChannel channel = session.getChannel(channelId);
+
+ if (channel == null)
+ {
+ throw evt.getMethod().getChannelNotFoundException(channelId);
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Rejecting:" + evt.getMethod().deliveryTag +
+ ": Requeue:" + evt.getMethod().requeue +
+ //": Resend:" + evt.getMethod().resend +
+ " on channel:" + channel.debugIdentity());
+ }
+
+ long deliveryTag = evt.getMethod().deliveryTag;
+
+ UnacknowledgedMessage message = channel.getUnacknowledgedMessageMap().get(deliveryTag);
+
+ if (message == null)
+ {
+ _logger.warn("Dropping reject request as message is null for tag:" + deliveryTag);
+// throw evt.getMethod().getChannelException(AMQConstant.NOT_FOUND, "Delivery Tag(" + deliveryTag + ")not known");
+ }
+ else
+ {
+ if (message.queue == null || message.queue.isDeleted())
+ {
+ _logger.warn("Message's Queue as already been purged, unable to Reject. " +
+ "Dropping message should use Dead Letter Queue");
+ //sendtoDeadLetterQueue(msg)
+ return;
+ }
+
+ if (!message.message.isReferenced())
+ {
+ _logger.warn("Message as already been purged, unable to Reject.");
+ return;
+ }
+
+
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Rejecting: DT:" + deliveryTag + "-" + message.message.debugIdentity() +
+ ": Requeue:" + evt.getMethod().requeue +
+ //": Resend:" + evt.getMethod().resend +
+ " on channel:" + channel.debugIdentity());
+ }
+
+ // If we haven't requested message to be resent to this consumer then reject it from ever getting it.
+ //if (!evt.getMethod().resend)
+ {
+ message.message.reject(message.message.getDeliveredSubscription(message.queue));
+ }
+
+ if (evt.getMethod().requeue)
+ {
+ channel.requeue(deliveryTag);
+ }
+ else
+ {
+ _logger.warn("Dropping message as requeue not required and there is no dead letter queue");
+ //sendtoDeadLetterQueue(AMQMessage message)
+// message.queue = channel.getDefaultDeadLetterQueue();
+// channel.requeue(deliveryTag);
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java
new file mode 100644
index 0000000000..1f4f1f9221
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java
@@ -0,0 +1,78 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.AMQChannel;
+
+public class ChannelCloseHandler implements StateAwareMethodListener<ChannelCloseBody>
+{
+ private static final Logger _logger = Logger.getLogger(ChannelCloseHandler.class);
+
+ private static ChannelCloseHandler _instance = new ChannelCloseHandler();
+
+ public static ChannelCloseHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelCloseHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ChannelCloseBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ ChannelCloseBody body = evt.getMethod();
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Received channel close for id " + evt.getChannelId() + " citing class " + body.classId +
+ " and method " + body.methodId);
+ }
+ int channelId = evt.getChannelId();
+
+ AMQChannel channel = session.getChannel(channelId);
+
+ if (channel == null)
+ {
+ throw body.getConnectionException(AMQConstant.CHANNEL_ERROR, "Trying to close unknown channel");
+ }
+
+ session.closeChannel(channelId);
+ // Client requested closure so we don't wait for ok we send it
+ stateManager.getProtocolSession().closeChannelOk(channelId);
+
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame response = ChannelCloseOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0);
+ session.writeFrame(response);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java
new file mode 100644
index 0000000000..ad5604e7ea
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ChannelCloseOkHandler implements StateAwareMethodListener<ChannelCloseOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ChannelCloseOkHandler.class);
+
+ private static ChannelCloseOkHandler _instance = new ChannelCloseOkHandler();
+
+ public static ChannelCloseOkHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelCloseOkHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ChannelCloseOkBody> evt) throws AMQException
+ {
+ int channelId = evt.getChannelId();
+ _logger.info("Received channel-close-ok for channel-id " + channelId);
+
+ // Let the Protocol Session know the channel is now closed.
+ stateManager.getProtocolSession().closeChannelOk(channelId);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java
new file mode 100644
index 0000000000..bfa170cfc5
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ChannelFlowBody;
+import org.apache.qpid.framing.ChannelFlowOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ChannelFlowHandler implements StateAwareMethodListener<ChannelFlowBody>
+{
+ private static final Logger _logger = Logger.getLogger(ChannelFlowHandler.class);
+
+ private static ChannelFlowHandler _instance = new ChannelFlowHandler();
+
+ public static ChannelFlowHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelFlowHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ChannelFlowBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ ChannelFlowBody body = evt.getMethod();
+
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ channel.setSuspended(!body.active);
+ _logger.debug("Channel.Flow for channel " + evt.getChannelId() + ", active=" + body.active);
+
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame response = ChannelFlowOkBody.createAMQFrame(evt.getChannelId(),
+ (byte)8, (byte)0, // AMQP version (major, minor)
+ body.active); // active
+ session.writeFrame(response);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java
new file mode 100644
index 0000000000..03fc7a3926
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ChannelOpenBody;
+import org.apache.qpid.framing.ChannelOpenOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class ChannelOpenHandler implements StateAwareMethodListener<ChannelOpenBody>
+{
+ private static ChannelOpenHandler _instance = new ChannelOpenHandler();
+
+ public static ChannelOpenHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelOpenHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ChannelOpenBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+
+ final AMQChannel channel = new AMQChannel(session,evt.getChannelId(), virtualHost.getMessageStore(),
+ virtualHost.getExchangeRegistry());
+ session.addChannel(channel);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame response = ChannelOpenOkBody.createAMQFrame(evt.getChannelId(), (byte)8, (byte)0);
+ session.writeFrame(response);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java
new file mode 100644
index 0000000000..b086cad67f
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.ConnectionCloseOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ConnectionCloseMethodHandler implements StateAwareMethodListener<ConnectionCloseBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionCloseMethodHandler.class);
+
+ private static ConnectionCloseMethodHandler _instance = new ConnectionCloseMethodHandler();
+
+ public static ConnectionCloseMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionCloseMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ConnectionCloseBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ final ConnectionCloseBody body = evt.getMethod();
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("ConnectionClose received with reply code/reply text " + body.replyCode + "/" +
+ body.replyText + " for " + session);
+ }
+ try
+ {
+ session.closeSession();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error closing protocol session: " + e, e);
+ }
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ final AMQFrame response = ConnectionCloseOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0);
+ session.writeFrame(response);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java
new file mode 100644
index 0000000000..853f4df435
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ConnectionCloseOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ConnectionCloseOkMethodHandler implements StateAwareMethodListener<ConnectionCloseOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionCloseOkMethodHandler.class);
+
+ private static ConnectionCloseOkMethodHandler _instance = new ConnectionCloseOkMethodHandler();
+
+ public static ConnectionCloseOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionCloseOkMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ConnectionCloseOkBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ //todo should this not do more than just log the method?
+ _logger.info("Received Connection-close-ok");
+
+ try
+ {
+ stateManager.changeState(AMQState.CONNECTION_CLOSED);
+ session.closeSession();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error closing protocol session: " + e, e);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java
new file mode 100644
index 0000000000..30a40c5a75
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java
@@ -0,0 +1,118 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionOpenBody;
+import org.apache.qpid.framing.ConnectionOpenOkBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+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>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionOpenMethodHandler.class);
+
+ private static ConnectionOpenMethodHandler _instance = new ConnectionOpenMethodHandler();
+
+ public static ConnectionOpenMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionOpenMethodHandler()
+ {
+ }
+
+ private static AMQShortString generateClientID()
+ {
+ return new AMQShortString(Long.toString(System.currentTimeMillis()));
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ConnectionOpenBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ ConnectionOpenBody body = evt.getMethod();
+
+ //ignore leading '/'
+ String virtualHostName;
+ if ((body.virtualHost != null) && body.virtualHost.charAt(0) == '/')
+ {
+ virtualHostName = new StringBuilder(body.virtualHost.subSequence(1, body.virtualHost.length())).toString();
+ }
+ else
+ {
+ virtualHostName = body.virtualHost == null ? null : String.valueOf(body.virtualHost);
+ }
+
+ VirtualHost virtualHost = stateManager.getVirtualHostRegistry().getVirtualHost(virtualHostName);
+
+ if (virtualHost == null)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_FOUND, "Unknown virtual host: '" + virtualHostName + "'");
+ }
+ else
+ {
+ 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() + "'");
+ }
+
+ // See Spec (0.8.2). Section 3.1.2 Virtual Hosts
+ if (session.getContextKey() == null)
+ {
+ session.setContextKey(generateClientID());
+ }
+
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame response = ConnectionOpenOkBody.createAMQFrame((short) 0,
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ body.virtualHost);
+ stateManager.changeState(AMQState.CONNECTION_OPEN);
+ session.writeFrame(response);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java
new file mode 100644
index 0000000000..fef00942a0
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java
@@ -0,0 +1,141 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.ConnectionSecureBody;
+import org.apache.qpid.framing.ConnectionSecureOkBody;
+import org.apache.qpid.framing.ConnectionTuneBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.HeartbeatConfig;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener<ConnectionSecureOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionSecureOkMethodHandler.class);
+
+ private static ConnectionSecureOkMethodHandler _instance = new ConnectionSecureOkMethodHandler();
+
+ public static ConnectionSecureOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionSecureOkMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ConnectionSecureOkBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ ConnectionSecureOkBody body = evt.getMethod();
+
+ //fixme Vhost not defined yet
+ //session.getVirtualHost().getAuthenticationManager();
+ AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager();
+
+ SaslServer ss = session.getSaslServer();
+ if (ss == null)
+ {
+ throw new AMQException("No SASL context set up in session");
+ }
+
+ AuthenticationResult authResult = authMgr.authenticate(ss, body.response);
+ switch (authResult.status)
+ {
+ case ERROR:
+ // Can't do this as we violate protocol. Need to send Close
+ // throw new AMQException(AMQConstant.NOT_ALLOWED.getCode(), AMQConstant.NOT_ALLOWED.getName());
+ _logger.info("Authentication failed");
+ stateManager.changeState(AMQState.CONNECTION_CLOSING);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame close = ConnectionCloseBody.createAMQFrame(0,
+ (byte)8, (byte)0, // AMQP version (major, minor)
+ ConnectionCloseBody.getClazz((byte)8, (byte)0), // classId
+ ConnectionCloseBody.getMethod((byte)8, (byte)0), // methodId
+ AMQConstant.NOT_ALLOWED.getCode(), // replyCode
+ AMQConstant.NOT_ALLOWED.getName()); // replyText
+ session.writeFrame(close);
+ disposeSaslServer(session);
+ break;
+ case SUCCESS:
+ _logger.info("Connected as: " + ss.getAuthorizationID());
+ stateManager.changeState(AMQState.CONNECTION_NOT_TUNED);
+ // TODO: Check the value of channelMax here: This should be the max
+ // value of a 2-byte unsigned integer (as channel is only 2 bytes on the wire),
+ // not Integer.MAX_VALUE (which is signed 4 bytes).
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame tune = ConnectionTuneBody.createAMQFrame(0,
+ (byte)8, (byte)0, // AMQP version (major, minor)
+ Integer.MAX_VALUE, // channelMax
+ ConnectionStartOkMethodHandler.getConfiguredFrameSize(), // frameMax
+ HeartbeatConfig.getInstance().getDelay()); // heartbeat
+ session.writeFrame(tune);
+ session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID()));
+ disposeSaslServer(session);
+ break;
+ case CONTINUE:
+ stateManager.changeState(AMQState.CONNECTION_NOT_AUTH);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame challenge = ConnectionSecureBody.createAMQFrame(0,
+ (byte)8, (byte)0, // AMQP version (major, minor)
+ authResult.challenge); // challenge
+ session.writeFrame(challenge);
+ }
+ }
+
+ private void disposeSaslServer(AMQProtocolSession ps)
+ {
+ SaslServer ss = ps.getSaslServer();
+ if (ss != null)
+ {
+ ps.setSaslServer(null);
+ try
+ {
+ ss.dispose();
+ }
+ catch (SaslException e)
+ {
+ _logger.error("Error disposing of Sasl server: " + e);
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java
new file mode 100644
index 0000000000..bb9eeed1d4
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java
@@ -0,0 +1,173 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ConnectionSecureBody;
+import org.apache.qpid.framing.ConnectionStartOkBody;
+import org.apache.qpid.framing.ConnectionTuneBody;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.HeartbeatConfig;
+import org.apache.qpid.server.protocol.AMQMinaProtocolSession;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+
+public class ConnectionStartOkMethodHandler implements StateAwareMethodListener<ConnectionStartOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionStartOkMethodHandler.class);
+
+ private static ConnectionStartOkMethodHandler _instance = new ConnectionStartOkMethodHandler();
+
+ private static final int DEFAULT_FRAME_SIZE = 65536;
+
+ public static StateAwareMethodListener<ConnectionStartOkBody> getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionStartOkMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ConnectionStartOkBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ final ConnectionStartOkBody body = evt.getMethod();
+ _logger.info("SASL Mechanism selected: " + body.mechanism);
+ _logger.info("Locale selected: " + body.locale);
+
+ AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager();//session.getVirtualHost().getAuthenticationManager();
+
+ SaslServer ss = null;
+ try
+ {
+ ss = authMgr.createSaslServer(String.valueOf(body.mechanism), session.getLocalFQDN());
+
+ if (ss == null)
+ {
+ throw body.getConnectionException(AMQConstant.RESOURCE_ERROR, "Unable to create SASL Server:" + body.mechanism
+ );
+ }
+
+ session.setSaslServer(ss);
+
+ AuthenticationResult authResult = authMgr.authenticate(ss, body.response);
+
+ //save clientProperties
+ if (session.getClientProperties() == null)
+ {
+ session.setClientProperties(body.clientProperties);
+ }
+
+ switch (authResult.status)
+ {
+ case ERROR:
+ _logger.info("Authentication failed");
+ stateManager.changeState(AMQState.CONNECTION_CLOSING);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame close = ConnectionCloseBody.createAMQFrame(0,
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ ConnectionCloseBody.getClazz((byte) 8, (byte) 0), // classId
+ ConnectionCloseBody.getMethod((byte) 8, (byte) 0), // methodId
+ AMQConstant.NOT_ALLOWED.getCode(), // replyCode
+ AMQConstant.NOT_ALLOWED.getName()); // replyText
+ session.writeFrame(close);
+ disposeSaslServer(session);
+ break;
+
+ case SUCCESS:
+ _logger.info("Connected as: " + ss.getAuthorizationID());
+ session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID()));
+
+ stateManager.changeState(AMQState.CONNECTION_NOT_TUNED);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame tune = ConnectionTuneBody.createAMQFrame(0,
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ Integer.MAX_VALUE, // channelMax
+ getConfiguredFrameSize(), // frameMax
+ HeartbeatConfig.getInstance().getDelay()); // heartbeat
+ session.writeFrame(tune);
+ break;
+ case CONTINUE:
+ stateManager.changeState(AMQState.CONNECTION_NOT_AUTH);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame challenge = ConnectionSecureBody.createAMQFrame(0,
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ authResult.challenge); // challenge
+ session.writeFrame(challenge);
+ }
+ }
+ catch (SaslException e)
+ {
+ disposeSaslServer(session);
+ throw new AMQException("SASL error: " + e, e);
+ }
+ }
+
+ private void disposeSaslServer(AMQProtocolSession ps)
+ {
+ SaslServer ss = ps.getSaslServer();
+ if (ss != null)
+ {
+ ps.setSaslServer(null);
+ try
+ {
+ ss.dispose();
+ }
+ catch (SaslException e)
+ {
+ _logger.error("Error disposing of Sasl server: " + e);
+ }
+ }
+ }
+
+ static int getConfiguredFrameSize()
+ {
+ final Configuration config = ApplicationRegistry.getInstance().getConfiguration();
+ final int framesize = config.getInt("advanced.framesize", DEFAULT_FRAME_SIZE);
+ _logger.info("Framesize set to " + framesize);
+ return framesize;
+ }
+}
+
+
+
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java
new file mode 100644
index 0000000000..ab7695955c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ConnectionTuneOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ConnectionTuneOkMethodHandler implements StateAwareMethodListener<ConnectionTuneOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionTuneOkMethodHandler.class);
+
+ private static ConnectionTuneOkMethodHandler _instance = new ConnectionTuneOkMethodHandler();
+
+ public static ConnectionTuneOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ConnectionTuneOkBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ ConnectionTuneOkBody body = evt.getMethod();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(body);
+ }
+ stateManager.changeState(AMQState.CONNECTION_NOT_OPENED);
+ session.initHeartbeats(body.heartbeat);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java
new file mode 100644
index 0000000000..162c03b025
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java
@@ -0,0 +1,205 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ExchangeBoundBody;
+import org.apache.qpid.framing.ExchangeBoundOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class ExchangeBoundHandler implements StateAwareMethodListener<ExchangeBoundBody>
+{
+ private static final ExchangeBoundHandler _instance = new ExchangeBoundHandler();
+
+ public static final int OK = 0;
+
+ public static final int EXCHANGE_NOT_FOUND = 1;
+
+ public static final int QUEUE_NOT_FOUND = 2;
+
+ public static final int NO_BINDINGS = 3;
+
+ public static final int QUEUE_NOT_BOUND = 4;
+
+ public static final int NO_QUEUE_BOUND_WITH_RK = 5;
+
+ public static final int SPECIFIC_QUEUE_NOT_BOUND_WITH_RK = 6;
+
+ public static ExchangeBoundHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ExchangeBoundHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ExchangeBoundBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+
+ // 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.
+ byte major = (byte)8;
+ byte minor = (byte)0;
+
+ ExchangeBoundBody body = evt.getMethod();
+
+ AMQShortString exchangeName = body.exchange;
+ AMQShortString queueName = body.queue;
+ AMQShortString routingKey = body.routingKey;
+ if (exchangeName == null)
+ {
+ throw new AMQException("Exchange exchange must not be null");
+ }
+ Exchange exchange = virtualHost.getExchangeRegistry().getExchange(exchangeName);
+ AMQFrame response;
+ if (exchange == null)
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ EXCHANGE_NOT_FOUND, // replyCode
+ new AMQShortString("Exchange " + exchangeName + " not found")); // replyText
+ }
+ else if (routingKey == null)
+ {
+ if (queueName == null)
+ {
+ if (exchange.hasBindings())
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ OK, // replyCode
+ null); // replyText
+ }
+ else
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ NO_BINDINGS, // replyCode
+ null); // replyText
+ }
+ }
+ else
+ {
+
+ AMQQueue queue = queueRegistry.getQueue(queueName);
+ if (queue == null)
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ QUEUE_NOT_FOUND, // replyCode
+ new AMQShortString("Queue " + queueName + " not found")); // replyText
+ }
+ else
+ {
+ if (exchange.isBound(queue))
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ OK, // replyCode
+ null); // replyText
+ }
+ else
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ QUEUE_NOT_BOUND, // replyCode
+ new AMQShortString("Queue " + queueName + " not bound to exchange " + exchangeName)); // replyText
+ }
+ }
+ }
+ }
+ else if (queueName != null)
+ {
+ AMQQueue queue = queueRegistry.getQueue(queueName);
+ if (queue == null)
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ QUEUE_NOT_FOUND, // replyCode
+ new AMQShortString("Queue " + queueName + " not found")); // replyText
+ }
+ else
+ {
+ if (exchange.isBound(body.routingKey, queue))
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ OK, // replyCode
+ null); // replyText
+ }
+ else
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ SPECIFIC_QUEUE_NOT_BOUND_WITH_RK, // replyCode
+ new AMQShortString("Queue " + queueName + " not bound with routing key " +
+ body.routingKey + " to exchange " + exchangeName)); // replyText
+ }
+ }
+ }
+ else
+ {
+ if (exchange.isBound(body.routingKey))
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ OK, // replyCode
+ null); // replyText
+ }
+ else
+ {
+ // AMQP version change: Be aware of possible changes to parameter order as versions change.
+ response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(),
+ major, minor, // AMQP version (major, minor)
+ NO_QUEUE_BOUND_WITH_RK, // replyCode
+ new AMQShortString("No queue bound with routing key " + body.routingKey +
+ " to exchange " + exchangeName)); // replyText
+ }
+ }
+ session.writeFrame(response);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java
new file mode 100644
index 0000000000..8ab8366d9e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java
@@ -0,0 +1,114 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQConnectionException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQUnknownExchangeType;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ExchangeDeclareBody;
+import org.apache.qpid.framing.ExchangeDeclareOkBody;
+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.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class ExchangeDeclareHandler implements StateAwareMethodListener<ExchangeDeclareBody>
+{
+ private static final Logger _logger = Logger.getLogger(ExchangeDeclareHandler.class);
+
+ private static final ExchangeDeclareHandler _instance = new ExchangeDeclareHandler();
+
+ public static ExchangeDeclareHandler getInstance()
+ {
+ return _instance;
+ }
+
+
+
+ private ExchangeDeclareHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ExchangeDeclareBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+ ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory();
+
+ final ExchangeDeclareBody body = evt.getMethod();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Request to declare exchange of type " + body.type + " with name " + body.exchange);
+ }
+ synchronized(exchangeRegistry)
+ {
+ Exchange exchange = exchangeRegistry.getExchange(body.exchange);
+
+
+
+ if (exchange == null)
+ {
+ if(body.passive && ((body.type == null) || body.type.length() ==0))
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange: " + body.exchange);
+ }
+ else
+ {
+ try
+ {
+
+ exchange = exchangeFactory.createExchange(body.exchange == null ? null : body.exchange.intern(),
+ body.type == null ? null : body.type.intern(),
+ body.durable,
+ body.passive, body.ticket);
+ exchangeRegistry.registerExchange(exchange);
+ }
+ catch(AMQUnknownExchangeType e)
+ {
+ throw body.getConnectionException(AMQConstant.COMMAND_INVALID, "Unknown exchange: " + body.exchange,e);
+ }
+ }
+ }
+ else if (!exchange.getType().equals(body.type))
+ {
+
+ throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.exchange + " of type " + exchange.getType() + " to " + body.type +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor());
+ }
+
+ }
+ if(!body.nowait)
+ {
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame response = ExchangeDeclareOkBody.createAMQFrame(evt.getChannelId(), (byte)8, (byte)0);
+ session.writeFrame(response);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java
new file mode 100644
index 0000000000..f9926c399c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class ExchangeDeleteHandler implements StateAwareMethodListener<ExchangeDeleteBody>
+{
+ private static final ExchangeDeleteHandler _instance = new ExchangeDeleteHandler();
+
+ public static ExchangeDeleteHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ExchangeDeleteHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<ExchangeDeleteBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+
+ ExchangeDeleteBody body = evt.getMethod();
+ try
+ {
+ exchangeRegistry.unregisterExchange(body.exchange, body.ifUnused);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame response = ExchangeDeleteOkBody.createAMQFrame(evt.getChannelId(), (byte)8, (byte)0);
+ session.writeFrame(response);
+ }
+ catch (ExchangeInUseException e)
+ {
+ // TODO: sort out consistent channel close mechanism that does all clean up etc.
+ }
+
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java
new file mode 100644
index 0000000000..ac516b6133
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import java.util.concurrent.Executor;
+
+/**
+ * An executor that executes the task on the current thread.
+ */
+public class OnCurrentThreadExecutor implements Executor
+{
+ public void execute(Runnable command)
+ {
+ command.run();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
new file mode 100644
index 0000000000..3e68069838
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
@@ -0,0 +1,135 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInvalidRoutingKeyException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.QueueBindBody;
+import org.apache.qpid.framing.QueueBindOkBody;
+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.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody>
+{
+ private static final Logger _log = Logger.getLogger(QueueBindHandler.class);
+
+ private static final QueueBindHandler _instance = new QueueBindHandler();
+
+ public static QueueBindHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private QueueBindHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<QueueBindBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+
+ final QueueBindBody body = evt.getMethod();
+ final AMQQueue queue;
+ if (body.queue == null)
+ {
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ queue = channel.getDefaultQueue();
+
+ if (queue == null)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "No default queue defined on channel and queue was null");
+ }
+
+ if (body.routingKey == null)
+ {
+ body.routingKey = queue.getName();
+ }
+ }
+ else
+ {
+ queue = queueRegistry.getQueue(body.queue);
+ }
+
+ if (queue == null)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.queue + " does not exist.");
+ }
+ final Exchange exch = exchangeRegistry.getExchange(body.exchange);
+ if (exch == null)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Exchange " + body.exchange + " does not exist.");
+ }
+
+ if (body.routingKey != null)
+ {
+ body.routingKey = body.routingKey.intern();
+ }
+
+ try
+ {
+ if (!exch.isBound(body.routingKey, body.arguments, queue))
+ {
+ queue.bind(body.routingKey, body.arguments, exch);
+ }
+ }
+ catch (AMQInvalidRoutingKeyException rke)
+ {
+ throw body.getChannelException(AMQConstant.INVALID_ROUTING_KEY, body.routingKey.toString());
+ }
+ catch (AMQException e)
+ {
+ throw body.getChannelException(AMQConstant.CHANNEL_ERROR, e.toString());
+ }
+
+ if (_log.isInfoEnabled())
+ {
+ _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + body.routingKey);
+ }
+ if (!body.nowait)
+ {
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ final AMQFrame response = QueueBindOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0);
+ session.writeFrame(response);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
new file mode 100644
index 0000000000..29697542be
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
@@ -0,0 +1,206 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import java.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.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.QueueDeclareBody;
+import org.apache.qpid.framing.QueueDeclareOkBody;
+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;
+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.state.AMQStateManager;
+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.commons.configuration.Configuration;
+
+public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclareBody>
+{
+ private static final Logger _log = Logger.getLogger(QueueDeclareHandler.class);
+
+ private static final QueueDeclareHandler _instance = new QueueDeclareHandler();
+
+ public static QueueDeclareHandler getInstance()
+ {
+ return _instance;
+ }
+
+ @Configured(path = "queue.auto_register", defaultValue = "false")
+ public boolean autoRegister;
+
+ private final AtomicInteger _counter = new AtomicInteger();
+
+
+ protected QueueDeclareHandler()
+ {
+ Configurator.configure(this);
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<QueueDeclareBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry();
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+ MessageStore store = virtualHost.getMessageStore();
+
+ QueueDeclareBody body = evt.getMethod();
+
+ // if we aren't given a queue name, we create one which we return to the client
+ if (body.queue == null)
+ {
+ body.queue = createName();
+ }
+
+ AMQQueue queue;
+ //TODO: do we need to check that the queue already exists with exactly the same "configuration"?
+
+ synchronized (queueRegistry)
+ {
+
+
+
+ if (((queue = queueRegistry.getQueue(body.queue)) == null))
+ {
+ if(body.queue != null)
+ {
+ body.queue = body.queue.intern();
+ }
+
+ if (body.passive)
+ {
+ String msg = "Queue: " + body.queue + " not found on VirtualHost(" + virtualHost + ").";
+ throw body.getChannelException(AMQConstant.NOT_FOUND, msg);
+ }
+ else
+ {
+ queue = createQueue(body, virtualHost, session);
+ if (queue.isDurable() && !queue.isAutoDelete())
+ {
+ store.createQueue(queue);
+ }
+ queueRegistry.registerQueue(queue);
+ if (autoRegister)
+ {
+ Exchange defaultExchange = exchangeRegistry.getDefaultExchange();
+
+ queue.bind(body.queue, null, defaultExchange);
+ _log.info("Queue " + body.queue + " bound to default exchange(" + defaultExchange.getName() + ")");
+ }
+ }
+ }
+ else if (queue.getOwner() != null && !session.getContextKey().equals(queue.getOwner()))
+ {
+ throw body.getChannelException(AMQConstant.ALREADY_EXISTS, "Cannot declare queue('" + body.queue + "'),"
+ + " as exclusive queue with same name "
+ + "declared on another client ID('"
+ + queue.getOwner() + "')");
+ }
+
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ //set this as the default queue on the channel:
+ channel.setDefaultQueue(queue);
+ }
+
+ if (!body.nowait)
+ {
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame response = QueueDeclareOkBody.createAMQFrame(evt.getChannelId(),
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ queue.getConsumerCount(), // consumerCount
+ queue.getMessageCount(), // messageCount
+ body.queue); // queue
+ _log.info("Queue " + body.queue + " declared successfully");
+ session.writeFrame(response);
+ }
+ }
+
+ protected AMQShortString createName()
+ {
+ return new AMQShortString("tmp_" + UUID.randomUUID());
+ }
+
+ protected AMQQueue createQueue(QueueDeclareBody body, VirtualHost virtualHost, final AMQProtocolSession session)
+ throws AMQException
+ {
+ final QueueRegistry registry = virtualHost.getQueueRegistry();
+ AMQShortString owner = body.exclusive ? session.getContextKey() : null;
+ final AMQQueue queue = new AMQQueue(body.queue, body.durable, owner, body.autoDelete, virtualHost);
+ final AMQShortString queueName = queue.getName();
+
+ if (body.exclusive && !body.durable)
+ {
+ final AMQProtocolSession.Task deleteQueueTask =
+ new AMQProtocolSession.Task()
+ {
+ public void doTask(AMQProtocolSession session) throws AMQException
+ {
+ if (registry.getQueue(queueName) == queue)
+ {
+ queue.delete();
+ }
+ }
+ };
+
+ session.addSessionCloseTask(deleteQueueTask);
+
+ queue.addQueueDeleteTask(new AMQQueue.Task()
+ {
+ public void doTask(AMQQueue queue)
+ {
+ session.removeSessionCloseTask(deleteQueueTask);
+ }
+ });
+ }// if exclusive and not durable
+
+ Configuration virtualHostDefaultQueueConfiguration = VirtualHostConfiguration.getDefaultQueueConfiguration(queue);
+ if (virtualHostDefaultQueueConfiguration != null)
+ {
+ Configurator.configure(queue, virtualHostDefaultQueueConfiguration);
+ }
+
+ return queue;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
new file mode 100644
index 0000000000..eb7089afdc
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.QueueDeleteBody;
+import org.apache.qpid.framing.QueueDeleteOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+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;
+
+public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteBody>
+{
+ private static final QueueDeleteHandler _instance = new QueueDeleteHandler();
+
+ public static QueueDeleteHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private final boolean _failIfNotFound;
+
+ public QueueDeleteHandler()
+ {
+ this(true);
+ }
+
+ public QueueDeleteHandler(boolean failIfNotFound)
+ {
+ _failIfNotFound = failIfNotFound;
+
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<QueueDeleteBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+ MessageStore store = virtualHost.getMessageStore();
+
+ QueueDeleteBody body = evt.getMethod();
+ AMQQueue queue;
+ if (body.queue == null)
+ {
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ //get the default queue on the channel:
+ queue = channel.getDefaultQueue();
+ }
+ else
+ {
+ queue = queueRegistry.getQueue(body.queue);
+ }
+
+ if (queue == null)
+ {
+ if (_failIfNotFound)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.queue + " does not exist.");
+ }
+ }
+ else
+ {
+ if (body.ifEmpty && !queue.isEmpty())
+ {
+ throw body.getChannelException(AMQConstant.IN_USE, "Queue: " + body.queue + " is not empty.");
+ }
+ else if (body.ifUnused && !queue.isUnused())
+ {
+ // TODO - Error code
+ throw body.getChannelException(AMQConstant.IN_USE, "Queue: " + body.queue + " is still used.");
+
+ }
+ else
+ {
+ int purged = queue.delete(body.ifUnused, body.ifEmpty);
+
+ if (queue.isDurable())
+ {
+ store.removeQueue(queue.getName());
+ }
+
+ // 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(QueueDeleteOkBody.createAMQFrame(evt.getChannelId(),
+ (byte) 8, (byte) 0, // AMQP version (major, minor)
+ purged)); // messageCount
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
new file mode 100644
index 0000000000..3e1937bb43
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.QueuePurgeBody;
+import org.apache.qpid.framing.QueuePurgeOkBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.AMQChannel;
+
+public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBody>
+{
+ private static final QueuePurgeHandler _instance = new QueuePurgeHandler();
+
+ public static QueuePurgeHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private final boolean _failIfNotFound;
+
+ public QueuePurgeHandler()
+ {
+ this(true);
+ }
+
+ public QueuePurgeHandler(boolean failIfNotFound)
+ {
+ _failIfNotFound = failIfNotFound;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<QueuePurgeBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ QueuePurgeBody body = evt.getMethod();
+ AMQQueue queue;
+ if(body.queue == null)
+ {
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(evt.getChannelId());
+ }
+
+ //get the default queue on the channel:
+ queue = channel.getDefaultQueue();
+
+ if(queue == null)
+ {
+ if(_failIfNotFound)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified.");
+ }
+ }
+ }
+ else
+ {
+ queue = queueRegistry.getQueue(body.queue);
+ }
+
+ if(queue == null)
+ {
+ if(_failIfNotFound)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.queue + " does not exist.");
+ }
+ }
+ else
+ {
+ long purged = queue.clearQueue(channel.getStoreContext());
+
+
+ if(!body.nowait)
+ {
+ // 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(QueuePurgeOkBody.createAMQFrame(evt.getChannelId(),
+ (byte)8, (byte)0, // AMQP version (major, minor)
+ purged)); // messageCount
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java
new file mode 100644
index 0000000000..3d7ec286f9
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.TxCommitBody;
+import org.apache.qpid.framing.TxCommitOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class TxCommitHandler implements StateAwareMethodListener<TxCommitBody>
+{
+ private static final Logger _log = Logger.getLogger(TxCommitHandler.class);
+
+ private static TxCommitHandler _instance = new TxCommitHandler();
+
+ public static TxCommitHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private TxCommitHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<TxCommitBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ try
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Commit received on channel " + evt.getChannelId());
+ }
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw evt.getMethod().getChannelNotFoundException(evt.getChannelId());
+ }
+
+ channel.commit();
+ // 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(TxCommitOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0));
+ channel.processReturns(session);
+ }
+ catch (AMQException e)
+ {
+ throw evt.getMethod().getChannelException(e.getErrorCode(), "Failed to commit: " + e.getMessage());
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java
new file mode 100644
index 0000000000..f747f7a840
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.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.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.TxRollbackBody;
+import org.apache.qpid.framing.TxRollbackOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class TxRollbackHandler implements StateAwareMethodListener<TxRollbackBody>
+{
+ private static TxRollbackHandler _instance = new TxRollbackHandler();
+
+ public static TxRollbackHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private TxRollbackHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<TxRollbackBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ try
+ {
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw evt.getMethod().getChannelNotFoundException(evt.getChannelId());
+ }
+
+ channel.rollback();
+ // 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(TxRollbackOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0));
+ //Now resend all the unacknowledged messages back to the original subscribers.
+ //(Must be done after the TxnRollback-ok response).
+ // Why, are we not allowed to send messages back to client before the ok method?
+ channel.resend(false);
+ }
+ catch (AMQException e)
+ {
+ throw evt.getMethod().getChannelException(e.getErrorCode(), "Failed to rollback: " + e.getMessage());
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java
new file mode 100644
index 0000000000..a9e478e301
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.TxSelectBody;
+import org.apache.qpid.framing.TxSelectOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.AMQChannel;
+
+public class TxSelectHandler implements StateAwareMethodListener<TxSelectBody>
+{
+ private static TxSelectHandler _instance = new TxSelectHandler();
+
+ public static TxSelectHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private TxSelectHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQMethodEvent<TxSelectBody> evt) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ AMQChannel channel = session.getChannel(evt.getChannelId());
+
+ if (channel == null)
+ {
+ throw evt.getMethod().getChannelNotFoundException(evt.getChannelId());
+ }
+
+ channel.setLocalTransactional();
+
+ // 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(TxSelectOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0));
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java b/Final/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java
new file mode 100644
index 0000000000..c08fae4e4e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java
@@ -0,0 +1,110 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.jms;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+
+public class JmsConsumer
+{
+ private int _prefetchValue;
+
+ private PrefetchUnits _prefetchUnits;
+
+ private boolean _noLocal;
+
+ private boolean _autoAck;
+
+ private boolean _exclusive;
+
+ private AMQProtocolSession _protocolSession;
+
+ public enum PrefetchUnits
+ {
+ OCTETS,
+ MESSAGES
+ }
+
+ public int getPrefetchValue()
+ {
+ return _prefetchValue;
+ }
+
+ public void setPrefetchValue(int prefetchValue)
+ {
+ _prefetchValue = prefetchValue;
+ }
+
+ public PrefetchUnits getPrefetchUnits()
+ {
+ return _prefetchUnits;
+ }
+
+ public void setPrefetchUnits(PrefetchUnits prefetchUnits)
+ {
+ _prefetchUnits = prefetchUnits;
+ }
+
+ public boolean isNoLocal()
+ {
+ return _noLocal;
+ }
+
+ public void setNoLocal(boolean noLocal)
+ {
+ _noLocal = noLocal;
+ }
+
+ public boolean isAutoAck()
+ {
+ return _autoAck;
+ }
+
+ public void setAutoAck(boolean autoAck)
+ {
+ _autoAck = autoAck;
+ }
+
+ public boolean isExclusive()
+ {
+ return _exclusive;
+ }
+
+ public void setExclusive(boolean exclusive)
+ {
+ _exclusive = exclusive;
+ }
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+
+ public void setProtocolSession(AMQProtocolSession protocolSession)
+ {
+ _protocolSession = protocolSession;
+ }
+
+ public void deliverMessage() throws AMQException
+ {
+
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java
new file mode 100644
index 0000000000..a2c2bd62a2
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+
+/**
+ * This class provides additinal feature of Notification Broadcaster to the
+ * DefaultManagedObject.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public abstract class AMQManagedObject extends DefaultManagedObject
+ implements NotificationBroadcaster
+{
+ /**
+ * broadcaster support class
+ */
+ protected NotificationBroadcasterSupport _broadcaster = new NotificationBroadcasterSupport();
+
+ /**
+ * sequence number for notifications
+ */
+ protected long _notificationSequenceNumber = 0;
+
+ protected MBeanInfo _mbeanInfo;
+
+ protected AMQManagedObject(Class<?> managementInterface, String typeName)
+ throws NotCompliantMBeanException
+ {
+ super(managementInterface, typeName);
+ buildMBeanInfo();
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo()
+ {
+ return _mbeanInfo;
+ }
+
+ private void buildMBeanInfo() throws NotCompliantMBeanException
+ {
+ _mbeanInfo = new MBeanInfo(this.getClass().getName(),
+ MBeanIntrospector.getMBeanDescription(this.getClass()),
+ MBeanIntrospector.getMBeanAttributesInfo(getManagementInterface()),
+ MBeanIntrospector.getMBeanConstructorsInfo(this.getClass()),
+ MBeanIntrospector.getMBeanOperationsInfo(getManagementInterface()),
+ this.getNotificationInfo());
+ }
+
+
+
+ // notification broadcaster implementation
+
+ public void addNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ {
+ _broadcaster.addNotificationListener(listener, filter, handback);
+ }
+
+ public void removeNotificationListener(NotificationListener listener)
+ throws ListenerNotFoundException
+ {
+ _broadcaster.removeNotificationListener(listener);
+ }
+
+ public MBeanNotificationInfo[] getNotificationInfo()
+ {
+ return null;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java
new file mode 100644
index 0000000000..84526dbc11
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java
@@ -0,0 +1,191 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management;
+
+import javax.management.JMException;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+/**
+ * Provides implementation of the boilerplate ManagedObject interface. Most managed objects should find it useful
+ * to extend this class rather than implementing ManagedObject from scratch.
+ *
+ */
+public abstract class DefaultManagedObject extends StandardMBean implements ManagedObject
+{
+ private Class<?> _managementInterface;
+
+ private String _typeName;
+
+ protected DefaultManagedObject(Class<?> managementInterface, String typeName)
+ throws NotCompliantMBeanException
+ {
+ super(managementInterface);
+ _managementInterface = managementInterface;
+ _typeName = typeName;
+ }
+
+ public String getType()
+ {
+ return _typeName;
+ }
+
+ public Class<?> getManagementInterface()
+ {
+ return _managementInterface;
+ }
+
+ public ManagedObject getParentObject()
+ {
+ return null;
+ }
+
+ public void register() throws AMQException
+ {
+ try
+ {
+ getManagedObjectRegistry().registerObject(this);
+ }
+ catch (JMException e)
+ {
+ throw new AMQException("Error registering managed object " + this + ": " + e, e);
+ }
+ }
+
+ protected ManagedObjectRegistry getManagedObjectRegistry()
+ {
+ return ApplicationRegistry.getInstance().getManagedObjectRegistry();
+ }
+
+ public void unregister() throws AMQException
+ {
+ try
+ {
+ getManagedObjectRegistry().unregisterObject(this);
+ }
+ catch (JMException e)
+ {
+ throw new AMQException("Error unregistering managed object: " + this + ": " + e, e);
+ }
+ }
+
+ public String toString()
+ {
+ return getObjectInstanceName() + "[" + getType() + "]";
+ }
+
+
+ /**
+ * Created the ObjectName as per the JMX Specs
+ * @return ObjectName
+ * @throws MalformedObjectNameException
+ */
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ String name = getObjectInstanceName();
+ StringBuffer objectName = new StringBuffer(ManagedObject.DOMAIN);
+
+ objectName.append(":type=");
+ objectName.append(getHierarchicalType(this));
+
+ objectName.append(",");
+ objectName.append(getHierarchicalName(this));
+ objectName.append("name=").append(name);
+
+ return new ObjectName(objectName.toString());
+ }
+
+ protected ObjectName getObjectNameForSingleInstanceMBean() throws MalformedObjectNameException
+ {
+ StringBuffer objectName = new StringBuffer(ManagedObject.DOMAIN);
+
+ objectName.append(":type=");
+ objectName.append(getHierarchicalType(this));
+
+ String hierarchyName = getHierarchicalName(this);
+ if (hierarchyName != null)
+ {
+ objectName.append(",");
+ objectName.append(hierarchyName.substring(0, hierarchyName.lastIndexOf(",")));
+ }
+
+ return new ObjectName(objectName.toString());
+ }
+
+ protected String getHierarchicalType(ManagedObject obj)
+ {
+ if (obj.getParentObject() != null)
+ {
+ String parentType = getHierarchicalType(obj.getParentObject()).toString();
+ return parentType + "." + obj.getType();
+ }
+ else
+ return obj.getType();
+ }
+
+ protected String getHierarchicalName(ManagedObject obj)
+ {
+ if (obj.getParentObject() != null)
+ {
+ String parentName = obj.getParentObject().getType() + "=" +
+ obj.getParentObject().getObjectInstanceName() + ","+
+ getHierarchicalName(obj.getParentObject());
+
+ return parentName;
+ }
+ else
+ return "";
+ }
+
+ protected static StringBuffer jmxEncode(StringBuffer jmxName, int attrPos)
+ {
+ for (int i = attrPos; i < jmxName.length(); i++)
+ {
+ if (jmxName.charAt(i) == ',')
+ {
+ jmxName.setCharAt(i, ';');
+ }
+ else if (jmxName.charAt(i) == ':')
+ {
+ jmxName.setCharAt(i, '-');
+ }
+ else if (jmxName.charAt(i) == '?' ||
+ jmxName.charAt(i) == '*' ||
+ jmxName.charAt(i) == '\\')
+ {
+ jmxName.insert(i, '\\');
+ i++;
+ }
+ else if (jmxName.charAt(i) == '\n')
+ {
+ jmxName.insert(i, '\\');
+ i++;
+ jmxName.setCharAt(i, 'n');
+ }
+ }
+ return jmxName;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
new file mode 100644
index 0000000000..4caae2b26f
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
@@ -0,0 +1,283 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.MBeanServerForwarder;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AccountNotFoundException;
+import javax.security.sasl.AuthorizeCallback;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class starts up an MBeanserver. If out of the box agent is being used then there are no security features
+ * implemented. To use the security features like user authentication, turn off the jmx options in the "QPID_OPTS" env
+ * variable and use JMXMP connector server. If JMXMP connector is not available, then the standard JMXConnector will be
+ * used, which again doesn't have user authentication.
+ */
+public class JMXManagedObjectRegistry implements ManagedObjectRegistry
+{
+ private static final Logger _log = Logger.getLogger(JMXManagedObjectRegistry.class);
+
+ private final MBeanServer _mbeanServer;
+ private Registry _rmiRegistry;
+ private JMXServiceURL _jmxURL;
+
+ public static final String MANAGEMENT_PORT_CONFIG_PATH = "management.jmxport";
+ public static final int MANAGEMENT_PORT_DEFAULT = 8999;
+
+ public JMXManagedObjectRegistry() throws AMQException
+ {
+ _log.info("Initialising managed object registry using platform MBean server");
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+
+ // Retrieve the config parameters
+ boolean platformServer = appRegistry.getConfiguration().getBoolean("management.platform-mbeanserver", true);
+
+ _mbeanServer =
+ platformServer ? ManagementFactory.getPlatformMBeanServer()
+ : MBeanServerFactory.createMBeanServer(ManagedObject.DOMAIN);
+ }
+
+
+ public void start() throws IOException
+ {
+ // Check if the "QPID_OPTS" is set to use Out of the Box JMXAgent
+ if (areOutOfTheBoxJMXOptionsSet())
+ {
+ _log.info("JMX: Using the out of the box JMX Agent");
+ return;
+ }
+
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+
+ boolean security = appRegistry.getConfiguration().getBoolean("management.security-enabled", false);
+ int port = appRegistry.getConfiguration().getInt(MANAGEMENT_PORT_CONFIG_PATH, MANAGEMENT_PORT_DEFAULT);
+
+ if (security)
+ {
+ // For SASL using JMXMP
+ _jmxURL = new JMXServiceURL("jmxmp", null, port);
+
+ Map env = new HashMap();
+ Map<String, PrincipalDatabase> map = appRegistry.getDatabaseManager().getDatabases();
+ PrincipalDatabase db = null;
+
+ for (Map.Entry<String, PrincipalDatabase> entry : map.entrySet())
+ {
+ if (entry.getValue() instanceof Base64MD5PasswordFilePrincipalDatabase)
+ {
+ db = entry.getValue();
+ break;
+ }
+ else if (entry.getValue() instanceof PlainPasswordFilePrincipalDatabase)
+ {
+ db = entry.getValue();
+ }
+ }
+
+ if (db instanceof Base64MD5PasswordFilePrincipalDatabase)
+ {
+ env.put("jmx.remote.profiles", "SASL/CRAM-MD5");
+ CRAMMD5HashedInitialiser initialiser = new CRAMMD5HashedInitialiser();
+ initialiser.initialise(db);
+ env.put("jmx.remote.sasl.callback.handler", initialiser.getCallbackHandler());
+ }
+ else if (db instanceof PlainPasswordFilePrincipalDatabase)
+ {
+ env.put("jmx.remote.profiles", "SASL/PLAIN");
+ env.put("jmx.remote.sasl.callback.handler", new UserCallbackHandler(db));
+ }
+
+ // Enable the SSL security and server authentication
+ /*
+ SslRMIClientSocketFactory csf = new SslRMIClientSocketFactory();
+ SslRMIServerSocketFactory ssf = new SslRMIServerSocketFactory();
+ env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
+ env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);
+ */
+
+ JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(_jmxURL, env, _mbeanServer);
+ MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance();
+ cs.setMBeanServerForwarder(mbsf);
+ cs.start();
+ _log.warn("JMX: Started JMXConnector server on port '" + port + "' with SASL");
+
+ }
+ else
+ {
+ startJMXConnectorServer(port);
+ _log.warn("JMX: Started JMXConnector server on port '" + port + "' with security disabled");
+ }
+ }
+
+ /**
+ * Starts up an RMIRegistry at configured port and attaches a JMXConnectorServer to it.
+ *
+ * @param port
+ *
+ * @throws IOException
+ */
+ private void startJMXConnectorServer(int port) throws IOException
+ {
+ startRMIRegistry(port);
+ _jmxURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi");
+ JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(_jmxURL, null, _mbeanServer);
+ cs.start();
+ }
+
+ public void registerObject(ManagedObject managedObject) throws JMException
+ {
+ _mbeanServer.registerMBean(managedObject, managedObject.getObjectName());
+ }
+
+ public void unregisterObject(ManagedObject managedObject) throws JMException
+ {
+ _mbeanServer.unregisterMBean(managedObject.getObjectName());
+ }
+
+ /**
+ * Checks is the "QPID_OPTS" env variable is set to use the out of the box JMXAgent.
+ *
+ * @return
+ */
+ private boolean areOutOfTheBoxJMXOptionsSet()
+ {
+ if (System.getProperty("com.sun.management.jmxremote") != null)
+ {
+ return true;
+ }
+
+ if (System.getProperty("com.sun.management.jmxremote.port") != null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Starts the rmi registry at given port
+ *
+ * @param port
+ *
+ * @throws RemoteException
+ */
+ private void startRMIRegistry(int port) throws RemoteException
+ {
+ System.setProperty("java.rmi.server.randomIDs", "true");
+ _rmiRegistry = LocateRegistry.createRegistry(port);
+ }
+
+ // stops the RMIRegistry, if it was running and bound to a port
+ public void close() throws RemoteException
+ {
+ if (_rmiRegistry != null)
+ {
+ // Stopping the RMI registry
+ UnicastRemoteObject.unexportObject(_rmiRegistry, true);
+ }
+ }
+
+ /** This class is used for SASL enabled JMXConnector for performing user authentication. */
+ private class UserCallbackHandler implements CallbackHandler
+ {
+ private final PrincipalDatabase _principalDatabase;
+
+ protected UserCallbackHandler(PrincipalDatabase database)
+ {
+ _principalDatabase = database;
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ // Retrieve callbacks
+ NameCallback ncb = null;
+ PasswordCallback pcb = null;
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ if (callbacks[i] instanceof NameCallback)
+ {
+ ncb = (NameCallback) callbacks[i];
+ }
+ else if (callbacks[i] instanceof PasswordCallback)
+ {
+ pcb = (PasswordCallback) callbacks[i];
+ }
+ else if (callbacks[i] instanceof AuthorizeCallback)
+ {
+ ((AuthorizeCallback) callbacks[i]).setAuthorized(true);
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(callbacks[i]);
+ }
+ }
+
+ boolean authorized = false;
+ // Process retrieval of password; can get password if username is available in NameCallback
+ if ((ncb != null) && (pcb != null))
+ {
+ String username = ncb.getDefaultName();
+ try
+ {
+ authorized = _principalDatabase.verifyPassword(username, pcb.getPassword());
+ }
+ catch (AccountNotFoundException e)
+ {
+ IOException ioe = new IOException("User not authorized. " + e);
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+
+ if (!authorized)
+ {
+ throw new IOException("User not authorized.");
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java
new file mode 100644
index 0000000000..7d42297699
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for MBean attributes. This should be used with getter or setter
+ * methods of attributes.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Inherited
+public @interface MBeanAttribute
+{
+ String name();
+ String description();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java
new file mode 100644
index 0000000000..9138e03085
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for MBean constructors.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.CONSTRUCTOR)
+@Inherited
+public @interface MBeanConstructor
+{
+ String value();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java
new file mode 100644
index 0000000000..448fed3280
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.server.management;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for MBean class.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface MBeanDescription {
+ String value();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java
new file mode 100644
index 0000000000..0c2ec2aebd
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java
@@ -0,0 +1,388 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.NotCompliantMBeanException;
+
+/**
+ * This class is a utility class to introspect the MBean class and the management
+ * interface class for various purposes.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+class MBeanIntrospector {
+
+ private static final String _defaultAttributeDescription = "Management attribute";
+ private static final String _defaultOerationDescription = "Management operation";
+ private static final String _defaultConstructorDescription = "MBean constructor";
+ private static final String _defaultMbeanDescription = "Management interface of the MBean";
+
+ /**
+ * Introspects the management interface class for MBean attributes.
+ * @param interfaceClass
+ * @return MBeanAttributeInfo[]
+ * @throws NotCompliantMBeanException
+ */
+ static MBeanAttributeInfo[] getMBeanAttributesInfo(Class interfaceClass)
+ throws NotCompliantMBeanException
+ {
+ List<MBeanAttributeInfo> attributesList = new ArrayList<MBeanAttributeInfo>();
+
+ /**
+ * Using reflection, all methods of the managemetn interface will be analysed,
+ * and MBeanInfo will be created.
+ */
+ for (Method method : interfaceClass.getMethods())
+ {
+ String name = method.getName();
+ Class<?> resultType = method.getReturnType();
+ MBeanAttributeInfo attributeInfo = null;
+
+ if (isAttributeGetterMethod(method))
+ {
+ String desc = getAttributeDescription(method);
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ resultType.getName(),
+ desc,
+ true,
+ false,
+ false);
+ int index = getIndexIfAlreadyExists(attributeInfo, attributesList);
+ if (index == -1)
+ {
+ attributesList.add(attributeInfo);
+ }
+ else
+ {
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ resultType.getName(),
+ desc,
+ true,
+ true,
+ false);
+ attributesList.set(index, attributeInfo);
+ }
+ }
+ else if (isAttributeSetterMethod(method))
+ {
+ String desc = getAttributeDescription(method);
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ method.getParameterTypes()[0].getName(),
+ desc,
+ false,
+ true,
+ false);
+ int index = getIndexIfAlreadyExists(attributeInfo, attributesList);
+ if (index == -1)
+ {
+ attributesList.add(attributeInfo);
+ }
+ else
+ {
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ method.getParameterTypes()[0].getName(),
+ desc,
+ true,
+ true,
+ false);
+ attributesList.set(index, attributeInfo);
+ }
+ }
+ else if (isAttributeBoolean(method))
+ {
+ attributeInfo = new MBeanAttributeInfo(name.substring(2),
+ resultType.getName(),
+ getAttributeDescription(method),
+ true,
+ false,
+ true);
+ attributesList.add(attributeInfo);
+ }
+ }
+
+ return attributesList.toArray(new MBeanAttributeInfo[0]);
+ }
+
+ /**
+ * Introspects the management interface class for management operations.
+ * @param interfaceClass
+ * @return MBeanOperationInfo[]
+ */
+ static MBeanOperationInfo[] getMBeanOperationsInfo(Class interfaceClass)
+ {
+ List<MBeanOperationInfo> operationsList = new ArrayList<MBeanOperationInfo>();
+
+ for (Method method : interfaceClass.getMethods())
+ {
+ if (!isAttributeGetterMethod(method) &&
+ !isAttributeSetterMethod(method) &&
+ !isAttributeBoolean(method))
+ {
+ operationsList.add(getOperationInfo(method));
+ }
+ }
+
+ return operationsList.toArray(new MBeanOperationInfo[0]);
+ }
+
+ /**
+ * Checks if the method is an attribute getter method.
+ * @param method
+ * @return true if the method is an attribute getter method.
+ */
+ private static boolean isAttributeGetterMethod(Method method)
+ {
+ if (!(method.getName().equals("get")) &&
+ method.getName().startsWith("get") &&
+ method.getParameterTypes().length == 0 &&
+ !method.getReturnType().equals(void.class))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the method is an attribute setter method.
+ * @param method
+ * @return true if the method is an attribute setter method.
+ */
+ private static boolean isAttributeSetterMethod(Method method)
+ {
+ if (!(method.getName().equals("set")) &&
+ method.getName().startsWith("set") &&
+ method.getParameterTypes().length == 1 &&
+ method.getReturnType().equals(void.class))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the attribute is a boolean and the method is a isX kind og method.
+ * @param method
+ * @return true if the method is an attribute isX type of method
+ */
+ private static boolean isAttributeBoolean(Method method)
+ {
+ if (!(method.getName().equals("is")) &&
+ method.getName().startsWith("is") &&
+ method.getParameterTypes().length == 0 &&
+ method.getReturnType().equals(boolean.class))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper method to retrieve the attribute index from the list of attributes.
+ * @param attribute
+ * @param list
+ * @return attribute index no. -1 if attribtue doesn't exist
+ * @throws NotCompliantMBeanException
+ */
+ private static int getIndexIfAlreadyExists(MBeanAttributeInfo attribute,
+ List<MBeanAttributeInfo> list)
+ throws NotCompliantMBeanException
+ {
+ String exceptionMsg = "Conflicting attribute methods for attribute " + attribute.getName();
+
+ for (MBeanAttributeInfo memberAttribute : list)
+ {
+ if (attribute.getName().equals(memberAttribute.getName()))
+ {
+ if (!attribute.getType().equals(memberAttribute.getType()))
+ {
+ throw new NotCompliantMBeanException(exceptionMsg);
+ }
+ if (attribute.isReadable() && memberAttribute.isReadable())
+ {
+ if (attribute.isIs() != memberAttribute.isIs())
+ {
+ throw new NotCompliantMBeanException(exceptionMsg);
+ }
+ }
+
+ return list.indexOf(memberAttribute);
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Retrieves the attribute description from annotation
+ * @param attributeMethod
+ * @return attribute description
+ */
+ private static String getAttributeDescription(Method attributeMethod)
+ {
+ MBeanAttribute anno = attributeMethod.getAnnotation(MBeanAttribute.class);
+ if (anno != null)
+ {
+ return anno.description();
+ }
+ return _defaultAttributeDescription;
+ }
+
+ /**
+ * Introspects the method to retrieve the operation information.
+ * @param operation
+ * @return MBeanOperationInfo
+ */
+ private static MBeanOperationInfo getOperationInfo(Method operation)
+ {
+ MBeanOperationInfo operationInfo = null;
+ Class<?> returnType = operation.getReturnType();
+
+ MBeanParameterInfo[] paramsInfo = getParametersInfo(operation.getParameterAnnotations(),
+ operation.getParameterTypes());
+
+ String operationDesc = _defaultOerationDescription;
+ int impact = MBeanOperationInfo.UNKNOWN;
+
+ if (operation.getAnnotation(MBeanOperation.class) != null)
+ {
+ operationDesc = operation.getAnnotation(MBeanOperation.class).description();
+ impact = operation.getAnnotation(MBeanOperation.class).impact();
+ }
+ operationInfo = new MBeanOperationInfo(operation.getName(),
+ operationDesc,
+ paramsInfo,
+ returnType.getName(),
+ impact);
+
+ return operationInfo;
+ }
+
+ /**
+ * Constructs the parameter info.
+ * @param paramsAnno
+ * @param paramTypes
+ * @return MBeanParameterInfo[]
+ */
+ private static MBeanParameterInfo[] getParametersInfo(Annotation[][] paramsAnno,
+ Class<?>[] paramTypes)
+ {
+ int noOfParams = paramsAnno.length;
+
+ MBeanParameterInfo[] paramsInfo = new MBeanParameterInfo[noOfParams];
+
+ for (int i = 0; i < noOfParams; i++)
+ {
+ MBeanParameterInfo paramInfo = null;
+ String type = paramTypes[i].getName();
+ for (Annotation anno : paramsAnno[i])
+ {
+ String name,desc;
+ if (MBeanOperationParameter.class.isInstance(anno))
+ {
+ name = MBeanOperationParameter.class.cast(anno).name();
+ desc = MBeanOperationParameter.class.cast(anno).description();
+ paramInfo = new MBeanParameterInfo(name, type, desc);
+ }
+ }
+
+
+ if (paramInfo == null)
+ {
+ paramInfo = new MBeanParameterInfo("p " + (i + 1), type, "parameter " + (i + 1));
+ }
+ if (paramInfo != null)
+ paramsInfo[i] = paramInfo;
+ }
+
+ return paramsInfo;
+ }
+
+ /**
+ * Introspects the MBean class for constructors
+ * @param implClass
+ * @return MBeanConstructorInfo[]
+ */
+ static MBeanConstructorInfo[] getMBeanConstructorsInfo(Class implClass)
+ {
+ List<MBeanConstructorInfo> constructors = new ArrayList<MBeanConstructorInfo>();
+
+ for (Constructor cons : implClass.getConstructors())
+ {
+ MBeanConstructorInfo constructorInfo = getMBeanConstructorInfo(cons);
+ //MBeanConstructorInfo constructorInfo = new MBeanConstructorInfo("desc", cons);
+ if (constructorInfo != null)
+ constructors.add(constructorInfo);
+ }
+
+ return constructors.toArray(new MBeanConstructorInfo[0]);
+ }
+
+ /**
+ * Retrieves the constructor info from given constructor.
+ * @param cons
+ * @return MBeanConstructorInfo
+ */
+ private static MBeanConstructorInfo getMBeanConstructorInfo(Constructor cons)
+ {
+ String desc = null;
+ Annotation anno = cons.getAnnotation(MBeanConstructor.class);
+ if (anno != null && MBeanConstructor.class.isInstance(anno))
+ {
+ desc = MBeanConstructor.class.cast(anno).value();
+ }
+
+ //MBeanParameterInfo[] paramsInfo = getParametersInfo(cons.getParameterAnnotations(),
+ // cons.getParameterTypes());
+
+ return new MBeanConstructorInfo(cons.getName(),
+ desc != null ? _defaultConstructorDescription : desc ,
+ null);
+ }
+
+ /**
+ * Retrieves the description from the annotations of given class
+ * @param annotatedClass
+ * @return class description
+ */
+ static String getMBeanDescription(Class annotatedClass)
+ {
+ Annotation anno = annotatedClass.getAnnotation(MBeanDescription.class);
+ if (anno != null && MBeanDescription.class.isInstance(anno))
+ {
+ return MBeanDescription.class.cast(anno).value();
+ }
+ return _defaultMbeanDescription;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
new file mode 100644
index 0000000000..4fb260472d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.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.server.management;
+
+import org.apache.qpid.server.security.access.UserManagement;
+import org.apache.log4j.Logger;
+
+import javax.management.remote.MBeanServerForwarder;
+import javax.management.remote.JMXPrincipal;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.JMException;
+import javax.security.auth.Subject;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.AccessControlContext;
+import java.util.Set;
+import java.util.Properties;
+
+/**
+ * This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. This implements
+ * the logic for allowing the users to invoke MBean operations and implements the restrictions for readOnly, readWrite
+ * and admin users.
+ */
+public class MBeanInvocationHandlerImpl implements InvocationHandler
+{
+ private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class);
+
+ public final static String ADMIN = "admin";
+ public final static String READWRITE = "readwrite";
+ public final static String READONLY = "readonly";
+ private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate";
+ private MBeanServer mbs;
+ private static Properties _userRoles = new Properties();
+
+ public static MBeanServerForwarder newProxyInstance()
+ {
+ final InvocationHandler handler = new MBeanInvocationHandlerImpl();
+ final Class[] interfaces = new Class[]{MBeanServerForwarder.class};
+
+ Object proxy = Proxy.newProxyInstance(MBeanServerForwarder.class.getClassLoader(), interfaces, handler);
+ return MBeanServerForwarder.class.cast(proxy);
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ final String methodName = method.getName();
+
+ if (methodName.equals("getMBeanServer"))
+ {
+ return mbs;
+ }
+
+ if (methodName.equals("setMBeanServer"))
+ {
+ if (args[0] == null)
+ {
+ throw new IllegalArgumentException("Null MBeanServer");
+ }
+ if (mbs != null)
+ {
+ throw new IllegalArgumentException("MBeanServer object already initialized");
+ }
+ mbs = (MBeanServer) args[0];
+ return null;
+ }
+
+ // Retrieve Subject from current AccessControlContext
+ AccessControlContext acc = AccessController.getContext();
+ Subject subject = Subject.getSubject(acc);
+
+ // Allow operations performed locally on behalf of the connector server itself
+ if (subject == null)
+ {
+ return method.invoke(mbs, args);
+ }
+
+ if (args == null || DELEGATE.equals(args[0]))
+ {
+ return method.invoke(mbs, args);
+ }
+
+ // Restrict access to "createMBean" and "unregisterMBean" to any user
+ if (methodName.equals("createMBean") || methodName.equals("unregisterMBean"))
+ {
+ _logger.debug("User trying to create or unregister an MBean");
+ throw new SecurityException("Access denied");
+ }
+
+ // Retrieve JMXPrincipal from Subject
+ Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class);
+ if (principals == null || principals.isEmpty())
+ {
+ throw new SecurityException("Access denied");
+ }
+
+ Principal principal = principals.iterator().next();
+ String identity = principal.getName();
+
+ if (isAdminMethod(args))
+ {
+ if (isAdmin(identity))
+ {
+ return method.invoke(mbs, args);
+ }
+ else
+ {
+ throw new SecurityException("Access denied");
+ }
+ }
+
+ // Following users can perform any operation other than "createMBean" and "unregisterMBean"
+ if (isAllowedToModify(identity))
+ {
+ return method.invoke(mbs, args);
+ }
+
+ // These users can only call "getAttribute" on the MBeanServerDelegate MBean
+ // Here we can add other fine grained permissions like specific method for a particular mbean
+ if (isReadOnlyUser(identity) && isReadOnlyMethod(method, args))
+ {
+ return method.invoke(mbs, args);
+ }
+
+ throw new SecurityException("Access denied");
+ }
+
+ private boolean isAdminMethod(Object[] args)
+ {
+ if (args[0] instanceof ObjectName)
+ {
+ ObjectName object = (ObjectName) args[0];
+ return UserManagement.TYPE.equals(object.getKeyProperty("type"));
+ }
+
+ return false;
+ }
+
+ // Initialises the user roles
+ public static void setAccessRights(Properties accessRights)
+ {
+ _userRoles = accessRights;
+ }
+
+ private boolean isAdmin(String userName)
+ {
+ if (ADMIN.equals(_userRoles.getProperty(userName)))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isAllowedToModify(String userName)
+ {
+ if (ADMIN.equals(_userRoles.getProperty(userName))
+ || READWRITE.equals(_userRoles.getProperty(userName)))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isReadOnlyUser(String userName)
+ {
+ if (READONLY.equals(_userRoles.getProperty(userName)))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isReadOnlyMethod(Method method, Object[] args)
+ {
+ String methodName = method.getName();
+ if (methodName.startsWith("query") || methodName.startsWith("get"))
+ {
+ return true;
+ }
+ else if (methodName.startsWith("set"))
+ {
+ return false;
+ }
+
+ if ((args[0] instanceof ObjectName) && (methodName.equals("invoke")))
+ {
+ String mbeanMethod = (args.length > 1) ? (String) args[1] : null;
+ if (mbeanMethod == null)
+ {
+ return false;
+ }
+
+ try
+ {
+ MBeanInfo mbeanInfo = mbs.getMBeanInfo((ObjectName) args[0]);
+ if (mbeanInfo != null)
+ {
+ MBeanOperationInfo[] opInfos = mbeanInfo.getOperations();
+ for (MBeanOperationInfo opInfo : opInfos)
+ {
+ if (opInfo.getName().equals(mbeanMethod) && (opInfo.getImpact() == MBeanOperationInfo.INFO))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ catch (JMException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java
new file mode 100644
index 0000000000..a2dca3e51d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.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.server.management;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.management.MBeanOperationInfo;
+
+/**
+ * Annotation for MBean operations.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Inherited
+public @interface MBeanOperation
+{
+ String name();
+ String description();
+ int impact() default MBeanOperationInfo.INFO;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java
new file mode 100644
index 0000000000..aba5ec70d8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java
@@ -0,0 +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.management;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for MBean operation parameters.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface MBeanOperationParameter {
+ String name();
+ String description();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java
new file mode 100644
index 0000000000..166a2a376d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+/**
+ * Any object that can return a related MBean should implement this interface.
+ *
+ * This enables other classes to get the managed object, which in turn is useful when
+ * constructing relationships between managed objects without having to maintain
+ * separate data structures containing MBeans.
+ *
+ */
+public interface Managable
+{
+ ManagedObject getManagedObject();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java
new file mode 100644
index 0000000000..45e2e91ed7
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.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.server.management;
+
+import java.io.IOException;
+
+import javax.management.JMException;
+import javax.management.MBeanOperationInfo;
+
+import org.apache.qpid.server.exchange.ManagedExchange;
+import org.apache.qpid.server.queue.ManagedQueue;
+
+/**
+ * The ManagedBroker is the management interface to expose management
+ * features of the Broker.
+ *
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedBroker
+{
+ static final String TYPE = "VirtualHostManager";
+
+ /**
+ * Creates a new Exchange.
+ * @param name
+ * @param type
+ * @param durable
+ * @param passive
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="createNewExchange", description="Creates a new Exchange", impact= MBeanOperationInfo.ACTION)
+ void createNewExchange(@MBeanOperationParameter(name="name", description="Name of the new exchange")String name,
+ @MBeanOperationParameter(name="ExchangeType", description="Type of the exchange")String type,
+ @MBeanOperationParameter(name="durable", description="true if the Exchang should be durable")boolean durable)
+ throws IOException, JMException;
+
+ /**
+ * unregisters all the channels, queuebindings etc and unregisters
+ * this exchange from managed objects.
+ * @param exchange
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="unregisterExchange",
+ description="Unregisters all the related channels and queuebindings of this exchange",
+ impact= MBeanOperationInfo.ACTION)
+ void unregisterExchange(@MBeanOperationParameter(name= ManagedExchange.TYPE, description="Exchange Name")String exchange)
+ throws IOException, JMException;
+
+ /**
+ * Create a new Queue on the Broker server
+ * @param queueName
+ * @param durable
+ * @param owner
+ * @param autoDelete
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="createNewQueue", description="Create a new Queue on the Broker server", impact= MBeanOperationInfo.ACTION)
+ void createNewQueue(@MBeanOperationParameter(name="queue name", description="Name of the new queue")String queueName,
+ @MBeanOperationParameter(name="owner", description="Owner name")String owner,
+ @MBeanOperationParameter(name="durable", description="true if the queue should be durable")boolean durable)
+ throws IOException, JMException;
+
+ /**
+ * Unregisters the Queue bindings, removes the subscriptions and unregisters
+ * from the managed objects.
+ * @param queueName
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="deleteQueue",
+ description="Unregisters the Queue bindings, removes the subscriptions and deletes the queue",
+ impact= MBeanOperationInfo.ACTION)
+ void deleteQueue(@MBeanOperationParameter(name= ManagedQueue.TYPE, description="Queue Name")String queueName)
+ throws IOException, JMException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java
new file mode 100644
index 0000000000..42ea8921a4
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.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.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.qpid.AMQException;
+
+/**
+ * This should be implemented by all Managable objects.
+ */
+public interface ManagedObject
+{
+ static final String DOMAIN = "org.apache.qpid";
+
+ /**
+ * @return the name that uniquely identifies this object instance. It must be
+ * unique only among objects of this type at this level in the hierarchy so
+ * the uniqueness should not be too difficult to ensure.
+ */
+ String getObjectInstanceName();
+
+ String getType();
+
+ Class<?> getManagementInterface();
+
+ ManagedObject getParentObject();
+
+ void register() throws AMQException;
+
+ void unregister() throws AMQException;
+
+ /**
+ * Returns the ObjectName required for the mbeanserver registration.
+ * @return ObjectName
+ * @throws MalformedObjectNameException
+ */
+ ObjectName getObjectName() throws MalformedObjectNameException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java
new file mode 100644
index 0000000000..d8d87ef881
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import javax.management.JMException;
+import java.rmi.RemoteException;
+import java.io.IOException;
+
+/**
+ * Handles the registration (and unregistration and so on) of managed objects.
+ *
+ * Managed objects are responsible for exposting attributes, operations and notifications. They will expose
+ * these outside the JVM therefore it is important not to use implementation objects directly as managed objects.
+ * Instead, creating inner classes and exposing those is an effective way of exposing internal state in a
+ * controlled way.
+ *
+ * Although we do not explictly use them while targetting Java 5, the enhanced MXBean approach in Java 6 will
+ * be the obvious choice for managed objects.
+ *
+ */
+public interface ManagedObjectRegistry
+{
+ void start() throws IOException;
+
+ void registerObject(ManagedObject managedObject) throws JMException;
+
+ void unregisterObject(ManagedObject managedObject) throws JMException;
+
+ void close() throws RemoteException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java
new file mode 100644
index 0000000000..042f626e8b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import org.apache.qpid.configuration.Configured;
+
+public class ManagementConfiguration
+{
+ @Configured(path = "management.enabled",
+ defaultValue = "true")
+ public boolean enabled;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java
new file mode 100644
index 0000000000..b4fbed6948
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management;
+
+import javax.management.JMException;
+
+import org.apache.log4j.Logger;
+
+import java.rmi.RemoteException;
+
+/**
+ * This managed object registry does not actually register MBeans. This can be used in tests when management is
+ * not required or when management has been disabled.
+ *
+ */
+public class NoopManagedObjectRegistry implements ManagedObjectRegistry
+{
+ private static final Logger _log = Logger.getLogger(NoopManagedObjectRegistry.class);
+
+ public NoopManagedObjectRegistry()
+ {
+ _log.info("Management is disabled");
+ }
+
+ public void start()
+ {
+ //no-op
+ }
+
+ public void registerObject(ManagedObject managedObject) throws JMException
+ {
+ }
+
+ public void unregisterObject(ManagedObject managedObject) throws JMException
+ {
+ }
+
+ public void close() throws RemoteException
+ {
+
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java b/Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java
new file mode 100644
index 0000000000..e01c5aabbf
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.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.
+ *
+ */
+
+/*
+ * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
+ * Supported AMQP versions:
+ * 8-0
+ */
+package org.apache.qpid.server.output;
+
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.AMQException;
+
+public interface ProtocolOutputConverter
+{
+ void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag);
+
+ interface Factory
+ {
+ ProtocolOutputConverter newInstance(AMQProtocolSession session);
+ }
+
+ void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException;
+
+ void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException;
+
+ byte getProtocolMinorVersion();
+
+ byte getProtocolMajorVersion();
+
+ void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
+ throws AMQException;
+
+ void writeFrame(AMQDataBlock block);
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java
new file mode 100644
index 0000000000..8366c426dd
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.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.
+ *
+ */
+
+/*
+ * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
+ * Supported AMQP versions:
+ * 8-0
+ */
+package org.apache.qpid.server.output;
+
+import org.apache.qpid.server.output.ProtocolOutputConverter.Factory;
+import org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class ProtocolOutputConverterRegistry
+{
+
+ private static final Map<Byte, Map<Byte, Factory>> _registry =
+ new HashMap<Byte, Map<Byte, Factory>>();
+
+
+ static
+ {
+ register((byte) 8, (byte) 0, ProtocolOutputConverterImpl.getInstanceFactory());
+ }
+
+ private static void register(byte major, byte minor, Factory converter)
+ {
+ if(!_registry.containsKey(major))
+ {
+ _registry.put(major, new HashMap<Byte, Factory>());
+ }
+ _registry.get(major).put(minor, converter);
+ }
+
+
+ public static ProtocolOutputConverter getConverter(AMQProtocolSession session)
+ {
+ return _registry.get(session.getProtocolMajorVersion()).get(session.getProtocolMinorVersion()).newInstance(session);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/Final/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
new file mode 100644
index 0000000000..8462ed9557
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
@@ -0,0 +1,282 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
+ * Supported AMQP versions:
+ * 8-0
+ */
+package org.apache.qpid.server.output.amqp0_8;
+
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQMessageHandle;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.AMQException;
+
+import org.apache.mina.common.ByteBuffer;
+
+import java.util.Iterator;
+
+public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
+{
+
+ public static Factory getInstanceFactory()
+ {
+ return new Factory()
+ {
+
+ public ProtocolOutputConverter newInstance(AMQProtocolSession session)
+ {
+ return new ProtocolOutputConverterImpl(session);
+ }
+ };
+ }
+
+ private final AMQProtocolSession _protocolSession;
+
+ private ProtocolOutputConverterImpl(AMQProtocolSession session)
+ {
+ _protocolSession = session;
+ }
+
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+
+ public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException
+ {
+ ByteBuffer deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ message.getContentHeaderBody());
+
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+ final StoreContext storeContext = message.getStoreContext();
+ final Long messageId = message.getMessageId();
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
+
+ if(bodyCount == 0)
+ {
+ SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
+ contentHeader);
+
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
+
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
+ AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for(int i = 1; i < bodyCount; i++)
+ {
+ cb = messageHandle.getContentChunk(storeContext,messageId, i);
+ writeFrame(new AMQFrame(channelId, getProtocolSession().getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ }
+
+
+ }
+
+
+ }
+
+
+ public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException
+ {
+
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+ final StoreContext storeContext = message.getStoreContext();
+ final long messageId = message.getMessageId();
+
+ ByteBuffer deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
+
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ message.getContentHeaderBody());
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
+ if(bodyCount == 0)
+ {
+ SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
+ contentHeader);
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
+
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
+ AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for(int i = 1; i < bodyCount; i++)
+ {
+ cb = messageHandle.getContentChunk(storeContext, messageId, i);
+ writeFrame(new AMQFrame(channelId, getProtocolSession().getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ }
+
+
+ }
+
+
+ }
+
+
+ private ByteBuffer createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException
+ {
+ final MessagePublishInfo pb = message.getMessagePublishInfo();
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+
+ AMQFrame deliverFrame = BasicDeliverBody.createAMQFrame(channelId, getProtocolMajorVersion(),
+ getProtocolMinorVersion(),
+ consumerTag,
+ deliveryTag, pb.getExchange(), messageHandle.isRedelivered(),
+ pb.getRoutingKey());
+
+
+ return deliverFrame.toByteBuffer();
+ }
+
+ private ByteBuffer createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
+ throws AMQException
+ {
+ final MessagePublishInfo pb = message.getMessagePublishInfo();
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+
+ AMQFrame getOkFrame = BasicGetOkBody.createAMQFrame(channelId,
+ getProtocolMajorVersion(),
+ getProtocolMinorVersion(),
+ deliveryTag, pb.getExchange(),
+ queueSize,
+ messageHandle.isRedelivered(),
+ pb.getRoutingKey());
+
+ return getOkFrame.toByteBuffer();
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return getProtocolSession().getProtocolMinorVersion();
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return getProtocolSession().getProtocolMajorVersion();
+ }
+
+ private ByteBuffer createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ {
+ AMQFrame returnFrame = BasicReturnBody.createAMQFrame(channelId,
+ getProtocolMajorVersion(),
+ getProtocolMinorVersion(),
+ message.getMessagePublishInfo().getExchange(),
+ replyCode, replyText,
+ message.getMessagePublishInfo().getRoutingKey());
+
+ return returnFrame.toByteBuffer();
+ }
+
+ public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
+ throws AMQException
+ {
+ ByteBuffer returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ message.getContentHeaderBody());
+
+ Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId);
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ if (bodyFrameIterator.hasNext())
+ {
+ AMQDataBlock firstContentBody = bodyFrameIterator.next();
+ AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, headerAndFirstContent);
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame,
+ new AMQDataBlock[]{contentHeader});
+
+ writeFrame(compositeBlock);
+ }
+
+ //
+ // Now start writing out the other content bodies
+ // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded
+ //
+ while (bodyFrameIterator.hasNext())
+ {
+ writeFrame(bodyFrameIterator.next());
+ }
+ }
+
+
+ public void writeFrame(AMQDataBlock block)
+ {
+ getProtocolSession().writeFrame(block);
+ }
+
+
+ public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
+ {
+
+ writeFrame(BasicCancelOkBody.createAMQFrame(channelId,
+ getProtocolMajorVersion(),
+ getProtocolMinorVersion(),
+ consumerTag // consumerTag
+ ));
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
new file mode 100644
index 0000000000..5bfd47b469
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
@@ -0,0 +1,773 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.log4j.Logger;
+
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.transport.vmpipe.VmPipeAddress;
+
+import org.apache.qpid.AMQChannelException;
+import org.apache.qpid.AMQConnectionException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.codec.AMQDecoder;
+import org.apache.qpid.common.ClientProperties;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQMethodListener;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.management.Managable;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.server.output.ProtocolOutputConverterRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+
+import javax.management.JMException;
+import javax.security.sasl.SaslServer;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
+{
+ private static final Logger _logger = Logger.getLogger(AMQProtocolSession.class);
+
+ private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString();
+
+ // to save boxing the channelId and looking up in a map... cache in an array the low numbered
+ // channels. This value must be of the form 2^x - 1.
+ private static final int CHANNEL_CACHE_SIZE = 0xff;
+
+ private final IoSession _minaProtocolSession;
+
+ private AMQShortString _contextKey;
+
+ private AMQShortString _clientVersion = null;
+
+ private VirtualHost _virtualHost;
+
+ private final Map<Integer, AMQChannel> _channelMap = new HashMap<Integer, AMQChannel>();
+
+ private final AMQChannel[] _cachedChannels = new AMQChannel[CHANNEL_CACHE_SIZE + 1];
+
+ private final CopyOnWriteArraySet<AMQMethodListener> _frameListeners = new CopyOnWriteArraySet<AMQMethodListener>();
+
+ private final AMQStateManager _stateManager;
+
+ private AMQCodecFactory _codecFactory;
+
+ private AMQProtocolSessionMBean _managedObject;
+
+ private SaslServer _saslServer;
+
+ private Object _lastReceived;
+
+ private Object _lastSent;
+
+ private boolean _closed;
+ // maximum number of channels this session should have
+ private long _maxNoOfChannels = 1000;
+
+ /* AMQP Version for this session */
+ private ProtocolVersion _protocolVersion = ProtocolVersion.getLatestSupportedVersion();
+
+ private FieldTable _clientProperties;
+ private final List<Task> _taskList = new CopyOnWriteArrayList<Task>();
+ private VersionSpecificRegistry _registry = MainRegistry.getVersionSpecificRegistry(_protocolVersion);
+ private List<Integer> _closingChannelsList = new ArrayList<Integer>();
+ private ProtocolOutputConverter _protocolOutputConverter;
+ private Principal _authorizedID;
+
+ public ManagedObject getManagedObject()
+ {
+ return _managedObject;
+ }
+
+ public AMQMinaProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory)
+ throws AMQException
+ {
+ _stateManager = new AMQStateManager(virtualHostRegistry, this);
+ _minaProtocolSession = session;
+ session.setAttachment(this);
+
+ _codecFactory = codecFactory;
+
+ try
+ {
+ IoServiceConfig config = session.getServiceConfig();
+ ReadWriteThreadModel threadModel = (ReadWriteThreadModel) config.getThreadModel();
+ threadModel.getAsynchronousReadFilter().createNewJobForSession(session);
+ threadModel.getAsynchronousWriteFilter().createNewJobForSession(session);
+ }
+ catch (RuntimeException e)
+ {
+ e.printStackTrace();
+ // throw e;
+
+ }
+
+ // this(session, queueRegistry, exchangeRegistry, codecFactory, new AMQStateManager());
+ }
+
+ public AMQMinaProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory,
+ AMQStateManager stateManager) throws AMQException
+ {
+ _stateManager = stateManager;
+ _minaProtocolSession = session;
+ session.setAttachment(this);
+
+ _codecFactory = codecFactory;
+
+ }
+
+ private AMQProtocolSessionMBean createMBean() throws AMQException
+ {
+ try
+ {
+ return new AMQProtocolSessionMBean(this);
+ }
+ catch (JMException ex)
+ {
+ _logger.error("AMQProtocolSession MBean creation has failed ", ex);
+ throw new AMQException("AMQProtocolSession MBean creation has failed ", ex);
+ }
+ }
+
+ public IoSession getIOSession()
+ {
+ return _minaProtocolSession;
+ }
+
+ public static AMQProtocolSession getAMQProtocolSession(IoSession minaProtocolSession)
+ {
+ return (AMQProtocolSession) minaProtocolSession.getAttachment();
+ }
+
+ public void dataBlockReceived(AMQDataBlock message) throws Exception
+ {
+ _lastReceived = message;
+ if (message instanceof ProtocolInitiation)
+ {
+ protocolInitiationReceived((ProtocolInitiation) message);
+
+ }
+ else if (message instanceof AMQFrame)
+ {
+ AMQFrame frame = (AMQFrame) message;
+ frameReceived(frame);
+
+ }
+ else
+ {
+ throw new UnknnownMessageTypeException(message);
+ }
+ }
+
+ private void frameReceived(AMQFrame frame) throws AMQException
+ {
+ int channelId = frame.getChannel();
+ AMQBody body = frame.getBodyFrame();
+
+ if (_logger.isDebugEnabled())
+ {
+ _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)
+ {
+ contentBodyReceived(channelId, (ContentBody) body);
+ }
+ else if (body instanceof HeartbeatBody)
+ {
+ // NO OP
+ }
+ else
+ {
+ _logger.warn("Unrecognised frame " + frame.getClass().getName());
+ }
+ }
+
+ private void protocolInitiationReceived(ProtocolInitiation pi)
+ {
+ // this ensures the codec never checks for a PI message again
+ ((AMQDecoder) _codecFactory.getDecoder()).setExpectProtocolInitiation(false);
+ try
+ {
+ pi.checkVersion(); // Fails if not correct
+
+ // This sets the protocol version (and hence framing classes) for this session.
+ setProtocolVersion(pi._protocolMajor, pi._protocolMinor);
+
+ String mechanisms = ApplicationRegistry.getInstance().getAuthenticationManager().getMechanisms();
+
+ String locales = "en_US";
+
+ // Interfacing with generated code - be aware of possible changes to parameter order as versions change.
+ AMQFrame response =
+ ConnectionStartBody.createAMQFrame((short) 0, getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor)
+ locales.getBytes(), // locales
+ mechanisms.getBytes(), // mechanisms
+ null, // serverProperties
+ (short) getProtocolMajorVersion(), // versionMajor
+ (short) getProtocolMinorVersion()); // versionMinor
+ _minaProtocolSession.write(response);
+ }
+ catch (AMQException e)
+ {
+ _logger.error("Received incorrect protocol initiation", e);
+
+ _minaProtocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()));
+
+ // TODO: Close connection (but how to wait until message is sent?)
+ // ritchiem 2006-12-04 will this not do?
+ // WriteFuture future = _minaProtocolSession.write(new ProtocolInitiation(pv[i][PROTOCOLgetProtocolMajorVersion()], pv[i][PROTOCOLgetProtocolMinorVersion()]));
+ // future.join();
+ // close connection
+
+ }
+ }
+
+ private 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
+ {
+
+ boolean wasAnyoneInterested = _stateManager.methodReceived(evt);
+
+ if (!_frameListeners.isEmpty())
+ {
+ for (AMQMethodListener listener : _frameListeners)
+ {
+ wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested;
+ }
+ }
+
+ if (!wasAnyoneInterested)
+ {
+ throw new AMQNoMethodHandlerException(evt);
+ }
+ }
+ catch (AMQChannelException e)
+ {
+ if (getChannel(channelId) != null)
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Closing channel due to: " + e.getMessage());
+ }
+
+ writeFrame(e.getCloseFrame(channelId));
+ closeChannel(channelId);
+ }
+ else
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("ChannelException occured on non-existent channel:" + e.getMessage());
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Closing connection due to: " + e.getMessage());
+ }
+
+ closeSession();
+
+ AMQConnectionException ce =
+ evt.getMethod().getConnectionException(AMQConstant.CHANNEL_ERROR,
+ AMQConstant.CHANNEL_ERROR.getName().toString());
+
+ _stateManager.changeState(AMQState.CONNECTION_CLOSING);
+ writeFrame(ce.getCloseFrame(channelId));
+ }
+ }
+ catch (AMQConnectionException e)
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Closing connection due to: " + e.getMessage());
+ }
+
+ closeSession();
+ _stateManager.changeState(AMQState.CONNECTION_CLOSING);
+ writeFrame(e.getCloseFrame(channelId));
+ }
+ }
+ catch (Exception e)
+ {
+ _stateManager.error(e);
+ for (AMQMethodListener listener : _frameListeners)
+ {
+ listener.error(e);
+ }
+
+ _minaProtocolSession.close();
+ }
+ }
+
+ private void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException
+ {
+
+ AMQChannel channel = getAndAssertChannel(channelId);
+
+ channel.publishContentHeader(body, this);
+
+ }
+
+ private void contentBodyReceived(int channelId, ContentBody body) throws AMQException
+ {
+ AMQChannel channel = getAndAssertChannel(channelId);
+
+ channel.publishContentBody(body, this);
+ }
+
+ /**
+ * Convenience method that writes a frame to the protocol session. Equivalent to calling
+ * getProtocolSession().write().
+ *
+ * @param frame the frame to write
+ */
+ public void writeFrame(AMQDataBlock frame)
+ {
+ _lastSent = frame;
+ _minaProtocolSession.write(frame);
+ }
+
+ public AMQShortString getContextKey()
+ {
+ return _contextKey;
+ }
+
+ public void setContextKey(AMQShortString contextKey)
+ {
+ _contextKey = contextKey;
+ }
+
+ public List<AMQChannel> getChannels()
+ {
+ return new ArrayList<AMQChannel>(_channelMap.values());
+ }
+
+ public AMQChannel getAndAssertChannel(int channelId) throws AMQException
+ {
+ AMQChannel channel = getChannel(channelId);
+ if (channel == null)
+ {
+ throw new AMQException(AMQConstant.NOT_FOUND, "Channel not found with id:" + channelId);
+ }
+
+ return channel;
+ }
+
+ public AMQChannel getChannel(int channelId) throws AMQException
+ {
+ final AMQChannel channel =
+ ((channelId & CHANNEL_CACHE_SIZE) == channelId) ? _cachedChannels[channelId] : _channelMap.get(channelId);
+ if ((channel == null) || channel.isClosing())
+ {
+ return null;
+ }
+ else
+ {
+ return channel;
+ }
+ }
+
+ public boolean channelAwaitingClosure(int channelId)
+ {
+ return _closingChannelsList.contains(channelId);
+ }
+
+ public void addChannel(AMQChannel channel) throws AMQException
+ {
+ if (_closed)
+ {
+ throw new AMQException("Session is closed");
+ }
+
+ final int channelId = channel.getChannelId();
+
+ if (_closingChannelsList.contains(channelId))
+ {
+ throw new AMQException("Session is marked awaiting channel close");
+ }
+
+ if (_channelMap.size() == _maxNoOfChannels)
+ {
+ String errorMessage =
+ toString() + ": maximum number of channels has been reached (" + _maxNoOfChannels
+ + "); can't create channel";
+ _logger.error(errorMessage);
+ throw new AMQException(AMQConstant.NOT_ALLOWED, errorMessage);
+ }
+ else
+ {
+ _channelMap.put(channel.getChannelId(), channel);
+ }
+
+ if (((channelId & CHANNEL_CACHE_SIZE) == channelId))
+ {
+ _cachedChannels[channelId] = channel;
+ }
+
+ checkForNotification();
+ }
+
+ private void checkForNotification()
+ {
+ int channelsCount = _channelMap.size();
+ if (channelsCount >= _maxNoOfChannels)
+ {
+ _managedObject.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value");
+ }
+ }
+
+ public Long getMaximumNumberOfChannels()
+ {
+ return _maxNoOfChannels;
+ }
+
+ public void setMaximumNumberOfChannels(Long value)
+ {
+ _maxNoOfChannels = value;
+ }
+
+ public void commitTransactions(AMQChannel channel) throws AMQException
+ {
+ if ((channel != null) && channel.isTransactional())
+ {
+ channel.commit();
+ }
+ }
+
+ public void rollbackTransactions(AMQChannel channel) throws AMQException
+ {
+ if ((channel != null) && channel.isTransactional())
+ {
+ channel.rollback();
+ }
+ }
+
+ /**
+ * Close a specific channel. This will remove any resources used by the channel, including: <ul><li>any queue
+ * subscriptions (this may in turn remove queues if they are auto delete</li> </ul>
+ *
+ * @param channelId id of the channel to close
+ *
+ * @throws AMQException if an error occurs closing the channel
+ * @throws IllegalArgumentException if the channel id is not valid
+ */
+ public void closeChannel(int channelId) throws AMQException
+ {
+ final AMQChannel channel = getChannel(channelId);
+ if (channel == null)
+ {
+ throw new IllegalArgumentException("Unknown channel id");
+ }
+ else
+ {
+ try
+ {
+ channel.close(this);
+ markChannelawaitingCloseOk(channelId);
+ }
+ finally
+ {
+ removeChannel(channelId);
+ }
+ }
+ }
+
+ public void closeChannelOk(int channelId)
+ {
+ _closingChannelsList.remove(new Integer(channelId));
+ }
+
+ private void markChannelawaitingCloseOk(int channelId)
+ {
+ _closingChannelsList.add(channelId);
+ }
+
+ /**
+ * In our current implementation this is used by the clustering code.
+ *
+ * @param channelId The channel to remove
+ */
+ public void removeChannel(int channelId)
+ {
+ _channelMap.remove(channelId);
+ if ((channelId & CHANNEL_CACHE_SIZE) == channelId)
+ {
+ _cachedChannels[channelId] = null;
+ }
+ }
+
+ /**
+ * Initialise heartbeats on the session.
+ *
+ * @param delay delay in seconds (not ms)
+ */
+ public void initHeartbeats(int delay)
+ {
+ if (delay > 0)
+ {
+ _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay);
+ _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, HeartbeatConfig.getInstance().getTimeout(delay));
+ }
+ }
+
+ /**
+ * Closes all channels that were opened by this protocol session. This frees up all resources used by the channel.
+ *
+ * @throws AMQException if an error occurs while closing any channel
+ */
+ private void closeAllChannels() throws AMQException
+ {
+ for (AMQChannel channel : _channelMap.values())
+ {
+ channel.close(this);
+ }
+
+ _channelMap.clear();
+ for (int i = 0; i <= CHANNEL_CACHE_SIZE; i++)
+ {
+ _cachedChannels[i] = null;
+ }
+ }
+
+ /** This must be called when the session is _closed in order to free up any resources managed by the session. */
+ public void closeSession() throws AMQException
+ {
+ if (!_closed)
+ {
+ _closed = true;
+ closeAllChannels();
+ if (_managedObject != null)
+ {
+ _managedObject.unregister();
+ }
+
+ for (Task task : _taskList)
+ {
+ task.doTask(this);
+ }
+ }
+ }
+
+ public String toString()
+ {
+ return "AMQProtocolSession(" + _minaProtocolSession.getRemoteAddress() + ")";
+ }
+
+ public String dump()
+ {
+ return this + " last_sent=" + _lastSent + " last_received=" + _lastReceived;
+ }
+
+ /** @return an object that can be used to identity */
+ public Object getKey()
+ {
+ return _minaProtocolSession.getRemoteAddress();
+ }
+
+ /**
+ * Get the fully qualified domain name of the local address to which this session is bound. Since some servers may
+ * be bound to multiple addresses this could vary depending on the acceptor this session was created from.
+ *
+ * @return a String FQDN
+ */
+ public String getLocalFQDN()
+ {
+ SocketAddress address = _minaProtocolSession.getLocalAddress();
+ // we use the vmpipe address in some tests hence the need for this rather ugly test. The host
+ // information is used by SASL primary.
+ if (address instanceof InetSocketAddress)
+ {
+ return ((InetSocketAddress) address).getHostName();
+ }
+ else if (address instanceof VmPipeAddress)
+ {
+ return "vmpipe:" + ((VmPipeAddress) address).getPort();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported socket address class: " + address);
+ }
+ }
+
+ public SaslServer getSaslServer()
+ {
+ return _saslServer;
+ }
+
+ public void setSaslServer(SaslServer saslServer)
+ {
+ _saslServer = saslServer;
+ }
+
+ public FieldTable getClientProperties()
+ {
+ return _clientProperties;
+ }
+
+ public void setClientProperties(FieldTable clientProperties)
+ {
+ _clientProperties = clientProperties;
+ if (_clientProperties != null)
+ {
+ if (_clientProperties.getString(CLIENT_PROPERTIES_INSTANCE) != null)
+ {
+ setContextKey(new AMQShortString(_clientProperties.getString(CLIENT_PROPERTIES_INSTANCE)));
+ }
+
+ if (_clientProperties.getString(ClientProperties.version.toString()) != null)
+ {
+ _clientVersion = new AMQShortString(_clientProperties.getString(ClientProperties.version.toString()));
+ }
+ }
+ }
+
+ private void setProtocolVersion(byte major, byte minor)
+ {
+ _protocolVersion = new ProtocolVersion(major, minor);
+
+ _registry = MainRegistry.getVersionSpecificRegistry(_protocolVersion);
+
+ _protocolOutputConverter = ProtocolOutputConverterRegistry.getConverter(this);
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return _protocolVersion.getMajorVersion();
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return _protocolVersion.getMinorVersion();
+ }
+
+ public boolean isProtocolVersion(byte major, byte minor)
+ {
+ return (getProtocolMajorVersion() == major) && (getProtocolMinorVersion() == minor);
+ }
+
+ public VersionSpecificRegistry getRegistry()
+ {
+ return _registry;
+ }
+
+ public Object getClientIdentifier()
+ {
+ return _minaProtocolSession.getRemoteAddress();
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public void setVirtualHost(VirtualHost virtualHost) throws AMQException
+ {
+ _virtualHost = virtualHost;
+ _managedObject = createMBean();
+ _managedObject.register();
+ }
+
+ public void addSessionCloseTask(Task task)
+ {
+ _taskList.add(task);
+ }
+
+ public void removeSessionCloseTask(Task task)
+ {
+ _taskList.remove(task);
+ }
+
+ public ProtocolOutputConverter getProtocolOutputConverter()
+ {
+ return _protocolOutputConverter;
+ }
+
+ public void setAuthorizedID(Principal authorizedID)
+ {
+ _authorizedID = authorizedID;
+ }
+
+ public Principal getAuthorizedID()
+ {
+ return _authorizedID;
+ }
+
+ public String getClientVersion()
+ {
+ return (_clientVersion == null) ? null : _clientVersion.toString();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java
new file mode 100644
index 0000000000..a7599a3e0d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+/**
+ * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to handle an AMQP method.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a
+ * Runtime.
+ */
+public class AMQNoMethodHandlerException extends AMQException
+{
+ public AMQNoMethodHandlerException(AMQMethodEvent<AMQMethodBody> evt)
+ {
+ super("AMQMethodEvent " + evt + " was not processed by any listener on Broker.");
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
new file mode 100644
index 0000000000..476e608b01
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
@@ -0,0 +1,240 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.log4j.Logger;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.SSLFilter;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.util.SessionUtil;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQProtocolHeaderException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.HeartbeatBody;
+import org.apache.qpid.framing.ProtocolInitiation;
+import org.apache.qpid.framing.ProtocolVersion;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.transport.ConnectorConfiguration;
+import org.apache.qpid.ssl.SSLContextFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+/**
+ * The protocol handler handles "protocol events" for all connections. The state
+ * associated with an individual connection is accessed through the protocol session.
+ *
+ * We delegate all frame (message) processing to the AMQProtocolSession which wraps
+ * the state for the connection.
+ *
+ */
+public class AMQPFastProtocolHandler extends IoHandlerAdapter
+{
+ private static final Logger _logger = Logger.getLogger(AMQPFastProtocolHandler.class);
+
+ private final IApplicationRegistry _applicationRegistry;
+
+
+ public AMQPFastProtocolHandler(Integer applicationRegistryInstance)
+ {
+ this(ApplicationRegistry.getInstance(applicationRegistryInstance));
+ }
+
+ public AMQPFastProtocolHandler(IApplicationRegistry applicationRegistry)
+ {
+ _applicationRegistry = applicationRegistry;
+ _logger.debug("AMQPFastProtocolHandler created");
+ }
+
+ protected AMQPFastProtocolHandler(AMQPFastProtocolHandler handler)
+ {
+ this(handler._applicationRegistry);
+ }
+
+ public void sessionCreated(IoSession protocolSession) throws Exception
+ {
+ SessionUtil.initialize(protocolSession);
+ final AMQCodecFactory codecFactory = new AMQCodecFactory(true);
+
+ createSession(protocolSession, _applicationRegistry, codecFactory);
+ _logger.info("Protocol session created for:" + protocolSession.getRemoteAddress());
+
+ final ProtocolCodecFilter pcf = new ProtocolCodecFilter(codecFactory);
+
+ ConnectorConfiguration connectorConfig = ApplicationRegistry.getInstance().
+ getConfiguredObject(ConnectorConfiguration.class);
+ if (connectorConfig.enableExecutorPool)
+ {
+ if (connectorConfig.enableSSL && isSSLClient(connectorConfig, protocolSession))
+ {
+ String keystorePath = connectorConfig.keystorePath;
+ String keystorePassword = connectorConfig.keystorePassword;
+ String certType = connectorConfig.certType;
+ SSLContextFactory sslContextFactory = new SSLContextFactory(keystorePath, keystorePassword, certType);
+ protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter",
+ new SSLFilter(sslContextFactory.buildServerContext()));
+ }
+ protocolSession.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf);
+ }
+ else
+ {
+ protocolSession.getFilterChain().addLast("protocolFilter", pcf);
+ if (connectorConfig.enableSSL && isSSLClient(connectorConfig, protocolSession))
+ {
+ String keystorePath = connectorConfig.keystorePath;
+ String keystorePassword = connectorConfig.keystorePassword;
+ String certType = connectorConfig.certType;
+ SSLContextFactory sslContextFactory = new SSLContextFactory(keystorePath, keystorePassword, certType);
+ protocolSession.getFilterChain().addBefore("protocolFilter", "sslFilter",
+ new SSLFilter(sslContextFactory.buildServerContext()));
+ }
+
+ }
+ }
+
+ /**
+ * Separated into its own, protected, method to allow easier reuse
+ */
+ protected void createSession(IoSession session, IApplicationRegistry applicationRegistry, AMQCodecFactory codec) throws AMQException
+ {
+ new AMQMinaProtocolSession(session, applicationRegistry.getVirtualHostRegistry(), codec);
+ }
+
+ public void sessionOpened(IoSession protocolSession) throws Exception
+ {
+ _logger.info("Session opened for:" + protocolSession.getRemoteAddress());
+ }
+
+ public void sessionClosed(IoSession protocolSession) throws Exception
+ {
+ _logger.info("Protocol Session closed for:" + protocolSession.getRemoteAddress());
+ final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession);
+ //fixme -- this can be null
+ if (amqProtocolSession != null)
+ {
+ amqProtocolSession.closeSession();
+ }
+ }
+
+ public void sessionIdle(IoSession session, IdleStatus status) throws Exception
+ {
+ _logger.debug("Protocol Session [" + this + "] idle: " + status + " :for:" + session.getRemoteAddress());
+ if (IdleStatus.WRITER_IDLE.equals(status))
+ {
+ //write heartbeat frame:
+ session.write(HeartbeatBody.FRAME);
+ }
+ else if (IdleStatus.READER_IDLE.equals(status))
+ {
+ //failover:
+ throw new IOException("Timed out while waiting for heartbeat from peer.");
+ }
+
+ }
+
+ public void exceptionCaught(IoSession protocolSession, Throwable throwable) throws Exception
+ {
+ AMQProtocolSession session = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession);
+ if (throwable instanceof AMQProtocolHeaderException)
+ {
+
+ protocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()));
+
+ protocolSession.close();
+
+ _logger.error("Error in protocol initiation " + session + ":" + protocolSession.getRemoteAddress() + " :" + throwable.getMessage(), throwable);
+ }
+ else if (throwable instanceof IOException)
+ {
+ _logger.error("IOException caught in" + session + ", session closed implictly: " + throwable, throwable);
+ }
+ else
+ {
+ _logger.error("Exception caught in" + session + ", closing session explictly: " + throwable, throwable);
+
+ // Be aware of possible changes to parameter order as versions change.
+ protocolSession.write(ConnectionCloseBody.createAMQFrame(0,
+ session.getProtocolMajorVersion(),
+ session.getProtocolMinorVersion(), // AMQP version (major, minor)
+ 0, // classId
+ 0, // methodId
+ 200, // replyCode
+ new AMQShortString(throwable.getMessage()) // replyText
+ ));
+ protocolSession.close();
+ }
+ }
+
+ /**
+ * Invoked when a message is received on a particular protocol session. Note that a
+ * protocol session is directly tied to a particular physical connection.
+ * @param protocolSession the protocol session that received the message
+ * @param message the message itself (i.e. a decoded frame)
+ * @throws Exception if the message cannot be processed
+ */
+ public void messageReceived(IoSession protocolSession, Object message) throws Exception
+ {
+ final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession);
+
+ if (message instanceof AMQDataBlock)
+ {
+ amqProtocolSession.dataBlockReceived((AMQDataBlock) message);
+
+ }
+ else if (message instanceof ByteBuffer)
+ {
+ throw new IllegalStateException("Handed undecoded ByteBuffer buf = " + message);
+ }
+ else
+ {
+ throw new IllegalStateException("Handed unhandled message. message.class = " + message.getClass() + " message = " + message);
+ }
+ }
+
+ /**
+ * Called after a message has been sent out on a particular protocol session
+ * @param protocolSession the protocol session (i.e. connection) on which this
+ * message was sent
+ * @param object the message (frame) that was encoded and sent
+ * @throws Exception if we want to indicate an error
+ */
+ public void messageSent(IoSession protocolSession, Object object) throws Exception
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Message sent: " + object);
+ }
+ }
+
+ protected boolean isSSLClient(ConnectorConfiguration connectionConfig,
+ IoSession protocolSession)
+ {
+ InetSocketAddress addr = (InetSocketAddress) protocolSession.getLocalAddress();
+ return addr.getPort() == connectionConfig.sslPort;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java
new file mode 100644
index 0000000000..07c153bfe8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+
+/**
+ * The protocol provide's role is to encapsulate the initialisation of the protocol handler.
+ *
+ * The protocol handler (see AMQPFastProtocolHandler class) handles protocol events
+ * such as connection closing or a frame being received. It can either do this directly
+ * or pass off to the protocol session in the cases where state information is required to
+ * deal with the event.
+ *
+ */
+public class AMQPProtocolProvider
+{
+ /**
+ * Handler for protocol events
+ */
+ private AMQPFastProtocolHandler _handler;
+
+ public AMQPProtocolProvider()
+ {
+ IApplicationRegistry registry = ApplicationRegistry.getInstance();
+ _handler = new AMQPFastProtocolHandler(registry);
+ }
+
+ public AMQPFastProtocolHandler getHandler()
+ {
+ return _handler;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java
new file mode 100644
index 0000000000..390117acf6
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.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.server.protocol;
+
+import javax.security.sasl.SaslServer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import java.security.Principal;
+
+
+public interface AMQProtocolSession extends AMQVersionAwareProtocolSession
+{
+
+ public static interface Task
+ {
+ public void doTask(AMQProtocolSession session) throws AMQException;
+ }
+
+ /**
+ * Called when a protocol data block is received
+ *
+ * @param message the data block that has been received
+ *
+ * @throws Exception if processing the datablock fails
+ */
+ void dataBlockReceived(AMQDataBlock message) throws Exception;
+
+ /**
+ * Get the context key associated with this session. Context key is described in the AMQ protocol specification (RFC
+ * 6).
+ *
+ * @return the context key
+ */
+ AMQShortString getContextKey();
+
+ /**
+ * Set the context key associated with this session. Context key is described in the AMQ protocol specification (RFC
+ * 6).
+ *
+ * @param contextKey the context key
+ */
+ void setContextKey(AMQShortString contextKey);
+
+ /**
+ * Get the channel for this session associated with the specified id. A channel id is unique per connection (i.e.
+ * per session).
+ *
+ * @param channelId the channel id which must be valid
+ *
+ * @return null if no channel exists, the channel otherwise
+ */
+ AMQChannel getChannel(int channelId) throws AMQException;
+
+ /**
+ * Associate a channel with this session.
+ *
+ * @param channel the channel to associate with this session. It is an error to associate the same channel with more
+ * than one session but this is not validated.
+ */
+ void addChannel(AMQChannel channel) throws AMQException;
+
+ /**
+ * Close a specific channel. This will remove any resources used by the channel, including: <ul><li>any queue
+ * subscriptions (this may in turn remove queues if they are auto delete</li> </ul>
+ *
+ * @param channelId id of the channel to close
+ *
+ * @throws org.apache.qpid.AMQException if an error occurs closing the channel
+ * @throws IllegalArgumentException if the channel id is not valid
+ */
+ void closeChannel(int channelId) throws AMQException;
+
+ /**
+ * Markes the specific channel as closed. This will release the lock for that channel id so a new channel can be
+ * created on that id.
+ *
+ * @param channelId id of the channel to close
+ */
+ void closeChannelOk(int channelId);
+
+ /**
+ * Check to see if this chanel is closing
+ *
+ * @param channelId id to check
+ * @return boolean with state of channel awaiting closure
+ */
+ boolean channelAwaitingClosure(int channelId);
+
+ /**
+ * Remove a channel from the session but do not close it.
+ *
+ * @param channelId
+ */
+ void removeChannel(int channelId);
+
+ /**
+ * Initialise heartbeats on the session.
+ *
+ * @param delay delay in seconds (not ms)
+ */
+ void initHeartbeats(int delay);
+
+ /** This must be called when the session is _closed in order to free up any resources managed by the session. */
+ void closeSession() throws AMQException;
+
+ /** @return a key that uniquely identifies this session */
+ Object getKey();
+
+ /**
+ * Get the fully qualified domain name of the local address to which this session is bound. Since some servers may
+ * be bound to multiple addresses this could vary depending on the acceptor this session was created from.
+ *
+ * @return a String FQDN
+ */
+ String getLocalFQDN();
+
+ /** @return the sasl server that can perform authentication for this session. */
+ SaslServer getSaslServer();
+
+ /**
+ * Set the sasl server that is to perform authentication for this session.
+ *
+ * @param saslServer
+ */
+ void setSaslServer(SaslServer saslServer);
+
+
+ FieldTable getClientProperties();
+
+ void setClientProperties(FieldTable clientProperties);
+
+ Object getClientIdentifier();
+
+ VirtualHost getVirtualHost();
+
+ void setVirtualHost(VirtualHost virtualHost) throws AMQException;
+
+ void addSessionCloseTask(Task task);
+
+ void removeSessionCloseTask(Task task);
+
+ public ProtocolOutputConverter getProtocolOutputConverter();
+
+ void setAuthorizedID(Principal authorizedID);
+
+ /** @return a Principal that was used to authorized this session */
+ Principal getAuthorizedID();
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java
new file mode 100644
index 0000000000..66f928a70e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java
@@ -0,0 +1,305 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import java.security.Principal;
+import java.util.Date;
+import java.util.List;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.NotCompliantMBeanException;
+import javax.management.Notification;
+import javax.management.monitor.MonitorNotification;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.management.AMQManagedObject;
+import org.apache.qpid.server.management.MBeanConstructor;
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.management.ManagedObject;
+
+/**
+ * This MBean class implements the management interface. In order to make more attributes, operations and notifications
+ * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here.
+ */
+@MBeanDescription("Management Bean for an AMQ Broker Connection")
+public class AMQProtocolSessionMBean extends AMQManagedObject implements ManagedConnection
+{
+ private AMQMinaProtocolSession _session = null;
+ private String _name = null;
+
+ // openmbean data types for representing the channel attributes
+ private static final String[] _channelAtttibuteNames =
+ { "Channel Id", "Transactional", "Default Queue", "Unacknowledged Message Count" };
+ private static final String[] _indexNames = { _channelAtttibuteNames[0] };
+ private static final OpenType[] _channelAttributeTypes =
+ { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER };
+ private static CompositeType _channelType = null; // represents the data type for channel data
+ private static TabularType _channelsType = null; // Data type for list of channels type
+ private static final AMQShortString BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION =
+ new AMQShortString("Broker Management Console has closed the connection.");
+
+ @MBeanConstructor("Creates an MBean exposing an AMQ Broker Connection")
+ public AMQProtocolSessionMBean(AMQMinaProtocolSession session) throws NotCompliantMBeanException, OpenDataException
+ {
+ super(ManagedConnection.class, ManagedConnection.TYPE);
+ _session = session;
+ String remote = getRemoteAddress();
+ remote = "anonymous".equals(remote) ? (remote + hashCode()) : remote;
+ _name = jmxEncode(new StringBuffer(remote), 0).toString();
+ init();
+ }
+
+ static
+ {
+ try
+ {
+ init();
+ }
+ catch (JMException ex)
+ {
+ // This is not expected to ever occur.
+ throw new RuntimeException("Got JMException in static initializer.", ex);
+ }
+ }
+
+ /**
+ * initialises the openmbean data types
+ */
+ private static void init() throws OpenDataException
+ {
+ _channelType =
+ new CompositeType("Channel", "Channel Details", _channelAtttibuteNames, _channelAtttibuteNames,
+ _channelAttributeTypes);
+ _channelsType = new TabularType("Channels", "Channels", _channelType, _indexNames);
+ }
+
+ public String getClientId()
+ {
+ return (_session.getContextKey() == null) ? null : _session.getContextKey().toString();
+ }
+
+ public String getAuthorizedId()
+ {
+ return (_session.getAuthorizedID() != null ) ? _session.getAuthorizedID().getName() : null;
+ }
+
+ public String getVersion()
+ {
+ return (_session.getClientVersion() == null) ? null : _session.getClientVersion().toString();
+ }
+
+ public Date getLastIoTime()
+ {
+ return new Date(_session.getIOSession().getLastIoTime());
+ }
+
+ public String getRemoteAddress()
+ {
+ return _session.getIOSession().getRemoteAddress().toString();
+ }
+
+ public ManagedObject getParentObject()
+ {
+ return _session.getVirtualHost().getManagedObject();
+ }
+
+ public Long getWrittenBytes()
+ {
+ return _session.getIOSession().getWrittenBytes();
+ }
+
+ public Long getReadBytes()
+ {
+ return _session.getIOSession().getReadBytes();
+ }
+
+ public Long getMaximumNumberOfChannels()
+ {
+ return _session.getMaximumNumberOfChannels();
+ }
+
+ public void setMaximumNumberOfChannels(Long value)
+ {
+ _session.setMaximumNumberOfChannels(value);
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _name;
+ }
+
+ /**
+ * commits transactions for a transactional channel
+ *
+ * @param channelId
+ * @throws JMException if channel with given id doesn't exist or if commit fails
+ */
+ public void commitTransactions(int channelId) throws JMException
+ {
+ try
+ {
+ AMQChannel channel = _session.getChannel(channelId);
+ if (channel == null)
+ {
+ throw new JMException("The channel (channel Id = " + channelId + ") does not exist");
+ }
+
+ _session.commitTransactions(channel);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ /**
+ * rollsback the transactions for a transactional channel
+ *
+ * @param channelId
+ * @throws JMException if channel with given id doesn't exist or if rollback fails
+ */
+ public void rollbackTransactions(int channelId) throws JMException
+ {
+ try
+ {
+ AMQChannel channel = _session.getChannel(channelId);
+ if (channel == null)
+ {
+ throw new JMException("The channel (channel Id = " + channelId + ") does not exist");
+ }
+
+ _session.rollbackTransactions(channel);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ /**
+ * Creates the list of channels in tabular form from the _channelMap.
+ *
+ * @return list of channels in tabular form.
+ * @throws OpenDataException
+ */
+ public TabularData channels() throws OpenDataException
+ {
+ TabularDataSupport channelsList = new TabularDataSupport(_channelsType);
+ List<AMQChannel> list = _session.getChannels();
+
+ for (AMQChannel channel : list)
+ {
+ Object[] itemValues =
+ {
+ channel.getChannelId(), channel.isTransactional(),
+ (channel.getDefaultQueue() != null) ? channel.getDefaultQueue().getName().asString() : null,
+ channel.getUnacknowledgedMessageMap().size()
+ };
+
+ CompositeData channelData = new CompositeDataSupport(_channelType, _channelAtttibuteNames, itemValues);
+ channelsList.put(channelData);
+ }
+
+ return channelsList;
+ }
+
+ /**
+ * closes the connection. The administrator can use this management operation to close connection to free up
+ * resources.
+ * @throws JMException
+ */
+ public void closeConnection() throws JMException
+ {
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ final AMQFrame response =
+ ConnectionCloseBody.createAMQFrame(0, _session.getProtocolMajorVersion(), _session.getProtocolMinorVersion(), // AMQP version (major, minor)
+ 0, // classId
+ 0, // methodId
+ AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
+ BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION // replyText
+ );
+ _session.writeFrame(response);
+
+ try
+ {
+ _session.closeSession();
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ @Override
+ public MBeanNotificationInfo[] getNotificationInfo()
+ {
+ String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED };
+ String name = MonitorNotification.class.getName();
+ String description = "Channel count has reached threshold value";
+ MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description);
+
+ return new MBeanNotificationInfo[] { info1 };
+ }
+
+ public void notifyClients(String notificationMsg)
+ {
+ Notification n =
+ new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber,
+ System.currentTimeMillis(), notificationMsg);
+ _broadcaster.sendNotification(n);
+ }
+
+} // End of MBean class
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java
new file mode 100644
index 0000000000..29d55ce763
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.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.server.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+
+public class ExchangeInitialiser
+{
+ public void initialise(ExchangeFactory factory, ExchangeRegistry registry) throws AMQException{
+ define(registry, factory, ExchangeDefaults.DEFAULT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS);
+ define(registry, factory, ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS);
+ define(registry, factory, ExchangeDefaults.TOPIC_EXCHANGE_NAME, ExchangeDefaults.TOPIC_EXCHANGE_CLASS);
+ define(registry, factory, ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
+ define(registry, factory, ExchangeDefaults.FANOUT_EXCHANGE_NAME, ExchangeDefaults.FANOUT_EXCHANGE_CLASS);
+
+ registry.setDefaultExchange(registry.getExchange(ExchangeDefaults.DEFAULT_EXCHANGE_NAME));
+ }
+
+ private void define(ExchangeRegistry r, ExchangeFactory f,
+ AMQShortString name, AMQShortString type) throws AMQException
+ {
+ if(r.getExchange(name)== null)
+ {
+ r.registerExchange(f.createExchange(name, type, true, false, 0));
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java
new file mode 100644
index 0000000000..310deaaf55
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java
@@ -0,0 +1,67 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+public class HeartbeatConfig
+{
+ @Configured(path = "heartbeat.delay", defaultValue = "5")
+ public int delay = 5;//in secs
+ @Configured(path = "heartbeat.timeoutFactor", defaultValue = "2.0")
+ public double timeoutFactor = 2;
+
+ public double getTimeoutFactor()
+ {
+ return timeoutFactor;
+ }
+
+ public void setTimeoutFactor(double timeoutFactor)
+ {
+ this.timeoutFactor = timeoutFactor;
+ }
+
+ public int getDelay()
+ {
+ return delay;
+ }
+
+ public void setDelay(int delay)
+ {
+ this.delay = delay;
+ }
+
+ int getTimeout(int writeDelay)
+ {
+ return (int) (timeoutFactor * writeDelay);
+ }
+
+ public static HeartbeatConfig getInstance()
+ {
+ return ApplicationRegistry.getInstance().getConfiguredObject(HeartbeatConfig.class);
+ }
+
+ public String toString()
+ {
+ return "HeartBeatConfig{delay = " + delay + " timeoutFactor = " + timeoutFactor + "}";
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java
new file mode 100644
index 0000000000..e6e713ac6d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java
@@ -0,0 +1,135 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.protocol;
+
+import java.io.IOException;
+import java.util.Date;
+import java.security.Principal;
+
+import javax.management.JMException;
+import javax.management.MBeanOperationInfo;
+import javax.management.openmbean.TabularData;
+
+import org.apache.qpid.server.management.MBeanAttribute;
+import org.apache.qpid.server.management.MBeanOperation;
+import org.apache.qpid.server.management.MBeanOperationParameter;
+
+/**
+ * The management interface exposed to allow management of Connections.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedConnection
+{
+ static final String TYPE = "Connection";
+
+ @MBeanAttribute(name = "ClientId", description = "Client Id")
+ String getClientId();
+
+ @MBeanAttribute(name = "AuthorizedId", description = "User Name")
+ String getAuthorizedId();
+
+ @MBeanAttribute(name = "Version", description = "Client Version")
+ String getVersion();
+
+ /**
+ * Tells the remote address of this connection.
+ * @return remote address
+ */
+ @MBeanAttribute(name="RemoteAddress", description=TYPE + " Address")
+ String getRemoteAddress();
+
+ /**
+ * Tells the last time, the IO operation was done.
+ * @return last IO time.
+ */
+ @MBeanAttribute(name="LastIOTime", description="The last time, the IO operation was done")
+ Date getLastIoTime();
+
+ /**
+ * Tells the total number of bytes written till now.
+ * @return number of bytes written.
+ *
+ @MBeanAttribute(name="WrittenBytes", description="The total number of bytes written till now")
+ Long getWrittenBytes();
+ */
+ /**
+ * Tells the total number of bytes read till now.
+ * @return number of bytes read.
+ *
+ @MBeanAttribute(name="ReadBytes", description="The total number of bytes read till now")
+ Long getReadBytes();
+ */
+
+ /**
+ * Threshold high value for no of channels. This is useful in setting notifications or
+ * taking required action is there are more channels being created.
+ * @return threshold limit for no of channels
+ */
+ Long getMaximumNumberOfChannels();
+
+ /**
+ * Sets the threshold high value for number of channels for a connection
+ * @param value
+ */
+ @MBeanAttribute(name="MaximumNumberOfChannels", description="The threshold high value for number of channels for this connection")
+ void setMaximumNumberOfChannels(Long value);
+
+ //********** Operations *****************//
+
+ /**
+ * channel details of all the channels opened for this connection.
+ * @return general channel details
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="channels", description="Channel details for this connection")
+ TabularData channels() throws IOException, JMException;
+
+ /**
+ * Commits the transactions if the channel is transactional.
+ * @param channelId
+ * @throws JMException
+ */
+ @MBeanOperation(name="commitTransaction",
+ description="Commits the transactions for given channel Id, if the channel is transactional",
+ impact= MBeanOperationInfo.ACTION)
+ void commitTransactions(@MBeanOperationParameter(name="channel Id", description="channel Id")int channelId) throws JMException;
+
+ /**
+ * Rollsback the transactions if the channel is transactional.
+ * @param channelId
+ * @throws JMException
+ */
+ @MBeanOperation(name="rollbackTransactions",
+ description="Rollsback the transactions for given channel Id, if the channel is transactional",
+ impact= MBeanOperationInfo.ACTION)
+ void rollbackTransactions(@MBeanOperationParameter(name="channel Id", description="channel Id")int channelId) throws JMException;
+
+ /**
+ * Closes all the related channels and unregisters this connection from managed objects.
+ */
+ @MBeanOperation(name="closeConnection",
+ description="Closes this connection and all related channels",
+ impact= MBeanOperationInfo.ACTION)
+ void closeConnection() throws Exception;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java
new file mode 100644
index 0000000000..6e72aa062f
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQDataBlock;
+
+/**
+ * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to cast a frame to its expected type.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would
+ * be better just to leave that as a ClassCastException. However, check the framing layer catches this error
+ * first.
+ */
+public class UnknnownMessageTypeException extends AMQException
+{
+ public UnknnownMessageTypeException(AMQDataBlock message)
+ {
+ super("Unknown message type: " + message.getClass().getName() + ": " + message);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
new file mode 100644
index 0000000000..dd9f32a306
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
@@ -0,0 +1,1000 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQBody;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.txn.TransactionalContext;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A deliverable message.
+ */
+public class AMQMessage
+{
+ /** Used for debugging purposes. */
+ private static final Logger _log = Logger.getLogger(AMQMessage.class);
+
+ /** Used in clustering. @todo What for? */
+ private Set<Object> _tokens;
+
+ /** Only use in clustering. @todo What for? */
+ private AMQProtocolSession _publisher;
+
+ private final Long _messageId;
+
+ private final AtomicInteger _referenceCount = new AtomicInteger(1);
+
+ private AMQMessageHandle _messageHandle;
+
+ /** Holds the transactional context in which this message is being processed. */
+ private TransactionalContext _txnContext;
+
+ /**
+ * Flag to indicate whether this message has been delivered to a consumer. Used in implementing return functionality
+ * for messages published with the 'immediate' flag.
+ */
+ private boolean _deliveredToConsumer;
+
+ /** Flag to indicate that this message requires 'immediate' delivery. */
+ private boolean _immediate;
+
+ // private Subscription _takenBySubcription;
+ // private AtomicBoolean _taken = new AtomicBoolean(false);
+ private TransientMessageData _transientMessageData = new TransientMessageData();
+
+ //todo: this should be part of a messageOnQueue object
+ private Set<Subscription> _rejectedBy = null;
+
+ //todo: this should be part of a messageOnQueue object
+ private Map<AMQQueue, AtomicBoolean> _takenMap = new HashMap<AMQQueue, AtomicBoolean>();
+ //todo: this should be part of a messageOnQueue object
+ private Map<AMQQueue, Subscription> _takenBySubcriptionMap = new HashMap<AMQQueue, Subscription>();
+
+ private final int hashcode = System.identityHashCode(this);
+
+ //todo: this should be part of a messageOnQueue object
+ private long _expiration;
+
+ public String debugIdentity()
+ {
+ return "(HC:" + hashcode + " ID:" + _messageId + " Ref:" + _referenceCount.get() + ")";
+ }
+
+ public void setExpiration()
+ {
+ long expiration =
+ ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getExpiration();
+ long timestamp =
+ ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getTimestamp();
+
+ if (ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false))
+ {
+ _expiration = expiration;
+ }
+ else
+ {
+ // Update TTL to be in broker time.
+ if (expiration != 0L)
+ {
+ if (timestamp != 0L)
+ {
+ // todo perhaps use arrival time
+ long diff = (System.currentTimeMillis() - timestamp);
+
+ if ((diff > 1000L) || (diff < 1000L))
+ {
+ _expiration = expiration + diff;
+ }
+ }
+ }
+ }
+
+ }
+
+ public boolean isReferenced()
+ {
+ return _referenceCount.get() > 0;
+ }
+
+ /**
+ * Used to iterate through all the body frames associated with this message. Will not keep all the data in memory
+ * therefore is memory-efficient.
+ */
+ private class BodyFrameIterator implements Iterator<AMQDataBlock>
+ {
+ private int _channel;
+
+ private int _index = -1;
+ private AMQProtocolSession _protocolSession;
+
+ private BodyFrameIterator(AMQProtocolSession protocolSession, int channel)
+ {
+ _channel = channel;
+ _protocolSession = protocolSession;
+ }
+
+ public boolean hasNext()
+ {
+ try
+ {
+ return _index < (_messageHandle.getBodyCount(getStoreContext(), _messageId) - 1);
+ }
+ catch (AMQException e)
+ {
+ _log.error("Unable to get body count: " + e, e);
+
+ return false;
+ }
+ }
+
+ public AMQDataBlock next()
+ {
+ try
+ {
+
+ AMQBody cb =
+ getProtocolVersionMethodConverter().convertToBody(_messageHandle.getContentChunk(getStoreContext(),
+ _messageId, ++_index));
+
+ return new AMQFrame(_channel, cb);
+ }
+ catch (AMQException e)
+ {
+ // have no choice but to throw a runtime exception
+ throw new RuntimeException("Error getting content body: " + e, e);
+ }
+
+ }
+
+ private ProtocolVersionMethodConverter getProtocolVersionMethodConverter()
+ {
+ return _protocolSession.getRegistry().getProtocolVersionMethodConverter();
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public StoreContext getStoreContext()
+ {
+ return _txnContext.getStoreContext();
+ }
+
+ private class BodyContentIterator implements Iterator<ContentChunk>
+ {
+
+ private int _index = -1;
+
+ public boolean hasNext()
+ {
+ try
+ {
+ return _index < (_messageHandle.getBodyCount(getStoreContext(), _messageId) - 1);
+ }
+ catch (AMQException e)
+ {
+ _log.error("Error getting body count: " + e, e);
+
+ return false;
+ }
+ }
+
+ public ContentChunk next()
+ {
+ try
+ {
+ return _messageHandle.getContentChunk(getStoreContext(), _messageId, ++_index);
+ }
+ catch (AMQException e)
+ {
+ throw new RuntimeException("Error getting content body: " + e, e);
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext)
+ {
+ _messageId = messageId;
+ _txnContext = txnContext;
+ _immediate = info.isImmediate();
+ _transientMessageData.setMessagePublishInfo(info);
+
+ }
+
+ /**
+ * Used when recovering, i.e. when the message store is creating references to messages. In that case, the normal
+ * enqueue/routingComplete is not done since the recovery process is responsible for routing the messages to
+ * queues.
+ *
+ * @param messageId
+ * @param store
+ * @param factory
+ *
+ * @throws AMQException
+ */
+ public AMQMessage(Long messageId, MessageStore store, MessageHandleFactory factory, TransactionalContext txnConext)
+ throws AMQException
+ {
+ _messageId = messageId;
+ _messageHandle = factory.createMessageHandle(messageId, store, true);
+ _txnContext = txnConext;
+ _transientMessageData = null;
+ }
+
+ /**
+ * Used in testing only. This allows the passing of the content header immediately on construction.
+ *
+ * @param messageId
+ * @param info
+ * @param txnContext
+ * @param contentHeader
+ */
+ public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext,
+ ContentHeaderBody contentHeader) throws AMQException
+ {
+ this(messageId, info, txnContext);
+ setContentHeaderBody(contentHeader);
+ }
+
+ /**
+ * Used in testing only. This allows the passing of the content header and some body fragments on construction.
+ *
+ * @param messageId
+ * @param info
+ * @param txnContext
+ * @param contentHeader
+ * @param destinationQueues
+ * @param contentBodies
+ *
+ * @throws AMQException
+ */
+ public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext,
+ ContentHeaderBody contentHeader, List<AMQQueue> destinationQueues, List<ContentChunk> contentBodies,
+ MessageStore messageStore, StoreContext storeContext, MessageHandleFactory messageHandleFactory) throws AMQException
+ {
+ this(messageId, info, txnContext, contentHeader);
+ _transientMessageData.setDestinationQueues(destinationQueues);
+ routingComplete(messageStore, storeContext, messageHandleFactory);
+ for (ContentChunk cb : contentBodies)
+ {
+ addContentBodyFrame(storeContext, cb);
+ }
+ }
+
+ protected AMQMessage(AMQMessage msg) throws AMQException
+ {
+ _messageId = msg._messageId;
+ _messageHandle = msg._messageHandle;
+ _txnContext = msg._txnContext;
+ _deliveredToConsumer = msg._deliveredToConsumer;
+ _transientMessageData = msg._transientMessageData;
+ }
+
+ public Iterator<AMQDataBlock> getBodyFrameIterator(AMQProtocolSession protocolSession, int channel)
+ {
+ return new BodyFrameIterator(protocolSession, channel);
+ }
+
+ public Iterator<ContentChunk> getContentBodyIterator()
+ {
+ return new BodyContentIterator();
+ }
+
+ public ContentHeaderBody getContentHeaderBody() throws AMQException
+ {
+ if (_transientMessageData != null)
+ {
+ return _transientMessageData.getContentHeaderBody();
+ }
+ else
+ {
+ return _messageHandle.getContentHeaderBody(getStoreContext(), _messageId);
+ }
+ }
+
+ public void setContentHeaderBody(ContentHeaderBody contentHeaderBody) throws AMQException
+ {
+ _transientMessageData.setContentHeaderBody(contentHeaderBody);
+ }
+
+ public void routingComplete(MessageStore store, StoreContext storeContext, MessageHandleFactory factory)
+ throws AMQException
+ {
+ final boolean persistent = isPersistent();
+ _messageHandle = factory.createMessageHandle(_messageId, store, persistent);
+ if (persistent)
+ {
+ _txnContext.beginTranIfNecessary();
+ }
+
+ // enqueuing the messages ensure that if required the destinations are recorded to a
+ // persistent store
+
+ for (AMQQueue q : _transientMessageData.getDestinationQueues())
+ {
+ _messageHandle.enqueue(storeContext, _messageId, q);
+ }
+
+ if (_transientMessageData.getContentHeaderBody().bodySize == 0)
+ {
+ deliver(storeContext);
+ }
+ }
+
+ public boolean addContentBodyFrame(StoreContext storeContext, ContentChunk contentChunk) throws AMQException
+ {
+ _transientMessageData.addBodyLength(contentChunk.getSize());
+ final boolean allContentReceived = isAllContentReceived();
+ _messageHandle.addContentBodyFrame(storeContext, _messageId, contentChunk, allContentReceived);
+ if (allContentReceived)
+ {
+ deliver(storeContext);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public boolean isAllContentReceived() throws AMQException
+ {
+ return _transientMessageData.isAllContentReceived();
+ }
+
+ public Long getMessageId()
+ {
+ return _messageId;
+ }
+
+ /**
+ * Creates a long-lived reference to this message, and increments the count of such references, as an atomic
+ * operation.
+ */
+ public AMQMessage takeReference()
+ {
+ incrementReference(); // _referenceCount.incrementAndGet();
+
+ return this;
+ }
+
+ /** Threadsafe. Increment the reference count on the message. */
+ public void incrementReference()
+ {
+ _referenceCount.incrementAndGet();
+ // if (_log.isDebugEnabled())
+ // {
+ // _log.debug("Ref count on message " + debugIdentity() + " incremented " + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6));
+ // }
+ }
+
+ /**
+ * Threadsafe. This will decrement the reference count and when it reaches zero will remove the message from the
+ * message store.
+ *
+ * @param storeContext
+ *
+ * @throws MessageCleanupException when an attempt was made to remove the message from the message store and that
+ * failed
+ */
+ public void decrementReference(StoreContext storeContext) throws MessageCleanupException
+ {
+ int count = _referenceCount.decrementAndGet();
+
+ // note that the operation of decrementing the reference count and then removing the message does not
+ // have to be atomic since the ref count starts at 1 and the exchange itself decrements that after
+ // the message has been passed to all queues. i.e. we are
+ // not relying on the all the increments having taken place before the delivery manager decrements.
+ if (count == 0)
+ {
+ try
+ {
+ // if (_log.isDebugEnabled())
+ // {
+ // _log.debug("Decremented ref count on message " + debugIdentity() + " is zero; removing message" + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6));
+ // }
+
+ // must check if the handle is null since there may be cases where we decide to throw away a message
+ // and the handle has not yet been constructed
+ if (_messageHandle != null)
+ {
+ _messageHandle.removeMessage(storeContext, _messageId);
+ }
+ }
+ catch (AMQException e)
+ {
+ // to maintain consistency, we revert the count
+ incrementReference();
+ throw new MessageCleanupException(_messageId, e);
+ }
+ }
+ else
+ {
+ if (count < 0)
+ {
+ throw new MessageCleanupException("Reference count for message id " + debugIdentity()
+ + " has gone below 0.");
+ }
+ }
+ }
+
+ public void setPublisher(AMQProtocolSession publisher)
+ {
+ _publisher = publisher;
+ }
+
+ public AMQProtocolSession getPublisher()
+ {
+ return _publisher;
+ }
+
+ /**
+ * Called selectors to determin if the message has already been sent
+ *
+ * @return _deliveredToConsumer
+ */
+ public boolean getDeliveredToConsumer()
+ {
+ return _deliveredToConsumer;
+ }
+
+ public boolean isTaken(AMQQueue queue)
+ {
+ // return _taken.get();
+
+ synchronized (this)
+ {
+ AtomicBoolean taken = _takenMap.get(queue);
+ if (taken == null)
+ {
+ taken = new AtomicBoolean(false);
+ _takenMap.put(queue, taken);
+ }
+
+ return taken.get();
+ }
+ }
+
+ public boolean taken(AMQQueue queue, Subscription sub)
+ {
+ // if (_taken.getAndSet(true))
+ // {
+ // return true;
+ // }
+ // else
+ // {
+ // _takenBySubcription = sub;
+ // return false;
+ // }
+
+ synchronized (this)
+ {
+ AtomicBoolean taken = _takenMap.get(queue);
+ if (taken == null)
+ {
+ taken = new AtomicBoolean(false);
+ }
+
+ if (taken.getAndSet(true))
+ {
+ return true;
+ }
+ else
+ {
+ _takenMap.put(queue, taken);
+ _takenBySubcriptionMap.put(queue, sub);
+
+ return false;
+ }
+ }
+ }
+
+ public void release(AMQQueue queue)
+ {
+ if (_log.isTraceEnabled())
+ {
+ _log.trace("Releasing Message:" + debugIdentity());
+ }
+
+ // _taken.set(false);
+ // _takenBySubcription = null;
+
+ synchronized (this)
+ {
+ AtomicBoolean taken = _takenMap.get(queue);
+ if (taken == null)
+ {
+ taken = new AtomicBoolean(false);
+ }
+ else
+ {
+ taken.set(false);
+ }
+
+ _deliveredToConsumer = false;
+ _takenMap.put(queue, taken);
+ _takenBySubcriptionMap.put(queue, null);
+ }
+ }
+
+ public boolean checkToken(Object token)
+ {
+
+ if (_tokens == null)
+ {
+ _tokens = new HashSet<Object>();
+ }
+
+ if (_tokens.contains(token))
+ {
+ return true;
+ }
+ else
+ {
+ _tokens.add(token);
+
+ return false;
+ }
+ }
+
+ /**
+ * Registers a queue to which this message is to be delivered. This is called from the exchange when it is routing
+ * the message. This will be called before any content bodies have been received so that the choice of
+ * AMQMessageHandle implementation can be picked based on various criteria.
+ *
+ * @param queue the queue
+ *
+ * @throws org.apache.qpid.AMQException if there is an error enqueuing the message
+ */
+ public void enqueue(AMQQueue queue) throws AMQException
+ {
+ _transientMessageData.addDestinationQueue(queue);
+ }
+
+ /**
+ * NOTE: Think about why you are using this method. Normal usages would want to do
+ * AMQQueue.dequeue(StoreContext, AMQMessage)
+ * This will keep the queue statistics up-to-date.
+ * Currently this method is only called _correctly_ from AMQQueue dequeue.
+ * Ideally we would have a better way for the queue to dequeue the message.
+ * Especially since enqueue isn't the recipriocal of this method.
+ * @deprecated
+ * @param storeContext
+ * @param queue
+ * @throws AMQException
+ */
+ void dequeue(StoreContext storeContext, AMQQueue queue) throws AMQException
+ {
+ _messageHandle.dequeue(storeContext, _messageId, queue);
+ }
+
+ public boolean isPersistent() throws AMQException
+ {
+ if (_transientMessageData != null)
+ {
+ return _transientMessageData.isPersistent();
+ }
+ else
+ {
+ return _messageHandle.isPersistent(getStoreContext(), _messageId);
+ }
+ }
+
+ /**
+ * Called to enforce the 'immediate' flag.
+ *
+ * @throws NoConsumersException if the message is marked for immediate delivery but has not been marked as delivered
+ * to a consumer
+ */
+ public void checkDeliveredToConsumer() throws NoConsumersException
+ {
+
+ if (_immediate && !_deliveredToConsumer)
+ {
+ throw new NoConsumersException(this);
+ }
+ }
+
+ public MessagePublishInfo getMessagePublishInfo() throws AMQException
+ {
+ MessagePublishInfo pb;
+ if (_transientMessageData != null)
+ {
+ pb = _transientMessageData.getMessagePublishInfo();
+ }
+ else
+ {
+ pb = _messageHandle.getMessagePublishInfo(getStoreContext(), _messageId);
+ }
+
+ return pb;
+ }
+
+ public boolean isRedelivered()
+ {
+ return _messageHandle.isRedelivered();
+ }
+
+ public void setRedelivered(boolean redelivered)
+ {
+ _messageHandle.setRedelivered(redelivered);
+ }
+
+ public long getArrivalTime()
+ {
+ return _messageHandle.getArrivalTime();
+ }
+
+ /**
+ * Checks to see if the message has expired. If it has the message is dequeued.
+ *
+ * @param queue The queue to check the expiration against. (Currently not used)
+ *
+ * @return true if the message has expire
+ *
+ * @throws AMQException
+ */
+ public boolean expired(AMQQueue queue) throws AMQException
+ {
+ // note: If the storecontext isn't need then we can remove the getChannel() from Subscription.
+
+ if (_expiration != 0L)
+ {
+ long now = System.currentTimeMillis();
+
+ return (now > _expiration);
+ }
+
+ return false;
+ }
+
+ /**
+ * Called when this message is delivered to a consumer. (used to implement the 'immediate' flag functionality).
+ * And for selector efficiency.
+ */
+ public void setDeliveredToConsumer()
+ {
+ _deliveredToConsumer = true;
+ }
+
+ private void deliver(StoreContext storeContext) throws AMQException
+ {
+ // we get a reference to the destination queues now so that we can clear the
+ // transient message data as quickly as possible
+ List<AMQQueue> destinationQueues = _transientMessageData.getDestinationQueues();
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Delivering message " + debugIdentity() + " to " + destinationQueues);
+ }
+
+ try
+ {
+ // first we allow the handle to know that the message has been fully received. This is useful if it is
+ // maintaining any calculated values based on content chunks
+ _messageHandle.setPublishAndContentHeaderBody(storeContext, _messageId,
+ _transientMessageData.getMessagePublishInfo(), _transientMessageData.getContentHeaderBody());
+
+ // we then allow the transactional context to do something with the message content
+ // now that it has all been received, before we attempt delivery
+ _txnContext.messageFullyReceived(isPersistent());
+
+ _transientMessageData = null;
+
+ for (AMQQueue q : destinationQueues)
+ {
+ // Increment the references to this message for each queue delivery.
+ incrementReference();
+ // normal deliver so add this message at the end.
+ _txnContext.deliver(this, q, false);
+ }
+ }
+ finally
+ {
+ destinationQueues.clear();
+ // Remove refence for routing process . Reference count should now == delivered queue count
+ decrementReference(storeContext);
+ }
+ }
+
+ /*
+ public void writeDeliver(AMQProtocolSession protocolSession, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException
+ {
+ ByteBuffer deliver = createEncodedDeliverFrame(protocolSession, channelId, deliveryTag, consumerTag);
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ getContentHeaderBody());
+
+ final int bodyCount = _messageHandle.getBodyCount(getStoreContext(), _messageId);
+ if (bodyCount == 0)
+ {
+ SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
+ contentHeader);
+
+ protocolSession.writeFrame(compositeBlock);
+ }
+ else
+ {
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = _messageHandle.getContentChunk(getStoreContext(), _messageId, 0);
+
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
+ AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ protocolSession.writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for (int i = 1; i < bodyCount; i++)
+ {
+ cb = _messageHandle.getContentChunk(getStoreContext(), _messageId, i);
+ protocolSession.writeFrame(new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ }
+
+
+ }
+
+
+ }
+
+ public void writeGetOk(AMQProtocolSession protocolSession, int channelId, long deliveryTag, int queueSize) throws AMQException
+ {
+ ByteBuffer deliver = createEncodedGetOkFrame(protocolSession, channelId, deliveryTag, queueSize);
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ getContentHeaderBody());
+
+ final int bodyCount = _messageHandle.getBodyCount(getStoreContext(), _messageId);
+ if (bodyCount == 0)
+ {
+ SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
+ contentHeader);
+ protocolSession.writeFrame(compositeBlock);
+ }
+ else
+ {
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = _messageHandle.getContentChunk(getStoreContext(), _messageId, 0);
+
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
+ AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent);
+ protocolSession.writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for (int i = 1; i < bodyCount; i++)
+ {
+ cb = _messageHandle.getContentChunk(getStoreContext(), _messageId, i);
+ protocolSession.writeFrame(new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ }
+
+
+ }
+
+
+ }
+
+
+ private ByteBuffer createEncodedDeliverFrame(AMQProtocolSession protocolSession, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException
+ {
+ MessagePublishInfo pb = getMessagePublishInfo();
+ AMQFrame deliverFrame = BasicDeliverBody.createAMQFrame(channelId, protocolSession.getProtocolMajorVersion(), (byte) 0, consumerTag,
+ deliveryTag, pb.getExchange(), _messageHandle.isRedelivered(),
+ pb.getRoutingKey());
+ ByteBuffer buf = ByteBuffer.allocate((int) deliverFrame.getSize()); // XXX: Could cast be a problem?
+ deliverFrame.writePayload(buf);
+ buf.flip();
+ return buf;
+ }
+
+ private ByteBuffer createEncodedGetOkFrame(AMQProtocolSession protocolSession, int channelId, long deliveryTag, int queueSize)
+ throws AMQException
+ {
+ MessagePublishInfo pb = getMessagePublishInfo();
+ AMQFrame getOkFrame = BasicGetOkBody.createAMQFrame(channelId,
+ protocolSession.getProtocolMajorVersion(),
+ protocolSession.getProtocolMinorVersion(),
+ deliveryTag, pb.getExchange(),
+ queueSize,
+ _messageHandle.isRedelivered(),
+ pb.getRoutingKey());
+ ByteBuffer buf = ByteBuffer.allocate((int) getOkFrame.getSize()); // XXX: Could cast be a problem?
+ getOkFrame.writePayload(buf);
+ buf.flip();
+ return buf;
+ }
+
+ private ByteBuffer createEncodedReturnFrame(AMQProtocolSession protocolSession, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ {
+ AMQFrame returnFrame = BasicReturnBody.createAMQFrame(channelId,
+ protocolSession.getProtocolMajorVersion(),
+ protocolSession.getProtocolMinorVersion(),
+ getMessagePublishInfo().getExchange(),
+ replyCode, replyText,
+ getMessagePublishInfo().getRoutingKey());
+ ByteBuffer buf = ByteBuffer.allocate((int) returnFrame.getSize()); // XXX: Could cast be a problem?
+ returnFrame.writePayload(buf);
+ buf.flip();
+ return buf;
+ }
+
+ public void writeReturn(AMQProtocolSession protocolSession, int channelId, int replyCode, AMQShortString replyText)
+ throws AMQException
+ {
+ ByteBuffer returnFrame = createEncodedReturnFrame(protocolSession, channelId, replyCode, replyText);
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ getContentHeaderBody());
+
+ Iterator<AMQDataBlock> bodyFrameIterator = getBodyFrameIterator(protocolSession, channelId);
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ if (bodyFrameIterator.hasNext())
+ {
+ AMQDataBlock firstContentBody = bodyFrameIterator.next();
+ AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, headerAndFirstContent);
+ protocolSession.writeFrame(compositeBlock);
+ }
+ else
+ {
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame,
+ new AMQDataBlock[]{contentHeader});
+ protocolSession.writeFrame(compositeBlock);
+ }
+
+ //
+ // Now start writing out the other content bodies
+ // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded
+ //
+ while (bodyFrameIterator.hasNext())
+ {
+ protocolSession.writeFrame(bodyFrameIterator.next());
+ }
+ }
+ */
+
+ public AMQMessageHandle getMessageHandle()
+ {
+ return _messageHandle;
+ }
+
+ public long getSize()
+ {
+ try
+ {
+ long size = getContentHeaderBody().bodySize;
+
+ return size;
+ }
+ catch (AMQException e)
+ {
+ _log.error(e.toString(), e);
+
+ return 0;
+ }
+
+ }
+
+ public void restoreTransientMessageData() throws AMQException
+ {
+ TransientMessageData transientMessageData = new TransientMessageData();
+ transientMessageData.setMessagePublishInfo(getMessagePublishInfo());
+ transientMessageData.setContentHeaderBody(getContentHeaderBody());
+ transientMessageData.addBodyLength(getContentHeaderBody().getSize());
+ _transientMessageData = transientMessageData;
+ }
+
+ public void clearTransientMessageData()
+ {
+ _transientMessageData = null;
+ }
+
+ public String toString()
+ {
+ // return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount + "; taken : " +
+ // _taken + " by :" + _takenBySubcription;
+
+ return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount + "; taken for queues: "
+ + _takenMap.toString() + " by Subs:" + _takenBySubcriptionMap.toString();
+ }
+
+ public Subscription getDeliveredSubscription(AMQQueue queue)
+ {
+ // return _takenBySubcription;
+ synchronized (this)
+ {
+ return _takenBySubcriptionMap.get(queue);
+ }
+ }
+
+ public void reject(Subscription subscription)
+ {
+ if (subscription != null)
+ {
+ if (_rejectedBy == null)
+ {
+ _rejectedBy = new HashSet<Subscription>();
+ }
+
+ _rejectedBy.add(subscription);
+ }
+ else
+ {
+ _log.warn("Requesting rejection by null subscriber:" + debugIdentity());
+ }
+ }
+
+ public boolean isRejectedBy(Subscription subscription)
+ {
+ boolean rejected = _rejectedBy != null;
+
+ if (rejected) // We have subscriptions that rejected this message
+ {
+ return _rejectedBy.contains(subscription);
+ }
+ else // This messasge hasn't been rejected yet.
+ {
+ return rejected;
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java
new file mode 100644
index 0000000000..ede55b3bbf
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.framing.ContentHeaderBody;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+
+/**
+ * A pluggable way of getting message data. Implementations can provide intelligent caching for example or
+ * even no caching at all to minimise the broker memory footprint.
+ *
+ * The method all take a messageId to avoid having to store it in the instance - the AMQMessage container
+ * must already keen the messageId so it is pointless storing it twice.
+ */
+public interface AMQMessageHandle
+{
+ ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException;
+
+ /**
+ * @return the number of body frames associated with this message
+ */
+ int getBodyCount(StoreContext context, Long messageId) throws AMQException;
+
+ /**
+ * @return the size of the body
+ */
+ long getBodySize(StoreContext context, Long messageId) throws AMQException;
+
+ /**
+ * Get a particular content body
+ * @param index the index of the body to retrieve, must be between 0 and getBodyCount() - 1
+ * @return a content body
+ * @throws IllegalArgumentException if the index is invalid
+ */
+ ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws IllegalArgumentException, AMQException;
+
+ void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentBody, boolean isLastContentBody) throws AMQException;
+
+ MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException;
+
+ boolean isRedelivered();
+
+ void setRedelivered(boolean redelivered);
+
+ boolean isPersistent(StoreContext context, Long messageId) throws AMQException;
+
+ void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo,
+ ContentHeaderBody contentHeaderBody)
+ throws AMQException;
+
+ void removeMessage(StoreContext storeContext, Long messageId) throws AMQException;
+
+ void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException;
+
+ void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException;
+
+ long getArrivalTime();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
new file mode 100644
index 0000000000..0c52a358f7
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
@@ -0,0 +1,940 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.management.Managable;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import javax.management.JMException;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * This is an AMQ Queue, and should not be confused with a JMS queue or any other abstraction like that. It is described
+ * fully in RFC 006.
+ */
+public class AMQQueue implements Managable, Comparable
+{
+ /**
+ * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription
+ * already exists.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to create a subscription, because an exclusive subscription already exists.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Move to top level, used outside this class.
+ */
+ public static final class ExistingExclusiveSubscription extends AMQException
+ {
+
+ public ExistingExclusiveSubscription()
+ {
+ super("");
+ }
+ }
+
+ /**
+ * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusize subscription, as a subscription
+ * already exists.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to create an exclusize subscription, as a subscription already exists.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Move to top level, used outside this class.
+ */
+ public static final class ExistingSubscriptionPreventsExclusive extends AMQException
+ {
+ public ExistingSubscriptionPreventsExclusive()
+ {
+ super("");
+ }
+ }
+
+ private static final Logger _logger = Logger.getLogger(AMQQueue.class);
+
+ private final AMQShortString _name;
+
+ /** null means shared */
+ private final AMQShortString _owner;
+
+ private final boolean _durable;
+
+ /** If true, this queue is deleted when the last subscriber is removed */
+ private final boolean _autoDelete;
+
+ /** Holds subscribers to the queue. */
+ private final SubscriptionSet _subscribers;
+
+ private final SubscriptionFactory _subscriptionFactory;
+
+ private final AtomicInteger _subscriberCount = new AtomicInteger();
+
+ private final AtomicBoolean _isExclusive = new AtomicBoolean();
+
+ private final AtomicBoolean _deleted = new AtomicBoolean(false);
+
+ private List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>();
+
+ /** Manages message delivery. */
+ private final DeliveryManager _deliveryMgr;
+
+ /** Used to track bindings to exchanges so that on deletion they can easily be cancelled. */
+ private final ExchangeBindings _bindings = new ExchangeBindings(this);
+
+ /** Executor on which asynchronous delivery will be carriedout where required */
+ private final Executor _asyncDelivery;
+
+ private final AMQQueueMBean _managedObject;
+
+ private final VirtualHost _virtualHost;
+
+ /** max allowed size(KB) of a single message */
+ @Configured(path = "maximumMessageSize", defaultValue = "0")
+ public long _maximumMessageSize;
+
+ /** max allowed number of messages on a queue. */
+ @Configured(path = "maximumMessageCount", defaultValue = "0")
+ public long _maximumMessageCount;
+
+ /** max queue depth for the queue */
+ @Configured(path = "maximumQueueDepth", defaultValue = "0")
+ public long _maximumQueueDepth;
+
+ /** maximum message age before alerts occur */
+ @Configured(path = "maximumMessageAge", defaultValue = "0")
+ public long _maximumMessageAge;
+
+ /** the minimum interval between sending out consequetive alerts of the same type */
+ @Configured(path = "minimumAlertRepeatGap", defaultValue = "0")
+ public long _minimumAlertRepeatGap;
+
+ /** total messages received by the queue since startup. */
+ public AtomicLong _totalMessagesReceived = new AtomicLong();
+
+ public int compareTo(Object o)
+ {
+ return _name.compareTo(((AMQQueue) o).getName());
+ }
+
+ public AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
+ throws AMQException
+ {
+ this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(),
+ new SubscriptionSet(), new SubscriptionImpl.Factory());
+ }
+
+ protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete,
+ VirtualHost virtualHost, SubscriptionSet subscribers) throws AMQException
+ {
+ this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(), subscribers,
+ new SubscriptionImpl.Factory());
+ }
+
+ protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete,
+ VirtualHost virtualHost, Executor asyncDelivery, SubscriptionSet subscribers,
+ SubscriptionFactory subscriptionFactory) throws AMQException
+ {
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Queue name must not be null");
+ }
+
+ if (virtualHost == null)
+ {
+ throw new IllegalArgumentException("Virtual Host must not be null");
+ }
+
+ _name = name;
+ _durable = durable;
+ _owner = owner;
+ _autoDelete = autoDelete;
+ _virtualHost = virtualHost;
+ _asyncDelivery = asyncDelivery;
+
+ _managedObject = createMBean();
+ _managedObject.register();
+
+ _subscribers = subscribers;
+ _subscriptionFactory = subscriptionFactory;
+ _deliveryMgr = new ConcurrentSelectorDeliveryManager(_subscribers, this);
+ }
+
+ private AMQQueueMBean createMBean() throws AMQException
+ {
+ try
+ {
+ return new AMQQueueMBean(this);
+ }
+ catch (JMException ex)
+ {
+ throw new AMQException("AMQQueue MBean creation has failed ", ex);
+ }
+ }
+
+ public AMQShortString getName()
+ {
+ return _name;
+ }
+
+ public boolean isShared()
+ {
+ return _owner == null;
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public AMQShortString getOwner()
+ {
+ return _owner;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ public boolean isDeleted()
+ {
+ return _deleted.get();
+ }
+
+ /** @return no of messages(undelivered) on the queue. */
+ public int getMessageCount()
+ {
+ return _deliveryMgr.getQueueMessageCount();
+ }
+
+ /** @return List of messages(undelivered) on the queue. */
+ public List<AMQMessage> getMessagesOnTheQueue()
+ {
+ return _deliveryMgr.getMessages();
+ }
+
+ /**
+ * Returns messages within the given range of message Ids.
+ *
+ * @param fromMessageId
+ * @param toMessageId
+ *
+ * @return List of messages
+ */
+ public List<AMQMessage> getMessagesOnTheQueue(long fromMessageId, long toMessageId)
+ {
+ return _deliveryMgr.getMessages(fromMessageId, toMessageId);
+ }
+
+ public long getQueueDepth()
+ {
+ return _deliveryMgr.getTotalMessageSize();
+ }
+
+ /**
+ * @param messageId
+ *
+ * @return AMQMessage with give id if exists. null if AMQMessage with given id doesn't exist.
+ */
+ public AMQMessage getMessageOnTheQueue(long messageId)
+ {
+ List<AMQMessage> list = getMessagesOnTheQueue(messageId, messageId);
+ if ((list == null) || (list.size() == 0))
+ {
+ return null;
+ }
+
+ return list.get(0);
+ }
+
+ /**
+ * Moves messages from this queue to another queue, and also commits the move on the message store. Delivery activity
+ * on the queues being moved between is suspended during the move.
+ *
+ * @param fromMessageId The first message id to move.
+ * @param toMessageId The last message id to move.
+ * @param queueName The queue to move the messages to.
+ * @param storeContext The context of the message store under which to perform the move. This is associated with
+ * the stores transactional context.
+ */
+ public synchronized void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName,
+ StoreContext storeContext)
+ {
+ AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName));
+
+ MessageStore fromStore = getVirtualHost().getMessageStore();
+ MessageStore toStore = toQueue.getVirtualHost().getMessageStore();
+
+ if (toStore != fromStore)
+ {
+ throw new RuntimeException("Can only move messages between queues on the same message store.");
+ }
+
+ try
+ {
+ // Obtain locks to prevent activity on the queues being moved between.
+ startMovingMessages();
+ toQueue.startMovingMessages();
+
+ // Get the list of messages to move.
+ List<AMQMessage> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId);
+
+ try
+ {
+ fromStore.beginTran(storeContext);
+
+ // Move the messages in on the message store.
+ for (AMQMessage message : foundMessagesList)
+ {
+ fromStore.dequeueMessage(storeContext, _name, message.getMessageId());
+ toStore.enqueueMessage(storeContext, toQueue._name, message.getMessageId());
+ }
+
+ // Commit and flush the move transcations.
+ try
+ {
+ fromStore.commitTran(storeContext);
+ }
+ catch (AMQException e)
+ {
+ throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e);
+ }
+
+ // Move the messages on the in-memory queues.
+ toQueue.enqueueMovedMessages(storeContext, foundMessagesList);
+ _deliveryMgr.removeMovedMessages(foundMessagesList);
+ }
+ // Abort the move transactions on move failures.
+ catch (AMQException e)
+ {
+ try
+ {
+ fromStore.abortTran(storeContext);
+ }
+ catch (AMQException ae)
+ {
+ throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae);
+ }
+ }
+ }
+ // Release locks to allow activity on the queues being moved between to continue.
+ finally
+ {
+ toQueue.stopMovingMessages();
+ stopMovingMessages();
+ }
+ }
+
+ /**
+ * Copies messages on this queue to another queue, and also commits the move on the message store. Delivery activity
+ * on the queues being moved between is suspended during the move.
+ *
+ * @param fromMessageId The first message id to move.
+ * @param toMessageId The last message id to move.
+ * @param queueName The queue to move the messages to.
+ * @param storeContext The context of the message store under which to perform the move. This is associated with
+ * the stores transactional context.
+ */
+ public synchronized void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName,
+ StoreContext storeContext)
+ {
+ AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName));
+
+ MessageStore fromStore = getVirtualHost().getMessageStore();
+ MessageStore toStore = toQueue.getVirtualHost().getMessageStore();
+
+ if (toStore != fromStore)
+ {
+ throw new RuntimeException("Can only move messages between queues on the same message store.");
+ }
+
+ try
+ {
+ // Obtain locks to prevent activity on the queues being moved between.
+ startMovingMessages();
+ toQueue.startMovingMessages();
+
+ // Get the list of messages to move.
+ List<AMQMessage> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId);
+
+ try
+ {
+ fromStore.beginTran(storeContext);
+
+ // Move the messages in on the message store.
+ for (AMQMessage message : foundMessagesList)
+ {
+ toStore.enqueueMessage(storeContext, toQueue._name, message.getMessageId());
+ message.takeReference();
+ }
+
+ // Commit and flush the move transcations.
+ try
+ {
+ fromStore.commitTran(storeContext);
+ }
+ catch (AMQException e)
+ {
+ throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e);
+ }
+
+ // Move the messages on the in-memory queues.
+ toQueue.enqueueMovedMessages(storeContext, foundMessagesList);
+ }
+ // Abort the move transactions on move failures.
+ catch (AMQException e)
+ {
+ try
+ {
+ fromStore.abortTran(storeContext);
+ }
+ catch (AMQException ae)
+ {
+ throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae);
+ }
+ }
+ }
+ // Release locks to allow activity on the queues being moved between to continue.
+ finally
+ {
+ toQueue.stopMovingMessages();
+ stopMovingMessages();
+ }
+ }
+
+ /**
+ * Removes messages from this queue, and also commits the remove on the message store. Delivery activity
+ * on the queues being moved between is suspended during the remove.
+ *
+ * @param fromMessageId The first message id to move.
+ * @param toMessageId The last message id to move.
+ * @param storeContext The context of the message store under which to perform the move. This is associated with
+ * the stores transactional context.
+ */
+ public synchronized void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext)
+ {
+ MessageStore fromStore = getVirtualHost().getMessageStore();
+
+ try
+ {
+ // Obtain locks to prevent activity on the queues being moved between.
+ startMovingMessages();
+
+ // Get the list of messages to move.
+ List<AMQMessage> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId);
+
+ try
+ {
+ fromStore.beginTran(storeContext);
+
+ // remove the messages in on the message store.
+ for (AMQMessage message : foundMessagesList)
+ {
+ fromStore.dequeueMessage(storeContext, _name, message.getMessageId());
+ }
+
+ // Commit and flush the move transcations.
+ try
+ {
+ fromStore.commitTran(storeContext);
+ }
+ catch (AMQException e)
+ {
+ throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e);
+ }
+
+ // remove the messages on the in-memory queues.
+ _deliveryMgr.removeMovedMessages(foundMessagesList);
+ }
+ // Abort the move transactions on move failures.
+ catch (AMQException e)
+ {
+ try
+ {
+ fromStore.abortTran(storeContext);
+ }
+ catch (AMQException ae)
+ {
+ throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae);
+ }
+ }
+ }
+ // Release locks to allow activity on the queues being moved between to continue.
+ finally
+ {
+ stopMovingMessages();
+ }
+ }
+
+ public void startMovingMessages()
+ {
+ _deliveryMgr.startMovingMessages();
+ }
+
+ private void enqueueMovedMessages(StoreContext storeContext, List<AMQMessage> messageList)
+ {
+ _deliveryMgr.enqueueMovedMessages(storeContext, messageList);
+ _totalMessagesReceived.addAndGet(messageList.size());
+ }
+
+ public void stopMovingMessages()
+ {
+ _deliveryMgr.stopMovingMessages();
+ _deliveryMgr.processAsync(_asyncDelivery);
+ }
+
+ /** @return MBean object associated with this Queue */
+ public ManagedObject getManagedObject()
+ {
+ return _managedObject;
+ }
+
+ public long getMaximumMessageSize()
+ {
+ return _maximumMessageSize;
+ }
+
+ public void setMaximumMessageSize(long value)
+ {
+ _maximumMessageSize = value;
+ }
+
+ public int getConsumerCount()
+ {
+ return _subscribers.size();
+ }
+
+ public int getActiveConsumerCount()
+ {
+ return _subscribers.getWeight();
+ }
+
+ public long getReceivedMessageCount()
+ {
+ return _totalMessagesReceived.get();
+ }
+
+ public long getMaximumMessageCount()
+ {
+ return _maximumMessageCount;
+ }
+
+ public void setMaximumMessageCount(long value)
+ {
+ _maximumMessageCount = value;
+ }
+
+ public long getMaximumQueueDepth()
+ {
+ return _maximumQueueDepth;
+ }
+
+ // Sets the queue depth, the max queue size
+ public void setMaximumQueueDepth(long value)
+ {
+ _maximumQueueDepth = value;
+ }
+
+ public long getOldestMessageArrivalTime()
+ {
+ return _deliveryMgr.getOldestMessageArrival();
+
+ }
+
+ /** Removes the AMQMessage from the top of the queue. */
+ public synchronized void deleteMessageFromTop(StoreContext storeContext) throws AMQException
+ {
+ _deliveryMgr.removeAMessageFromTop(storeContext, this);
+ }
+
+ /** removes all the messages from the queue. */
+ public synchronized long clearQueue(StoreContext storeContext) throws AMQException
+ {
+ return _deliveryMgr.clearAllMessages(storeContext);
+ }
+
+ public void bind(AMQShortString routingKey, FieldTable arguments, Exchange exchange) throws AMQException
+ {
+ exchange.registerQueue(routingKey, this, arguments);
+ if (isDurable() && exchange.isDurable())
+ {
+ _virtualHost.getMessageStore().bindQueue(exchange, routingKey, this, arguments);
+ }
+
+ _bindings.addBinding(routingKey, arguments, exchange);
+ }
+
+ public void unBind(AMQShortString routingKey, FieldTable arguments, Exchange exchange) throws AMQException
+ {
+ exchange.deregisterQueue(routingKey, this, arguments);
+ if (isDurable() && exchange.isDurable())
+ {
+ _virtualHost.getMessageStore().unbindQueue(exchange, routingKey, this, arguments);
+ }
+
+ _bindings.remove(routingKey, arguments, exchange);
+ }
+
+ public void registerProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag, boolean acks,
+ FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException
+ {
+ if (incrementSubscriberCount() > 1)
+ {
+ if (isExclusive())
+ {
+ decrementSubscriberCount();
+ throw new ExistingExclusiveSubscription();
+ }
+ else if (exclusive)
+ {
+ decrementSubscriberCount();
+ throw new ExistingSubscriptionPreventsExclusive();
+ }
+
+ }
+ else if (exclusive)
+ {
+ setExclusive(true);
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(MessageFormat.format("Registering protocol session {0} with channel {1} and "
+ + "consumer tag {2} with {3}", ps, channel, consumerTag, this));
+ }
+
+ Subscription subscription =
+ _subscriptionFactory.createSubscription(channel, ps, consumerTag, acks, filters, noLocal, this);
+
+ if (subscription.filtersMessages())
+ {
+ if (_deliveryMgr.hasQueuedMessages())
+ {
+ _deliveryMgr.populatePreDeliveryQueue(subscription);
+ }
+ }
+
+ _subscribers.addSubscriber(subscription);
+ }
+
+ private boolean isExclusive()
+ {
+ return _isExclusive.get();
+ }
+
+ private void setExclusive(boolean exclusive)
+ {
+ _isExclusive.set(exclusive);
+ }
+
+ private int incrementSubscriberCount()
+ {
+ return _subscriberCount.incrementAndGet();
+ }
+
+ private int decrementSubscriberCount()
+ {
+ return _subscriberCount.decrementAndGet();
+ }
+
+ public void unregisterProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag) throws AMQException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(MessageFormat.format(
+ "Unregistering protocol session {0} with channel {1} and consumer tag {2} from {3}",
+ ps, channel, consumerTag, this));
+ }
+
+ Subscription removedSubscription;
+ if ((removedSubscription = _subscribers.removeSubscriber(_subscriptionFactory.createSubscription(channel, ps,
+ consumerTag)))
+ == null)
+ {
+ throw new AMQException("Protocol session with channel " + channel + " and consumer tag " + consumerTag
+ + " and protocol session key " + ps.getKey() + " not registered with queue " + this);
+ }
+
+ removedSubscription.close();
+ setExclusive(false);
+ decrementSubscriberCount();
+
+ // if we are eligible for auto deletion, unregister from the queue registry
+ if (_autoDelete && _subscribers.isEmpty())
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Auto-deleteing queue:" + this);
+ }
+
+ autodelete();
+ // we need to manually fire the event to the removed subscription (which was the last one left for this
+ // queue. This is because the delete method uses the subscription set which has just been cleared
+ removedSubscription.queueDeleted(this);
+ }
+ }
+
+ public boolean isUnused()
+ {
+ return _subscribers.isEmpty();
+ }
+
+ public boolean isEmpty()
+ {
+ return !_deliveryMgr.hasQueuedMessages();
+ }
+
+ public int delete(boolean checkUnused, boolean checkEmpty) throws AMQException
+ {
+ if (checkUnused && !_subscribers.isEmpty())
+ {
+ _logger.info("Will not delete " + this + " as it is in use.");
+
+ return 0;
+ }
+ else if (checkEmpty && _deliveryMgr.hasQueuedMessages())
+ {
+ _logger.info("Will not delete " + this + " as it is not empty.");
+
+ return 0;
+ }
+ else
+ {
+ delete();
+
+ return _deliveryMgr.getQueueMessageCount();
+ }
+ }
+
+ public void delete() throws AMQException
+ {
+ if (!_deleted.getAndSet(true))
+ {
+ _subscribers.queueDeleted(this);
+ _bindings.deregister();
+ _virtualHost.getQueueRegistry().unregisterQueue(_name);
+ _managedObject.unregister();
+ for (Task task : _deleteTaskList)
+ {
+ task.doTask(this);
+ }
+
+ _deleteTaskList.clear();
+ }
+ }
+
+ protected void autodelete() throws AMQException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(MessageFormat.format("autodeleting {0}", this));
+ }
+
+ delete();
+ }
+
+ /*public void processGet(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException
+ {
+ // fixme not sure what this is doing. should we be passing deliverFirst through here?
+ // This code is not used so when it is perhaps it should
+ _deliveryMgr.deliver(storeContext, getName(), msg, deliverFirst);
+ try
+ {
+ msg.checkDeliveredToConsumer();
+ updateReceivedMessageCount(msg);
+ }
+ catch (NoConsumersException e)
+ {
+ // as this message will be returned, it should be removed
+ // from the queue:
+ dequeue(storeContext, msg);
+ }
+ }*/
+
+ // public DeliveryManager getDeliveryManager()
+ // {
+ // return _deliveryMgr;
+ // }
+
+ public void process(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException
+ {
+ _deliveryMgr.deliver(storeContext, getName(), msg, deliverFirst);
+ try
+ {
+ msg.checkDeliveredToConsumer();
+ updateReceivedMessageCount(msg);
+ }
+ catch (NoConsumersException e)
+ {
+ // as this message will be returned, it should be removed
+ // from the queue:
+ dequeue(storeContext, msg);
+ }
+ }
+
+ public void dequeue(StoreContext storeContext, AMQMessage msg) throws FailedDequeueException
+ {
+ try
+ {
+ msg.dequeue(storeContext, this);
+ }
+ catch (MessageCleanupException e)
+ {
+ // Message was dequeued, but could not then be deleted
+ // though it is no longer referenced. This should be very
+ // rare and can be detected and cleaned up on recovery or
+ // done through some form of manual intervention.
+ _logger.error(e, e);
+ }
+ catch (AMQException e)
+ {
+ throw new FailedDequeueException(_name.toString(), e);
+ }
+ }
+
+ public void deliverAsync()
+ {
+ _deliveryMgr.processAsync(_asyncDelivery);
+ }
+
+ protected SubscriptionManager getSubscribers()
+ {
+ return _subscribers;
+ }
+
+ protected void updateReceivedMessageCount(AMQMessage msg) throws AMQException
+ {
+ if (!msg.isRedelivered())
+ {
+ _totalMessagesReceived.incrementAndGet();
+ }
+
+ try
+ {
+ _managedObject.checkForNotification(msg);
+ }
+ catch (JMException e)
+ {
+ throw new AMQException("Unable to get notification from manage queue: " + e, e);
+ }
+ }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ if ((o == null) || (getClass() != o.getClass()))
+ {
+ return false;
+ }
+
+ final AMQQueue amqQueue = (AMQQueue) o;
+
+ return (_name.equals(amqQueue._name));
+ }
+
+ public int hashCode()
+ {
+ return _name.hashCode();
+ }
+
+ public String toString()
+ {
+ return "Queue(" + _name + ")@" + System.identityHashCode(this);
+ }
+
+ public boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException
+ {
+ return _deliveryMgr.performGet(session, channel, acks);
+ }
+
+ public QueueRegistry getQueueRegistry()
+ {
+ return _virtualHost.getQueueRegistry();
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public static interface Task
+ {
+ public void doTask(AMQQueue queue) throws AMQException;
+ }
+
+ public void addQueueDeleteTask(Task task)
+ {
+ _deleteTaskList.add(task);
+ }
+
+ public long getMinimumAlertRepeatGap()
+ {
+ return _minimumAlertRepeatGap;
+ }
+
+ public void setMinimumAlertRepeatGap(long minimumAlertRepeatGap)
+ {
+ _minimumAlertRepeatGap = minimumAlertRepeatGap;
+ }
+
+ public long getMaximumMessageAge()
+ {
+ return _maximumMessageAge;
+ }
+
+ public void setMaximumMessageAge(long maximumMessageAge)
+ {
+ _maximumMessageAge = maximumMessageAge;
+ }
+
+ public void subscriberHasPendingResend(boolean hasContent, SubscriptionImpl subscription, AMQMessage msg)
+ {
+ _deliveryMgr.subscriberHasPendingResend(hasContent, subscription, msg);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
new file mode 100644
index 0000000000..07872d7644
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
@@ -0,0 +1,471 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.CommonContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.management.AMQManagedObject;
+import org.apache.qpid.server.management.MBeanConstructor;
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.store.StoreContext;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.Notification;
+import javax.management.OperationsException;
+import javax.management.monitor.MonitorNotification;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * AMQQueueMBean is the management bean for an {@link AMQQueue}.
+ *
+ * <p/><tablse id="crc"><caption>CRC Caption</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ */
+@MBeanDescription("Management Interface for AMQQueue")
+public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, QueueNotificationListener
+{
+ /** Used for debugging purposes. */
+ private static final Logger _logger = Logger.getLogger(AMQQueueMBean.class);
+
+ private static final SimpleDateFormat _dateFormat = new SimpleDateFormat("MM-dd-yy HH:mm:ss.SSS z");
+
+ /**
+ * Since the MBean is not associated with a real channel we can safely create our own store context
+ * for use in the few methods that require one.
+ */
+ private StoreContext _storeContext = new StoreContext();
+
+ private AMQQueue _queue = null;
+ private String _queueName = null;
+ // OpenMBean data types for viewMessages method
+ private static final String[] _msgAttributeNames = { "AMQ MessageId", "Header", "Size(bytes)", "Redelivered" };
+ private static String[] _msgAttributeIndex = { _msgAttributeNames[0] };
+ private static OpenType[] _msgAttributeTypes = new OpenType[4]; // AMQ message attribute types.
+ private static CompositeType _messageDataType = null; // Composite type for representing AMQ Message data.
+ private static TabularType _messagelistDataType = null; // Datatype for representing AMQ messages list.
+
+ // OpenMBean data types for viewMessageContent method
+ private static CompositeType _msgContentType = null;
+ private static final String[] _msgContentAttributes = { "AMQ MessageId", "MimeType", "Encoding", "Content" };
+ private static OpenType[] _msgContentAttributeTypes = new OpenType[4];
+
+ 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
+ {
+ super(ManagedQueue.class, ManagedQueue.TYPE);
+ _queue = queue;
+ _queueName = jmxEncode(new StringBuffer(queue.getName()), 0).toString();
+ }
+
+ public ManagedObject getParentObject()
+ {
+ return _queue.getVirtualHost().getManagedObject();
+ }
+
+ static
+ {
+ try
+ {
+ init();
+ }
+ catch (JMException ex)
+ {
+ // This is not expected to ever occur.
+ throw new RuntimeException("Got JMException in static initializer.", ex);
+ }
+ }
+
+ /**
+ * initialises the openmbean data types
+ */
+ private static void init() throws OpenDataException
+ {
+ _msgContentAttributeTypes[0] = SimpleType.LONG; // For message id
+ _msgContentAttributeTypes[1] = SimpleType.STRING; // For MimeType
+ _msgContentAttributeTypes[2] = SimpleType.STRING; // For Encoding
+ _msgContentAttributeTypes[3] = new ArrayType(1, SimpleType.BYTE); // For message content
+ _msgContentType =
+ new CompositeType("Message Content", "AMQ Message Content", _msgContentAttributes, _msgContentAttributes,
+ _msgContentAttributeTypes);
+
+ _msgAttributeTypes[0] = SimpleType.LONG; // For message id
+ _msgAttributeTypes[1] = new ArrayType(1, SimpleType.STRING); // For header attributes
+ _msgAttributeTypes[2] = SimpleType.LONG; // For size
+ _msgAttributeTypes[3] = SimpleType.BOOLEAN; // For redelivered
+
+ _messageDataType =
+ new CompositeType("Message", "AMQ Message", _msgAttributeNames, _msgAttributeNames, _msgAttributeTypes);
+ _messagelistDataType = new TabularType("Messages", "List of messages", _messageDataType, _msgAttributeIndex);
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _queueName;
+ }
+
+ public String getName()
+ {
+ return _queueName;
+ }
+
+ public boolean isDurable()
+ {
+ return _queue.isDurable();
+ }
+
+ public String getOwner()
+ {
+ return String.valueOf(_queue.getOwner());
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _queue.isAutoDelete();
+ }
+
+ public Integer getMessageCount()
+ {
+ return _queue.getMessageCount();
+ }
+
+ public Long getMaximumMessageSize()
+ {
+ return _queue.getMaximumMessageSize();
+ }
+
+ public Long getMaximumMessageAge()
+ {
+ return _queue.getMaximumMessageAge();
+ }
+
+ public void setMaximumMessageAge(Long maximumMessageAge)
+ {
+ _queue.setMaximumMessageAge(maximumMessageAge);
+ }
+
+ public void setMaximumMessageSize(Long value)
+ {
+ _queue.setMaximumMessageSize(value);
+ }
+
+ public Integer getConsumerCount()
+ {
+ return _queue.getConsumerCount();
+ }
+
+ public Integer getActiveConsumerCount()
+ {
+ return _queue.getActiveConsumerCount();
+ }
+
+ public Long getReceivedMessageCount()
+ {
+ return _queue.getReceivedMessageCount();
+ }
+
+ public Long getMaximumMessageCount()
+ {
+ return _queue.getMaximumMessageCount();
+ }
+
+ public void setMaximumMessageCount(Long value)
+ {
+ _queue.setMaximumMessageCount(value);
+ }
+
+ public Long getMaximumQueueDepth()
+ {
+ long queueDepthInBytes = _queue.getMaximumQueueDepth();
+
+ return queueDepthInBytes >> 10;
+ }
+
+ public void setMaximumQueueDepth(Long value)
+ {
+ _queue.setMaximumQueueDepth(value);
+ }
+
+ /**
+ * returns the size of messages(KB) in the queue.
+ */
+ public Long getQueueDepth() throws JMException
+ {
+ long queueBytesSize = _queue.getQueueDepth();
+
+ return queueBytesSize >> 10;
+ }
+
+ /**
+ * Checks if there is any notification to be send to the listeners
+ */
+ public void checkForNotification(AMQMessage msg) throws AMQException, JMException
+ {
+
+ final long currentTime = System.currentTimeMillis();
+ final long thresholdTime = currentTime - _queue.getMinimumAlertRepeatGap();
+
+ for (NotificationCheck check : NotificationCheck.values())
+ {
+ if (check.isMessageSpecific() || (_lastNotificationTimes[check.ordinal()] < thresholdTime))
+ {
+ if (check.notifyIfNecessary(msg, _queue, this))
+ {
+ _lastNotificationTimes[check.ordinal()] = currentTime;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Sends the notification to the listeners
+ */
+ public void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg)
+ {
+ // important : add log to the log file - monitoring tools may be looking for this
+ _logger.info(notification.name() + " On Queue " + queue.getName() + " - " + notificationMsg);
+ notificationMsg = notification.name() + " " + notificationMsg;
+
+ _lastNotification =
+ new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber,
+ System.currentTimeMillis(), notificationMsg);
+
+ _broadcaster.sendNotification(_lastNotification);
+ }
+
+ public Notification getLastNotification()
+ {
+ return _lastNotification;
+ }
+
+ /**
+ * @see org.apache.qpid.server.queue.AMQQueue#deleteMessageFromTop
+ */
+ public void deleteMessageFromTop() throws JMException
+ {
+ try
+ {
+ _queue.deleteMessageFromTop(_storeContext);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ /**
+ * @see org.apache.qpid.server.queue.AMQQueue#clearQueue
+ */
+ public void clearQueue() throws JMException
+ {
+ try
+ {
+ _queue.clearQueue(_storeContext);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ /**
+ * returns message content as byte array and related attributes for the given message id.
+ */
+ public CompositeData viewMessageContent(long msgId) throws JMException
+ {
+ AMQMessage msg = _queue.getMessageOnTheQueue(msgId);
+ if (msg == null)
+ {
+ throw new OperationsException("AMQMessage with message id = " + msgId + " is not in the " + _queueName);
+ }
+ // get message content
+ Iterator<ContentChunk> cBodies = msg.getContentBodyIterator();
+ List<Byte> msgContent = new ArrayList<Byte>();
+ while (cBodies.hasNext())
+ {
+ ContentChunk body = cBodies.next();
+ if (body.getSize() != 0)
+ {
+ if (body.getSize() != 0)
+ {
+ ByteBuffer slice = body.getData().slice();
+ for (int j = 0; j < slice.limit(); j++)
+ {
+ msgContent.add(slice.get());
+ }
+ }
+ }
+ }
+
+ try
+ {
+ // Create header attributes list
+ CommonContentHeaderProperties headerProperties =
+ (CommonContentHeaderProperties) msg.getContentHeaderBody().properties;
+ String mimeType = null, encoding = null;
+ if (headerProperties != null)
+ {
+ AMQShortString mimeTypeShortSting = headerProperties.getContentType();
+ mimeType = (mimeTypeShortSting == null) ? null : mimeTypeShortSting.toString();
+ encoding = (headerProperties.getEncoding() == null) ? "" : headerProperties.getEncoding().toString();
+ }
+
+ Object[] itemValues = { msgId, mimeType, encoding, msgContent.toArray(new Byte[0]) };
+
+ return new CompositeDataSupport(_msgContentType, _msgContentAttributes, itemValues);
+ }
+ catch (AMQException e)
+ {
+ JMException jme = new JMException("Error creating header attributes list: " + e);
+ jme.initCause(e);
+ throw jme;
+ }
+ }
+
+ /**
+ * Returns the header contents of the messages stored in this queue in tabular form.
+ */
+ public TabularData viewMessages(int beginIndex, int endIndex) throws JMException
+ {
+ if ((beginIndex > endIndex) || (beginIndex < 1))
+ {
+ throw new OperationsException("From Index = " + beginIndex + ", To Index = " + endIndex
+ + "\n\"From Index\" should be greater than 0 and less than \"To Index\"");
+ }
+
+ List<AMQMessage> list = _queue.getMessagesOnTheQueue();
+ TabularDataSupport _messageList = new TabularDataSupport(_messagelistDataType);
+
+ try
+ {
+ // Create the tabular list of message header contents
+ for (int i = beginIndex; (i <= endIndex) && (i <= list.size()); i++)
+ {
+ AMQMessage msg = list.get(i - 1);
+ ContentHeaderBody headerBody = msg.getContentHeaderBody();
+ // Create header attributes list
+ String[] headerAttributes = getMessageHeaderProperties(headerBody);
+ Object[] itemValues = { msg.getMessageId(), headerAttributes, headerBody.bodySize, msg.isRedelivered() };
+ CompositeData messageData = new CompositeDataSupport(_messageDataType, _msgAttributeNames, itemValues);
+ _messageList.put(messageData);
+ }
+ }
+ catch (AMQException e)
+ {
+ JMException jme = new JMException("Error creating message contents: " + e);
+ jme.initCause(e);
+ throw jme;
+ }
+
+ return _messageList;
+ }
+
+ private String[] getMessageHeaderProperties(ContentHeaderBody headerBody)
+ {
+ List<String> list = new ArrayList<String>();
+ BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties) headerBody.properties;
+ list.add("reply-to = " + headerProperties.getReplyToAsString());
+ list.add("propertyFlags = " + headerProperties.getPropertyFlags());
+ list.add("ApplicationID = " + headerProperties.getAppIdAsString());
+ list.add("ClusterID = " + headerProperties.getClusterIdAsString());
+ list.add("UserId = " + headerProperties.getUserIdAsString());
+ list.add("JMSMessageID = " + headerProperties.getMessageIdAsString());
+ list.add("JMSCorrelationID = " + headerProperties.getCorrelationIdAsString());
+
+ int delMode = headerProperties.getDeliveryMode();
+ list.add("JMSDeliveryMode = " + ((delMode == 1) ? "Persistent" : "Non_Persistent"));
+
+ list.add("JMSPriority = " + headerProperties.getPriority());
+ list.add("JMSType = " + headerProperties.getType());
+
+ long longDate = headerProperties.getExpiration();
+ String strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null;
+ list.add("JMSExpiration = " + strDate);
+
+ longDate = headerProperties.getTimestamp();
+ strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null;
+ list.add("JMSTimestamp = " + strDate);
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * @see ManagedQueue#moveMessages
+ * @param fromMessageId
+ * @param toMessageId
+ * @param toQueueName
+ * @throws JMException
+ */
+ public void moveMessages(long fromMessageId, long toMessageId, String toQueueName) throws JMException
+ {
+ if ((fromMessageId > toMessageId) || (fromMessageId < 1))
+ {
+ throw new OperationsException("\"From MessageId\" should be greater then 0 and less then \"To MessageId\"");
+ }
+
+ _queue.moveMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName, _storeContext);
+ }
+
+ /**
+ * returns Notifications sent by this MBean.
+ */
+ @Override
+ public MBeanNotificationInfo[] getNotificationInfo()
+ {
+ String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED };
+ String name = MonitorNotification.class.getName();
+ String description = "Either Message count or Queue depth or Message size has reached threshold high value";
+ MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description);
+
+ return new MBeanNotificationInfo[] { info1 };
+ }
+
+} // End of AMQQueueMBean class
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java
new file mode 100644
index 0000000000..290fedcf7b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+public class AsyncDeliveryConfig
+{
+ private Executor _executor;
+
+ @Configured(path = "delivery.poolsize", defaultValue = "0")
+ public int poolSize;
+
+ public Executor getExecutor()
+ {
+ if (_executor == null)
+ {
+ if (poolSize > 0)
+ {
+ _executor = Executors.newFixedThreadPool(poolSize);
+ }
+ else
+ {
+ _executor = Executors.newCachedThreadPool();
+ }
+ }
+ return _executor;
+ }
+
+ public static Executor getAsyncDeliveryExecutor()
+ {
+ return ApplicationRegistry.getInstance().getConfiguredObject(AsyncDeliveryConfig.class).getExecutor();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java
new file mode 100644
index 0000000000..eabc8ebf38
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java
@@ -0,0 +1,1049 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.configuration.Configurator;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize;
+import org.apache.qpid.util.MessageQueue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReentrantLock;
+
+
+/** Manages delivery of messages on behalf of a queue */
+public class ConcurrentSelectorDeliveryManager implements DeliveryManager
+{
+ private static final Logger _log = Logger.getLogger(ConcurrentSelectorDeliveryManager.class);
+
+ @Configured(path = "advanced.compressBufferOnQueue",
+ defaultValue = "false")
+ public boolean compressBufferOnQueue;
+ /** Holds any queued messages */
+ private final MessageQueue<AMQMessage> _messages = new ConcurrentLinkedMessageQueueAtomicSize<AMQMessage>();
+
+ /** Ensures that only one asynchronous task is running for this manager at any time. */
+ private final AtomicBoolean _processing = new AtomicBoolean();
+ /** The subscriptions on the queue to whom messages are delivered */
+ private final SubscriptionManager _subscriptions;
+
+ /**
+ * A reference to the queue we are delivering messages for. We need this to be able to pass the code that handles
+ * acknowledgements a handle on the queue.
+ */
+ private final AMQQueue _queue;
+
+ /**
+ * Flag used while moving messages from this queue to another. For moving messages the async delivery should also
+ * stop. This flat should be set to true to stop async delivery and set to false to enable async delivery again.
+ */
+ private AtomicBoolean _movingMessages = new AtomicBoolean();
+
+ /**
+ * Lock used to ensure that an channel that becomes unsuspended during the start of the queueing process is forced
+ * to wait till the first message is added to the queue. This will ensure that the _queue has messages to be
+ * delivered via the async thread. <p/> Lock is used to control access to hasQueuedMessages() and over the addition
+ * of messages to the queue.
+ */
+ private ReentrantLock _lock = new ReentrantLock();
+ private AtomicLong _totalMessageSize = new AtomicLong();
+ private AtomicInteger _extraMessages = new AtomicInteger();
+ private Set<Subscription> _hasContent = Collections.synchronizedSet(new HashSet<Subscription>());
+ private final Object _queueHeadLock = new Object();
+ private String _processingThreadName = "";
+
+
+ /** Used by any reaping thread to purge messages */
+ private StoreContext _reapingStoreContext = new StoreContext();
+
+ ConcurrentSelectorDeliveryManager(SubscriptionManager subscriptions, AMQQueue queue)
+ {
+
+ //Set values from configuration
+ Configurator.configure(this);
+
+ if (compressBufferOnQueue)
+ {
+ _log.warn("Compressing Buffers on queue.");
+ }
+
+ _subscriptions = subscriptions;
+ _queue = queue;
+ }
+
+
+ private boolean addMessageToQueue(AMQMessage msg, boolean deliverFirst)
+ {
+ // Shrink the ContentBodies to their actual size to save memory.
+ if (compressBufferOnQueue)
+ {
+ Iterator<ContentChunk> it = msg.getContentBodyIterator();
+ while (it.hasNext())
+ {
+ ContentChunk cb = it.next();
+ cb.reduceToFit();
+ }
+ }
+
+ if (deliverFirst)
+ {
+ synchronized (_queueHeadLock)
+ {
+ _messages.pushHead(msg);
+ }
+ }
+ else
+ {
+ _messages.offer(msg);
+ }
+
+ _totalMessageSize.addAndGet(msg.getSize());
+
+ return true;
+ }
+
+
+ public boolean hasQueuedMessages()
+ {
+ _lock.lock();
+ try
+ {
+ return !(_messages.isEmpty() && _hasContent.isEmpty());
+ }
+ finally
+ {
+ _lock.unlock();
+ }
+ }
+
+ public int getQueueMessageCount()
+ {
+ return getMessageCount();
+ }
+
+ /**
+ * This is an EXPENSIVE opperation to perform with a ConcurrentLinkedQueue as it must run the queue to determine
+ * size. The ConcurrentLinkedQueueAtomicSize uses an AtomicInteger to record the number of elements on the queue.
+ *
+ * @return int the number of messages in the delivery queue.
+ */
+ private int getMessageCount()
+ {
+ return _messages.size() + _extraMessages.get();
+ }
+
+
+ public long getTotalMessageSize()
+ {
+ return _totalMessageSize.get();
+ }
+
+ public long getOldestMessageArrival()
+ {
+ AMQMessage msg = _messages.peek();
+ return msg == null ? Long.MAX_VALUE : msg.getArrivalTime();
+ }
+
+ public void subscriberHasPendingResend(boolean hasContent, Subscription subscription, AMQMessage msg)
+ {
+ _lock.lock();
+ try
+ {
+ if (hasContent)
+ {
+ _log.debug("Queue has adding subscriber content");
+ _hasContent.add(subscription);
+ _totalMessageSize.addAndGet(msg.getSize());
+ _extraMessages.addAndGet(1);
+ }
+ else
+ {
+ _log.debug("Queue has removing subscriber content");
+ if (msg == null)
+ {
+ _hasContent.remove(subscription);
+ }
+ else
+ {
+ _totalMessageSize.addAndGet(-msg.getSize());
+ _extraMessages.addAndGet(-1);
+ }
+ }
+ }
+ finally
+ {
+ _lock.unlock();
+ }
+ }
+
+ /** @return the state of the async processor. */
+ public boolean isProcessingAsync()
+ {
+ return _processing.get();
+ }
+
+ /**
+ * Returns all the messages in the Queue
+ *
+ * @return List of messages
+ */
+ public List<AMQMessage> getMessages()
+ {
+ _lock.lock();
+ List<AMQMessage> list = new ArrayList<AMQMessage>();
+
+ for (AMQMessage message : _messages)
+ {
+ list.add(message);
+ }
+ _lock.unlock();
+
+ return list;
+ }
+
+ /**
+ * Returns messages within the range of given messageIds
+ *
+ * @param fromMessageId
+ * @param toMessageId
+ *
+ * @return
+ */
+ public List<AMQMessage> getMessages(long fromMessageId, long toMessageId)
+ {
+ if (fromMessageId <= 0 || toMessageId <= 0)
+ {
+ return null;
+ }
+
+ long maxMessageCount = toMessageId - fromMessageId + 1;
+
+ _lock.lock();
+
+ List<AMQMessage> foundMessagesList = new ArrayList<AMQMessage>();
+
+ for (AMQMessage message : _messages)
+ {
+ long msgId = message.getMessageId();
+ if (msgId >= fromMessageId && msgId <= toMessageId)
+ {
+ foundMessagesList.add(message);
+ }
+ // break if the no of messages are found
+ if (foundMessagesList.size() == maxMessageCount)
+ {
+ break;
+ }
+ }
+ _lock.unlock();
+
+ return foundMessagesList;
+ }
+
+ public void populatePreDeliveryQueue(Subscription subscription)
+ {
+ if (_log.isTraceEnabled())
+ {
+ _log.trace("Populating PreDeliveryQueue for Subscription(" + System.identityHashCode(subscription) + ")");
+ }
+
+ Iterator<AMQMessage> currentQueue = _messages.iterator();
+
+ while (currentQueue.hasNext())
+ {
+ AMQMessage message = currentQueue.next();
+ if (!message.getDeliveredToConsumer())
+ {
+ if (subscription.hasInterest(message))
+ {
+ subscription.enqueueForPreDelivery(message, false);
+ }
+ }
+ }
+ }
+
+ public boolean performGet(AMQProtocolSession protocolSession, AMQChannel channel, boolean acks) throws AMQException
+ {
+ AMQMessage msg = getNextMessage();
+ if (msg == null)
+ {
+ return false;
+ }
+ else
+ {
+
+ try
+ {
+ // if we do not need to wait for client acknowledgements
+ // we can decrement the reference count immediately.
+
+ // By doing this _before_ the send we ensure that it
+ // doesn't get sent if it can't be dequeued, preventing
+ // duplicate delivery on recovery.
+
+ // The send may of course still fail, in which case, as
+ // the message is unacked, it will be lost.
+ if (!acks)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("No ack mode so dequeuing message immediately: " + msg.getMessageId());
+ }
+ _queue.dequeue(channel.getStoreContext(), msg);
+ }
+ synchronized (channel)
+ {
+ long deliveryTag = channel.getNextDeliveryTag();
+
+ if (acks)
+ {
+ channel.addUnacknowledgedMessage(msg, deliveryTag, null, _queue);
+ }
+
+ protocolSession.getProtocolOutputConverter().writeGetOk(msg, channel.getChannelId(),
+ deliveryTag, _queue.getMessageCount());
+ _totalMessageSize.addAndGet(-msg.getSize());
+ }
+
+ if (!acks)
+ {
+ msg.decrementReference(channel.getStoreContext());
+ }
+ }
+ finally
+ {
+ msg.setDeliveredToConsumer();
+ }
+ return true;
+
+ }
+ }
+
+ /**
+ * For feature of moving messages, this method is used. It sets the lock and sets the movingMessages flag, so that
+ * the asyn delivery is also stopped.
+ */
+ public void startMovingMessages()
+ {
+ _movingMessages.set(true);
+ }
+
+ /**
+ * Once moving messages to another queue is done or aborted, remove lock and unset the movingMessages flag, so that
+ * the async delivery can start again.
+ */
+ public void stopMovingMessages()
+ {
+ _movingMessages.set(false);
+ if (_lock.isHeldByCurrentThread())
+ {
+ _lock.unlock();
+ }
+ }
+
+ /**
+ * Messages will be removed from this queue and all preDeliveryQueues
+ *
+ * @param messageList
+ */
+ public void removeMovedMessages(List<AMQMessage> messageList)
+ {
+ // Remove from the
+ boolean hasSubscribers = _subscriptions.hasActiveSubscribers();
+ if (hasSubscribers)
+ {
+ for (Subscription sub : _subscriptions.getSubscriptions())
+ {
+ if (!sub.isSuspended() && sub.filtersMessages())
+ {
+ Queue<AMQMessage> preDeliveryQueue = sub.getPreDeliveryQueue();
+ for (AMQMessage msg : messageList)
+ {
+ preDeliveryQueue.remove(msg);
+ }
+ }
+ }
+ }
+
+ for (AMQMessage msg : messageList)
+ {
+ if (_messages.remove(msg))
+ {
+ _totalMessageSize.getAndAdd(-msg.getSize());
+ }
+ }
+ }
+
+ /**
+ * Now with implementation of predelivery queues, this method will mark the message on the top as taken.
+ *
+ * @param storeContext
+ *
+ * @throws AMQException
+ */
+ public void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException
+ {
+ _lock.lock();
+
+ AMQMessage message = _messages.poll();
+
+ if (message != null)
+ {
+ queue.dequeue(storeContext, message);
+
+ _totalMessageSize.addAndGet(-message.getSize());
+
+ //If this causes ref count to hit zero then data will be purged so message.getSize() will NPE.
+ message.decrementReference(storeContext);
+
+ }
+
+ _lock.unlock();
+ }
+
+ public long clearAllMessages(StoreContext storeContext) throws AMQException
+ {
+ long count = 0;
+ _lock.lock();
+
+ synchronized (_queueHeadLock)
+ {
+ AMQMessage msg = getNextMessage();
+ while (msg != null)
+ {
+ //and remove it
+ _messages.poll();
+
+ _queue.dequeue(storeContext, msg);
+
+ msg.decrementReference(_reapingStoreContext);
+
+ msg = getNextMessage();
+ count++;
+ }
+ _totalMessageSize.set(0L);
+ }
+ _lock.unlock();
+ return count;
+ }
+
+ /**
+ * This can only be used to clear the _messages queue. Any subscriber resend queue will not be purged.
+ *
+ * @return the next message or null
+ *
+ * @throws org.apache.qpid.AMQException
+ */
+ private AMQMessage getNextMessage() throws AMQException
+ {
+ return getNextMessage(_messages, null, false);
+ }
+
+ private AMQMessage getNextMessage(Queue<AMQMessage> messages, Subscription sub, boolean purgeOnly) throws AMQException
+ {
+ AMQMessage message = messages.peek();
+
+ //while (we have a message) && ((The subscriber is not a browser or message is taken ) or we are clearing) && (Check message is taken.)
+ while (purgeMessage(message, sub, purgeOnly))
+ {
+ // 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
+ AMQMessage removed = messages.poll();
+
+ assert removed == message;
+
+ // if the message expired then the _totalMessageSize needs adjusting
+ if (message.expired(_queue) && !message.getDeliveredToConsumer())
+ {
+ _totalMessageSize.addAndGet(-message.getSize());
+
+ // Use the reapingStoreContext as any sub(if we have one) may be in a tx.
+ _queue.dequeue(_reapingStoreContext, message);
+
+ message.decrementReference(_reapingStoreContext);
+
+ if (_log.isInfoEnabled())
+ {
+ _log.info(debugIdentity() + " Doing clean up of the main _message queue.");
+ }
+ }
+
+ //else the clean up is not required as the message has already been taken for this queue therefore
+ // it was the responsibility of the code that took the message to ensure the _totalMessageSize was updated.
+
+ if (_log.isTraceEnabled())
+ {
+ _log.trace("Removed taken message:" + message.debugIdentity());
+ }
+
+ // try the next message
+ message = messages.peek();
+ }
+
+ return message;
+ }
+
+ /**
+ * This method will return true if the message is to be purged from the queue.
+ *
+ *
+ * SIDE-EFFECT: The message will be taken by the Subscription(sub) for the current Queue(_queue)
+ *
+ * @param message
+ * @param sub
+ *
+ * @return
+ *
+ * @throws AMQException
+ */
+ private boolean purgeMessage(AMQMessage message, Subscription sub) throws AMQException
+ {
+ return purgeMessage(message, sub, false);
+ }
+
+ /**
+ * This method will return true if the message is to be purged from the queue.
+ * \
+ * SIDE-EFFECT: The msg will be taken by the Subscription(sub) for the current Queue(_queue) when purgeOnly is false
+ *
+ * @param message
+ * @param sub
+ * @param purgeOnly When set to false the message will be taken by the given Subscription.
+ *
+ * @return if the msg should be purged
+ *
+ * @throws AMQException
+ */
+ private boolean purgeMessage(AMQMessage message, Subscription sub, boolean purgeOnly) throws AMQException
+ {
+ //Original.. complicated while loop control
+// (message != null
+// && (
+// ((sub != null && !sub.isBrowser()) || message.isTaken(_queue))
+// || sub == null)
+// && message.taken(_queue, sub));
+
+ boolean purge = false;
+
+ // if the message is null then don't purge as we have no messagse.
+ if (message != null)
+ {
+ // Check that the message hasn't expired.
+ if (message.expired(_queue))
+ {
+ return true;
+ }
+
+ // if we have a subscriber perform message checks
+ if (sub != null)
+ {
+ // if we have a queue browser(we don't purge) so check mark the message as taken
+ purge = ((!sub.isBrowser() || message.isTaken(_queue)));
+ }
+ else
+ {
+ // if there is no subscription we are doing
+ // a get or purging so mark message as taken.
+ message.isTaken(_queue);
+ // and then ensure that it gets purged
+ purge = true;
+ }
+ }
+
+ if (purgeOnly)
+ {
+ // If we are simply purging the queue don't take the message
+ // just purge up to the next non-taken msg.
+ return purge && message.isTaken(_queue);
+ }
+ else
+ {
+ // if we are purging then ensure we mark this message taken for the current subscriber
+ // the current subscriber may be null in the case of a get or a purge but this is ok.
+ return purge && message.taken(_queue, sub);
+ }
+ }
+
+ public void sendNextMessage(Subscription sub, AMQQueue queue)//Queue<AMQMessage> messageQueue)
+ {
+
+ Queue<AMQMessage> messageQueue = sub.getNextQueue(_messages);
+
+ if (_log.isTraceEnabled())
+ {
+ _log.trace(debugIdentity() + "Async sendNextMessage for sub (" + System.identityHashCode(sub) +
+ ") from queue (" + System.identityHashCode(messageQueue) +
+ ") AMQQueue (" + System.identityHashCode(queue) + ")");
+ }
+
+ if (messageQueue == null)
+ {
+ // There is no queue with messages currently. This is ok... just means the queue has no msgs matching selector
+ if (_log.isInfoEnabled())
+ {
+ _log.info(debugIdentity() + sub + ": asked to send messages but has none on given queue:" + queue);
+ }
+ return;
+ }
+
+ AMQMessage message = null;
+ AMQMessage removed = null;
+ try
+ {
+ synchronized (_queueHeadLock)
+ {
+ message = getNextMessage(messageQueue, sub, false);
+
+ // message will be null if we have no messages in the messageQueue.
+ if (message == null)
+ {
+ if (_log.isTraceEnabled())
+ {
+ _log.trace(debugIdentity() + "No messages for Subscriber(" + System.identityHashCode(sub) + ") from queue; (" + System.identityHashCode(messageQueue) + ")");
+ }
+ return;
+ }
+ if (_log.isDebugEnabled())
+ {
+ _log.debug(debugIdentity() + "Async Delivery Message :" + message + "(" + System.identityHashCode(message) +
+ ") by :" + System.identityHashCode(this) +
+ ") to :" + System.identityHashCode(sub));
+ }
+
+
+ if (messageQueue == _messages)
+ {
+ _totalMessageSize.addAndGet(-message.getSize());
+ }
+
+ sub.send(message, _queue);
+
+ //remove sent message from our queue.
+ removed = messageQueue.poll();
+ //If we don't remove the message from _messages
+ // Otherwise the Async send will never end
+ }
+
+ if (removed != message)
+ {
+ _log.error("Just send message:" + message.debugIdentity() + " BUT removed this from queue:" + removed);
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug(debugIdentity() + "Async Delivered Message r:" + removed.debugIdentity() + "d:" + message +
+ ") by :" + System.identityHashCode(this) +
+ ") to :" + System.identityHashCode(sub));
+ }
+
+
+ if (messageQueue == sub.getResendQueue())
+ {
+ if (_log.isTraceEnabled())
+ {
+ _log.trace(debugIdentity() + "All messages sent from resendQueue for " + sub);
+ }
+ if (messageQueue.isEmpty())
+ {
+ subscriberHasPendingResend(false, sub, null);
+ //better to use the above method as this keeps all the tracking in one location.
+ // _hasContent.remove(sub);
+ }
+
+ _extraMessages.decrementAndGet();
+ }
+ else if (messageQueue == sub.getPreDeliveryQueue() && !sub.isBrowser())
+ {
+ if (_log.isInfoEnabled())
+ {
+ //fixme - we should do the clean up as the message remains on the _message queue
+ // this is resulting in the next consumer receiving the message and then attempting to purge it
+ //
+ cleanMainQueue(sub);
+ }
+ }
+
+ }
+ catch (AMQException e)
+ {
+ if (message != null)
+ {
+ message.release(_queue);
+ }
+ else
+ {
+ _log.error(debugIdentity() + "Unable to release message as it is null. " + e, e);
+ }
+ _log.error(debugIdentity() + "Unable to deliver message as dequeue failed: " + e, e);
+ }
+ }
+
+ private void cleanMainQueue(Subscription sub)
+ {
+ try
+ {
+ getNextMessage(_messages, sub, true);
+ }
+ catch (AMQException e)
+ {
+ _log.warn("Problem during main queue purge:" + e.getMessage());
+ }
+ }
+
+ /**
+ * enqueues the messages in the list on the queue and all required predelivery queues
+ *
+ * @param storeContext
+ * @param movedMessageList
+ */
+ public void enqueueMovedMessages(StoreContext storeContext, List<AMQMessage> movedMessageList)
+ {
+ _lock.lock();
+ for (AMQMessage msg : movedMessageList)
+ {
+ addMessageToQueue(msg, false);
+ }
+
+ // enqueue on the pre delivery queues
+ for (Subscription sub : _subscriptions.getSubscriptions())
+ {
+ for (AMQMessage msg : movedMessageList)
+ {
+ // Only give the message to those that want them.
+ if (sub.hasInterest(msg))
+ {
+ sub.enqueueForPreDelivery(msg, true);
+ }
+ }
+ }
+ _lock.unlock();
+ }
+
+ /**
+ * Only one thread should ever execute this method concurrently, but it can do so while other threads invoke
+ * deliver().
+ */
+ private void processQueue()
+ {
+ //record thread name
+ if (_log.isDebugEnabled())
+ {
+ _processingThreadName = Thread.currentThread().getName();
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug(debugIdentity() + "Running process Queue." + currentStatus());
+ }
+
+ // Continue to process delivery while we haveSubscribers and messages
+ boolean hasSubscribers = _subscriptions.hasActiveSubscribers();
+
+ while (hasSubscribers && hasQueuedMessages() && !_movingMessages.get())
+ {
+ hasSubscribers = false;
+
+ for (Subscription sub : _subscriptions.getSubscriptions())
+ {
+ synchronized (sub.getSendLock())
+ {
+ if (!sub.isSuspended())
+ {
+ sendNextMessage(sub, _queue);
+
+ hasSubscribers = true;
+ }
+ }
+ }
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug(debugIdentity() + "Done process Queue." + currentStatus());
+ }
+
+ }
+
+ public void deliver(StoreContext context, AMQShortString name, AMQMessage msg, boolean deliverFirst) throws AMQException
+ {
+
+ final boolean debugEnabled = _log.isDebugEnabled();
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + "deliver :first(" + deliverFirst + ") :" + msg);
+ }
+
+ //Check if we have someone to deliver the message to.
+ _lock.lock();
+ try
+ {
+ Subscription s = _subscriptions.nextSubscriber(msg);
+
+ if (s == null || (!s.filtersMessages() && hasQueuedMessages())) //no-one can take the message right now or we're queueing
+ {
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + "Testing Message(" + msg + ") for Queued Delivery:" + currentStatus());
+ }
+ if (!msg.getMessagePublishInfo().isImmediate())
+ {
+ addMessageToQueue(msg, deliverFirst);
+
+ //release lock now message is on queue.
+ _lock.unlock();
+
+ //Pre Deliver to all subscriptions
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + "We have " + _subscriptions.getSubscriptions().size() +
+ " subscribers to give the message to:" + currentStatus());
+ }
+ for (Subscription sub : _subscriptions.getSubscriptions())
+ {
+
+ // stop if the message gets delivered whilst PreDelivering if we have a shared queue.
+ if (_queue.isShared() && msg.getDeliveredToConsumer())
+ {
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + "Stopping PreDelivery as message(" + System.identityHashCode(msg) +
+ ") is already delivered.");
+ }
+ continue;
+ }
+
+ // Only give the message to those that want them.
+ if (sub.hasInterest(msg))
+ {
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + "Queuing message(" + System.identityHashCode(msg) +
+ ") for PreDelivery for subscriber(" + System.identityHashCode(sub) + ")");
+ }
+ sub.enqueueForPreDelivery(msg, deliverFirst);
+ }
+ }
+
+ //if we have a non-filtering subscriber but queued messages && we're not Async && we have other Active subs then something is wrong!
+ if ((s != null && hasQueuedMessages()) && !isProcessingAsync() && _subscriptions.hasActiveSubscribers())
+ {
+ _queue.deliverAsync();
+ }
+
+ }
+ }
+ else
+ {
+
+ if (s.filtersMessages())
+ {
+ if (s.getPreDeliveryQueue().size() > 0)
+ {
+ _log.error("Direct delivery from PDQ with queued msgs:" + s.getPreDeliveryQueue().size());
+ }
+ }
+ else if (_messages.size() > 0)
+ {
+ _log.error("Direct delivery from MainQueue queued msgs:" + _messages.size());
+ }
+
+ //release lock now
+ _lock.unlock();
+ synchronized (s.getSendLock())
+ {
+ if (!s.isSuspended())
+ {
+ if (_log.isTraceEnabled())
+ {
+ _log.trace(debugIdentity() + "Delivering Message:" + msg.debugIdentity() + " to(" +
+ System.identityHashCode(s) + ") :" + s);
+ }
+
+ if (msg.taken(_queue, s))
+ {
+ //Message has been delivered so don't redeliver.
+ // This can currently occur because of the recursive call below
+ // During unit tests the send can occur
+ // client then rejects
+ // this reject then releases the message by the time the
+ // if(!msg.isTaken()) call is made below
+ // the message has been released so that thread loops to send the message again
+ // of course by the time it gets back to here. the thread that released the
+ // message is now ready to send it. Here is a sample trace for reference
+//1192627162613:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]
+//1192627162613:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}
+//1192627162613:Thread[pool-917-thread-4,5,main]:28398657 Sent :dt:214 msg:(HC:5529738 ID:145 Ref:1)
+//1192627162613:Thread[pool-917-thread-2,5,main]:Reject message by:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]
+//1192627162613:Thread[pool-917-thread-2,5,main]:Releasing Message:(HC:5529738 ID:145 Ref:1)
+//1192627162613:Thread[pool-917-thread-2,5,main]:Msg:Release:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:This:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]}
+//1192627162613:Thread[pool-917-thread-2,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]
+//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:suspended: Message((HC:5529738 ID:145 Ref:1)) has not been taken so recursing!: Subscriber:28398657
+//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]
+//1192627162629:Thread[pool-917-thread-2,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}
+//1192627162629:Thread[pool-917-thread-2,5,main]:25386607 Sent :dt:172 msg:(HC:5529738 ID:145 Ref:1)
+//1192627162629:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=true} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]}
+ // Note: In the last request to take the message from thread 4,5 the message has been
+ // taken by the previous call done by thread 2,5
+
+
+ return;
+ }
+ //Deliver the message
+ s.send(msg, _queue);
+ }
+ else
+ {
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + " Subscription(" + System.identityHashCode(s) + ") became " +
+ "suspended between nextSubscriber and send for message:" + msg.debugIdentity());
+ }
+ }
+ }
+
+ //
+ // Why do we do this? What was the reasoning? We should have a better approach
+ // than recursion and rejecting if someone else sends it before we do.
+ //
+ if (!msg.isTaken(_queue))
+ {
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + " Message(" + msg.debugIdentity() + ") has not been taken so recursing!:" +
+ " Subscriber:" + System.identityHashCode(s));
+ }
+
+ deliver(context, name, msg, deliverFirst);
+ }
+ else
+ {
+ if (debugEnabled)
+ {
+ _log.debug(debugIdentity() + " Message(" + msg.toString() +
+ ") has been taken so disregarding deliver request to Subscriber:" +
+ System.identityHashCode(s));
+ }
+ }
+ }
+
+ }
+ finally
+ {
+ //ensure lock is released
+ if (_lock.isHeldByCurrentThread())
+ {
+ _lock.unlock();
+ }
+ }
+ }
+
+ private final String id = "(" + String.valueOf(System.identityHashCode(this)) + ")";
+
+ private String debugIdentity()
+ {
+ return id;
+ }
+
+ final Runner _asyncDelivery = new Runner();
+
+ private class Runner implements Runnable
+ {
+ public void run()
+ {
+ String startName = Thread.currentThread().getName();
+ Thread.currentThread().setName("CSDM-AsyncDelivery:" + startName);
+ boolean running = true;
+ while (running && !_movingMessages.get())
+ {
+ processQueue();
+
+ //Check that messages have not been added since we did our last peek();
+ // Synchronize with the thread that adds to the queue.
+ // If the queue is still empty then we can exit
+ synchronized (_asyncDelivery)
+ {
+ if (!(hasQueuedMessages() && _subscriptions.hasActiveSubscribers()))
+ {
+ running = false;
+ _processing.set(false);
+ }
+ }
+ }
+ Thread.currentThread().setName(startName);
+ }
+ }
+
+ public void processAsync(Executor executor)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug(debugIdentity() + "Processing Async." + currentStatus());
+ }
+
+ synchronized (_asyncDelivery)
+ {
+ if (hasQueuedMessages() && _subscriptions.hasActiveSubscribers())
+ {
+ //are we already running? if so, don't re-run
+ if (_processing.compareAndSet(false, true))
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug(debugIdentity() + "Executing Async process.");
+ }
+ executor.execute(_asyncDelivery);
+ }
+ }
+ }
+ }
+
+ private String currentStatus()
+ {
+ return " Queued:" + (_messages.isEmpty() ? "Empty " : "Contains(H:M)") +
+ "(" + ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize() +
+ ":" + (_messages.size() - ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize()) + ") " +
+ " Extra: " + (_hasContent.isEmpty() ? "Empty " : "Contains") +
+ "(" + _hasContent.size() + ":" + _extraMessages.get() + ") " +
+ " Active:" + _subscriptions.hasActiveSubscribers() +
+ " Processing:" + (_processing.get() ? " true : Processing Thread: " + _processingThreadName : " false");
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java
new file mode 100644
index 0000000000..cbe9246f09
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class DefaultQueueRegistry implements QueueRegistry
+{
+ private ConcurrentMap<AMQShortString, AMQQueue> _queueMap = new ConcurrentHashMap<AMQShortString, AMQQueue>();
+
+ private final VirtualHost _virtualHost;
+
+ public DefaultQueueRegistry(VirtualHost virtualHost)
+ {
+ _virtualHost = virtualHost;
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public void registerQueue(AMQQueue queue) throws AMQException
+ {
+ _queueMap.put(queue.getName(), queue);
+ }
+
+ public void unregisterQueue(AMQShortString name) throws AMQException
+ {
+ _queueMap.remove(name);
+ }
+
+ public AMQQueue getQueue(AMQShortString name)
+ {
+ return _queueMap.get(name);
+ }
+
+ public Collection<AMQShortString> getQueueNames()
+ {
+ return _queueMap.keySet();
+ }
+
+ public Collection<AMQQueue> getQueues()
+ {
+ return _queueMap.values();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java
new file mode 100644
index 0000000000..153106d919
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java
@@ -0,0 +1,100 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.store.StoreContext;
+
+interface DeliveryManager
+{
+ /**
+ * Determines whether there are queued messages. Sets _queueing to false if there are no queued messages. This needs
+ * to be atomic.
+ *
+ * @return true if there are queued messages
+ */
+ boolean hasQueuedMessages();
+
+ /**
+ * This method should not be used to determin if there are messages in the queue.
+ *
+ * @return int The number of messages in the queue
+ *
+ * @use hasQueuedMessages() for all controls relating to having messages on the queue.
+ */
+ int getQueueMessageCount();
+
+ /**
+ * Requests that the delivery manager start processing the queue asynchronously if there is work that can be done
+ * (i.e. there are messages queued up and subscribers that can receive them. <p/> This should be called when
+ * subscribers are added, but only after the consume-ok message has been returned as message delivery may start
+ * immediately. It should also be called after unsuspending a client. <p/>
+ *
+ * @param executor the executor on which the delivery should take place
+ */
+ void processAsync(Executor executor);
+
+ /**
+ * Handles message delivery. The delivery manager is always in one of two modes; it is either queueing messages for
+ * asynchronous delivery or delivering directly.
+ *
+ * @param storeContext
+ * @param name the name of the entity on whose behalf we are delivering the message
+ * @param msg the message to deliver
+ * @param deliverFirst
+ *
+ * @throws org.apache.qpid.server.queue.FailedDequeueException
+ * if the message could not be dequeued
+ */
+ void deliver(StoreContext storeContext, AMQShortString name, AMQMessage msg, boolean deliverFirst) throws FailedDequeueException, AMQException;
+
+ void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException;
+
+ long clearAllMessages(StoreContext storeContext) throws AMQException;
+
+ void startMovingMessages();
+
+ void enqueueMovedMessages(StoreContext context, List<AMQMessage> messageList);
+
+ void stopMovingMessages();
+
+ void removeMovedMessages(List<AMQMessage> messageListToRemove);
+
+ List<AMQMessage> getMessages();
+
+ List<AMQMessage> getMessages(long fromMessageId, long toMessageId);
+
+ void populatePreDeliveryQueue(Subscription subscription);
+
+ boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException;
+
+ long getTotalMessageSize();
+
+ long getOldestMessageArrival();
+
+ void subscriberHasPendingResend(boolean hasContent, Subscription subscription, AMQMessage msg);
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
new file mode 100644
index 0000000000..60c1a8f574
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
@@ -0,0 +1,137 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.HashSet;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.exchange.Exchange;
+
+/**
+ * When a queue is deleted, it should be deregistered from any
+ * exchange it has been bound to. This class assists in this task,
+ * by keeping track of all bindings for a given queue.
+ */
+class ExchangeBindings
+{
+ private static final FieldTable EMPTY_ARGUMENTS = new FieldTable();
+
+ static class ExchangeBinding
+ {
+ private final Exchange _exchange;
+ private final AMQShortString _routingKey;
+ private final FieldTable _arguments;
+
+ ExchangeBinding(AMQShortString routingKey, Exchange exchange)
+ {
+ this(routingKey, exchange, EMPTY_ARGUMENTS);
+ }
+
+ ExchangeBinding(AMQShortString routingKey, Exchange exchange, FieldTable arguments)
+ {
+ _routingKey = routingKey;
+ _exchange = exchange;
+ _arguments = arguments == null ? EMPTY_ARGUMENTS : arguments;
+ }
+
+ void unbind(AMQQueue queue) throws AMQException
+ {
+ _exchange.deregisterQueue(_routingKey, queue, _arguments);
+ }
+
+ public Exchange getExchange()
+ {
+ return _exchange;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return _routingKey;
+ }
+
+ public int hashCode()
+ {
+ return (_exchange == null ? 0 : _exchange.hashCode())
+ + (_routingKey == null ? 0 : _routingKey.hashCode())
+ + (_arguments == null ? 0 : _arguments.hashCode());
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof ExchangeBinding))
+ {
+ return false;
+ }
+ ExchangeBinding eb = (ExchangeBinding) o;
+ return _exchange.equals(eb._exchange)
+ && _routingKey.equals(eb._routingKey)
+ && _arguments.equals(eb._arguments);
+ }
+ }
+
+ private final List<ExchangeBinding> _bindings = new CopyOnWriteArrayList<ExchangeBinding>();
+ private final AMQQueue _queue;
+
+ ExchangeBindings(AMQQueue queue)
+ {
+ _queue = queue;
+ }
+
+ /**
+ * Adds the specified binding to those being tracked.
+ * @param routingKey the routing key with which the queue whose bindings
+ * are being tracked by the instance has been bound to the exchange
+ * @param exchange the exchange bound to
+ */
+ void addBinding(AMQShortString routingKey, FieldTable arguments, Exchange exchange)
+ {
+ _bindings.add(new ExchangeBinding(routingKey, exchange, arguments));
+ }
+
+
+ public void remove(AMQShortString routingKey, FieldTable arguments, Exchange exchange)
+ {
+ _bindings.remove(new ExchangeBinding(routingKey, exchange, arguments));
+ }
+
+
+ /**
+ * Deregisters this queue from any exchange it has been bound to
+ */
+ void deregister() throws AMQException
+ {
+ //remove duplicates at this point
+ HashSet<ExchangeBinding> copy = new HashSet<ExchangeBinding>(_bindings);
+ for (ExchangeBinding b : copy)
+ {
+ b.unbind(_queue);
+ }
+ }
+
+ List<ExchangeBinding> getExchangeBindings()
+ {
+ return _bindings;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java
new file mode 100644
index 0000000000..6466e81dd2
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.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.server.queue;
+
+import org.apache.qpid.AMQException;
+
+/**
+ * Signals that the dequeue of a message from a queue failed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Indicates the a message could not be dequeued from a queue.
+ * <tr><td>
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Happens as a consequence of a message store failure, or reference counting error. Both of which migh become
+ * runtime exceptions, as unrecoverable conditions? In which case this one might be dropped too.
+ */
+public class FailedDequeueException extends AMQException
+{
+ public FailedDequeueException(String queue)
+ {
+ super("Failed to dequeue message from " + queue);
+ }
+
+ public FailedDequeueException(String queue, AMQException e)
+ {
+ super("Failed to dequeue message from " + queue, e);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java
new file mode 100644
index 0000000000..630186991b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java
@@ -0,0 +1,143 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.store.StoreContext;
+
+/**
+ */
+public class InMemoryMessageHandle implements AMQMessageHandle
+{
+
+ private ContentHeaderBody _contentHeaderBody;
+
+ private MessagePublishInfo _messagePublishInfo;
+
+ private List<ContentChunk> _contentBodies = new LinkedList<ContentChunk>();
+
+ private boolean _redelivered;
+
+ private long _arrivalTime;
+
+ public InMemoryMessageHandle()
+ {
+ }
+
+ public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException
+ {
+ return _contentHeaderBody;
+ }
+
+ public int getBodyCount(StoreContext context, Long messageId)
+ {
+ return _contentBodies.size();
+ }
+
+ public long getBodySize(StoreContext context, Long messageId) throws AMQException
+ {
+ return getContentHeaderBody(context, messageId).bodySize;
+ }
+
+ public ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws AMQException, IllegalArgumentException
+ {
+ if (index > _contentBodies.size() - 1)
+ {
+ throw new IllegalArgumentException("Index " + index + " out of valid range 0 to " +
+ (_contentBodies.size() - 1));
+ }
+ return _contentBodies.get(index);
+ }
+
+ public void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentBody, boolean isLastContentBody)
+ throws AMQException
+ {
+ _contentBodies.add(contentBody);
+ }
+
+ public MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException
+ {
+ return _messagePublishInfo;
+ }
+
+ public boolean isRedelivered()
+ {
+ return _redelivered;
+ }
+
+
+ public void setRedelivered(boolean redelivered)
+ {
+ _redelivered = redelivered;
+ }
+
+ public boolean isPersistent(StoreContext context, Long messageId) throws AMQException
+ {
+ //todo remove literal values to a constant file such as AMQConstants in common
+ ContentHeaderBody chb = getContentHeaderBody(context, messageId);
+ return chb.properties instanceof BasicContentHeaderProperties &&
+ ((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2;
+ }
+
+ /**
+ * This is called when all the content has been received.
+ * @param messagePublishInfo
+ * @param contentHeaderBody
+ * @throws AMQException
+ */
+ public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo,
+ ContentHeaderBody contentHeaderBody)
+ throws AMQException
+ {
+ _messagePublishInfo = messagePublishInfo;
+ _contentHeaderBody = contentHeaderBody;
+ _arrivalTime = System.currentTimeMillis();
+ }
+
+ public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException
+ {
+ // NO OP
+ }
+
+ public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
+ {
+ // NO OP
+ }
+
+ public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
+ {
+ // NO OP
+ }
+
+ public long getArrivalTime()
+ {
+ return _arrivalTime;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java
new file mode 100644
index 0000000000..061ab56024
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java
@@ -0,0 +1,245 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException;
+
+import javax.management.JMException;
+import javax.management.MBeanOperationInfo;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.management.MBeanAttribute;
+import org.apache.qpid.server.management.MBeanOperation;
+import org.apache.qpid.server.management.MBeanOperationParameter;
+
+/**
+ * The management interface exposed to allow management of a queue.
+ * @author Robert J. Greig
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedQueue
+{
+ static final String TYPE = "Queue";
+
+ /**
+ * Returns the Name of the ManagedQueue.
+ * @return the name of the managedQueue.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="Name", description = TYPE + " Name")
+ String getName() throws IOException;
+
+ /**
+ * Total number of messages on the queue, which are yet to be delivered to the consumer(s).
+ * @return number of undelivered message in the Queue.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="MessageCount", description = "Total number of undelivered messages on the queue")
+ Integer getMessageCount() throws IOException;
+
+ /**
+ * Tells the total number of messages receieved by the queue since startup.
+ * @return total number of messages received.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="ReceivedMessageCount", description="The total number of messages receieved by the queue since startup")
+ Long getReceivedMessageCount() throws IOException;
+
+ /**
+ * Size of messages in the queue
+ * @return
+ * @throws IOException
+ */
+ @MBeanAttribute(name="QueueDepth", description="Size of messages(KB) in the queue")
+ Long getQueueDepth() throws IOException, JMException;
+
+ /**
+ * Returns the total number of active subscribers to the queue.
+ * @return the number of active subscribers
+ * @throws IOException
+ */
+ @MBeanAttribute(name="ActiveConsumerCount", description="The total number of active subscribers to the queue")
+ Integer getActiveConsumerCount() throws IOException;
+
+ /**
+ * Returns the total number of subscribers to the queue.
+ * @return the number of subscribers.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="ConsumerCount", description="The total number of subscribers to the queue")
+ Integer getConsumerCount() throws IOException;
+
+ /**
+ * Tells the Owner of the ManagedQueue.
+ * @return the owner's name.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="Owner", description = "Owner")
+ String getOwner() throws IOException;
+
+ /**
+ * Tells whether this ManagedQueue is durable or not.
+ * @return true if this ManagedQueue is a durable queue.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="Durable", description = "true if the AMQQueue is durable")
+ boolean isDurable() throws IOException;
+
+ /**
+ * Tells if the ManagedQueue is set to AutoDelete.
+ * @return true if the ManagedQueue is set to AutoDelete.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="AutoDelete", description = "true if the AMQQueue is AutoDelete")
+ boolean isAutoDelete() throws IOException;
+
+ /**
+ * Returns the maximum age of a message (expiration time)
+ * @return the maximum age
+ * @throws IOException
+ */
+ Long getMaximumMessageAge() throws IOException;
+
+ /**
+ * Sets the maximum age of a message
+ * @param age maximum age of message.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="MaximumMessageAge", description="Threshold high value for message age on thr broker")
+ void setMaximumMessageAge(Long age) throws IOException;
+
+ /**
+ * Returns the maximum size of a message (in kbytes) allowed to be accepted by the
+ * ManagedQueue. This is useful in setting notifications or taking
+ * appropriate action, if the size of the message received is more than
+ * the allowed size.
+ * @return the maximum size of a message allowed to be aceepted by the
+ * ManagedQueue.
+ * @throws IOException
+ */
+ Long getMaximumMessageSize() throws IOException;
+
+ /**
+ * Sets the maximum size of the message (in kbytes) that is allowed to be
+ * accepted by the Queue.
+ * @param size maximum size of message.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="MaximumMessageSize", description="Threshold high value(KB) for a message size")
+ void setMaximumMessageSize(Long size) throws IOException;
+
+ /**
+ * Tells the maximum number of messages that can be stored in the queue.
+ * This is useful in setting the notifications or taking required
+ * action is the number of message increase this limit.
+ * @return maximum muber of message allowed to be stored in the queue.
+ * @throws IOException
+ */
+ Long getMaximumMessageCount() throws IOException;
+
+ /**
+ * Sets the maximum number of messages allowed to be stored in the queue.
+ * @param value the maximum number of messages allowed to be stored in the queue.
+ * @throws IOException
+ */
+ @MBeanAttribute(name="MaximumMessageCount", description="Threshold high value for number of undelivered messages in the queue")
+ void setMaximumMessageCount(Long value) throws IOException;
+
+ /**
+ * This is useful for setting notifications or taking required action if the size of messages
+ * stored in the queue increases over this limit.
+ * @return threshold high value for Queue Depth
+ * @throws IOException
+ */
+ Long getMaximumQueueDepth() throws IOException;
+
+ /**
+ * Sets the maximum size of all the messages together, that can be stored
+ * in the queue.
+ * @param value
+ * @throws IOException
+ */
+ @MBeanAttribute(name="MaximumQueueDepth", description="The threshold high value(KB) for Queue Depth")
+ void setMaximumQueueDepth(Long value) throws IOException;
+
+
+
+ //********** Operations *****************//
+
+
+ /**
+ * Returns a subset of all the messages stored in the queue. The messages
+ * are returned based on the given index numbers.
+ * @param fromIndex
+ * @param toIndex
+ * @return
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="viewMessages",
+ description="Message headers for messages in this queue within given index range. eg. from index 1 - 100")
+ TabularData viewMessages(@MBeanOperationParameter(name="from index", description="from index")int fromIndex,
+ @MBeanOperationParameter(name="to index", description="to index")int toIndex)
+ throws IOException, JMException, AMQException;
+
+ @MBeanOperation(name="viewMessageContent", description="The message content for given Message Id")
+ CompositeData viewMessageContent(@MBeanOperationParameter(name="Message Id", description="Message Id")long messageId)
+ throws IOException, JMException;
+
+ /**
+ * Deletes the first message from top.
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="deleteMessageFromTop", description="Deletes the first message from top",
+ impact= MBeanOperationInfo.ACTION)
+ void deleteMessageFromTop() throws IOException, JMException;
+
+ /**
+ * Clears the queue by deleting all the undelivered messages from the queue.
+ * @throws IOException
+ * @throws JMException
+ */
+ @MBeanOperation(name="clearQueue",
+ description="Clears the queue by deleting all the undelivered messages from the queue",
+ impact= MBeanOperationInfo.ACTION)
+ void clearQueue() throws IOException, JMException;
+
+ /**
+ * Moves the messages in given range of message Ids to given Queue. QPID-170
+ * @param fromMessageId first in the range of message ids
+ * @param toMessageId last in the range of message ids
+ * @param toQueue where the messages are to be moved
+ * @throws IOException
+ * @throws JMException
+ * @throws AMQException
+ */
+ @MBeanOperation(name="moveMessages",
+ description="You can move messages to another queue from this queue ",
+ impact= MBeanOperationInfo.ACTION)
+ void moveMessages(@MBeanOperationParameter(name="from MessageId", description="from MessageId")long fromMessageId,
+ @MBeanOperationParameter(name="to MessageId", description="to MessageId")long toMessageId,
+ @MBeanOperationParameter(name= ManagedQueue.TYPE, description="to Queue Name")String toQueue)
+ throws IOException, JMException, AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java
new file mode 100644
index 0000000000..090096d3c3
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+
+/**
+ * MessageCleanupException represents the failure to perform reference counting on messages correctly. This should not
+ * happen, but there may be programming errors giving race conditions that cause the reference counting to go wrong.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Signals that the reference count of a message has gone below zero.
+ * <tr><td> Indicates that a message store has lost a message which is still referenced.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo The race conditions leading to this error should be cleaned up, and a runtime exception used instead. If the
+ * message store loses messages, then something is seriously wrong and it would be sensible to terminate the
+ * broker. This may be disguising out of memory errors.
+ */
+public class MessageCleanupException extends AMQException
+{
+ public MessageCleanupException(long messageId, AMQException e)
+ {
+ super("Failed to cleanup message with id " + messageId, e);
+ }
+
+ public MessageCleanupException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java
new file mode 100644
index 0000000000..94ab935115
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.server.store.MessageStore;
+
+/**
+ * Constructs a message handle based on the publish body, the content header and the queue to which the message
+ * has been routed.
+ *
+ * @author Robert Greig (robert.j.greig@jpmorgan.com)
+ */
+public class MessageHandleFactory
+{
+
+ public AMQMessageHandle createMessageHandle(Long messageId, MessageStore store, boolean persistent)
+ {
+ // just hardcoded for now
+ if (persistent)
+ {
+ return new WeakReferenceMessageHandle(store);
+ }
+ else
+ {
+ return new InMemoryMessageHandle();
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java
new file mode 100644
index 0000000000..6118a4c11f
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.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.server.queue;
+
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+
+/**
+ * Encapsulates a publish body and a content header. In the context of the message store these are treated as a
+ * single unit.
+ */
+public class MessageMetaData
+{
+ private MessagePublishInfo _messagePublishInfo;
+
+ private ContentHeaderBody _contentHeaderBody;
+
+ private int _contentChunkCount;
+
+ private long _arrivalTime;
+
+ public MessageMetaData(MessagePublishInfo publishBody, ContentHeaderBody contentHeaderBody, int contentChunkCount)
+ {
+ this(publishBody,contentHeaderBody, contentChunkCount, System.currentTimeMillis());
+ }
+
+ public MessageMetaData(MessagePublishInfo publishBody, ContentHeaderBody contentHeaderBody, int contentChunkCount, long arrivalTime)
+ {
+ _contentHeaderBody = contentHeaderBody;
+ _messagePublishInfo = publishBody;
+ _contentChunkCount = contentChunkCount;
+ _arrivalTime = arrivalTime;
+ }
+
+ public int getContentChunkCount()
+ {
+ return _contentChunkCount;
+ }
+
+ public void setContentChunkCount(int contentChunkCount)
+ {
+ _contentChunkCount = contentChunkCount;
+ }
+
+ public ContentHeaderBody getContentHeaderBody()
+ {
+ return _contentHeaderBody;
+ }
+
+ public void setContentHeaderBody(ContentHeaderBody contentHeaderBody)
+ {
+ _contentHeaderBody = contentHeaderBody;
+ }
+
+ public MessagePublishInfo getMessagePublishInfo()
+ {
+ return _messagePublishInfo;
+ }
+
+ public void setMessagePublishInfo(MessagePublishInfo messagePublishInfo)
+ {
+ _messagePublishInfo = messagePublishInfo;
+ }
+
+ public long getArrivalTime()
+ {
+ return _arrivalTime;
+ }
+
+ public void setArrivalTime(long arrivalTime)
+ {
+ _arrivalTime = arrivalTime;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java
new file mode 100644
index 0000000000..d6fd1eec89
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java
@@ -0,0 +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.server.queue;
+
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.RequiredDeliveryException;
+
+/**
+ * NoConsumersException is a {@link RequiredDeliveryException} that represents the failure case where an immediate
+ * message cannot be delivered because there are presently no consumers for the message. The AMQP status code, 313, is
+ * always used to report this condition.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to deliver a message that must be delivered.
+ * </table>
+ */
+public class NoConsumersException extends RequiredDeliveryException
+{
+ public NoConsumersException(AMQMessage message)
+ {
+ super("Immediate delivery is not possible.", message);
+ }
+
+ public AMQConstant getReplyCode()
+ {
+ return AMQConstant.NO_CONSUMERS;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java
new file mode 100644
index 0000000000..6b3d65661f
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java
@@ -0,0 +1,138 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+
+public enum NotificationCheck
+{
+
+ MESSAGE_COUNT_ALERT
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+ int msgCount = queue.getMessageCount();
+ final long maximumMessageCount = queue.getMaximumMessageCount();
+ if (maximumMessageCount!= 0 && msgCount >= maximumMessageCount)
+ {
+ listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached.");
+ return true;
+ }
+ return false;
+ }
+ },
+ MESSAGE_SIZE_ALERT(true)
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+ final long maximumMessageSize = queue.getMaximumMessageSize();
+ if(maximumMessageSize != 0)
+ {
+ // Check for threshold message size
+ long messageSize;
+ try
+ {
+ messageSize = (msg == null) ? 0 : msg.getContentHeaderBody().bodySize;
+ }
+ catch (AMQException e)
+ {
+ messageSize = 0;
+ }
+
+
+ if (messageSize >= maximumMessageSize)
+ {
+ listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ },
+ QUEUE_DEPTH_ALERT
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+ // Check for threshold queue depth in bytes
+ final long maximumQueueDepth = queue.getMaximumQueueDepth();
+
+ if(maximumQueueDepth != 0)
+ {
+ final long queueDepth = queue.getQueueDepth();
+
+ if (queueDepth >= maximumQueueDepth)
+ {
+ listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached.");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ },
+ MESSAGE_AGE_ALERT
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+
+ final long maxMessageAge = queue.getMaximumMessageAge();
+ if(maxMessageAge != 0)
+ {
+ final long currentTime = System.currentTimeMillis();
+ final long thresholdTime = currentTime - maxMessageAge;
+ final long firstArrivalTime = queue.getOldestMessageArrivalTime();
+
+ if(firstArrivalTime < thresholdTime)
+ {
+ long oldestAge = currentTime - firstArrivalTime;
+ listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached.");
+
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+ }
+ ;
+
+ private final boolean _messageSpecific;
+
+ NotificationCheck()
+ {
+ this(false);
+ }
+
+ NotificationCheck(boolean messageSpecific)
+ {
+ _messageSpecific = messageSpecific;
+ }
+
+ public boolean isMessageSpecific()
+ {
+ return _messageSpecific;
+ }
+
+ abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener);
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java
new file mode 100644
index 0000000000..959ca03c80
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java
@@ -0,0 +1,27 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+
+public interface QueueNotificationListener
+{
+ void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg);
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java
new file mode 100644
index 0000000000..1210f0e97c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.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.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import java.util.Collection;
+
+public interface QueueRegistry
+{
+ VirtualHost getVirtualHost();
+
+ void registerQueue(AMQQueue queue) throws AMQException;
+
+ void unregisterQueue(AMQShortString name) throws AMQException;
+
+ AMQQueue getQueue(AMQShortString name);
+
+ Collection<AMQShortString> getQueueNames();
+
+ Collection<AMQQueue> getQueues();
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java
new file mode 100644
index 0000000000..77688f19be
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.Queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.AMQChannel;
+
+public interface Subscription
+{
+ void send(AMQMessage msg, AMQQueue queue) throws AMQException;
+
+ boolean isSuspended();
+
+ void queueDeleted(AMQQueue queue) throws AMQException;
+
+ boolean filtersMessages();
+
+ boolean hasInterest(AMQMessage msg);
+
+ Queue<AMQMessage> getPreDeliveryQueue();
+
+ Queue<AMQMessage> getResendQueue();
+
+ Queue<AMQMessage> getNextQueue(Queue<AMQMessage> messages);
+
+ void enqueueForPreDelivery(AMQMessage msg, boolean deliverFirst);
+
+ boolean isAutoClose();
+
+ void close();
+
+ boolean isClosed();
+
+ boolean isBrowser();
+
+ boolean wouldSuspend(AMQMessage msg);
+
+ void addToResendQueue(AMQMessage msg);
+
+ Object getSendLock();
+
+ AMQChannel getChannel();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java
new file mode 100644
index 0000000000..917f7c4e97
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.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.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+
+/**
+ * Allows the customisation of the creation of a subscription. This is typically done within an AMQQueue. This factory
+ * primarily assists testing although in future more sophisticated subscribers may need a different subscription
+ * implementation.
+ *
+ * @see org.apache.qpid.server.queue.AMQQueue
+ */
+public interface SubscriptionFactory
+{
+ Subscription createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag, boolean acks,
+ FieldTable filters, boolean noLocal, AMQQueue queue) throws AMQException;
+
+
+ Subscription createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag)
+ throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java
new file mode 100644
index 0000000000..1299c3a80c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java
@@ -0,0 +1,680 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.common.AMQPFilterTypes;
+import org.apache.qpid.common.ClientProperties;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.server.filter.FilterManager;
+import org.apache.qpid.server.filter.FilterManagerFactory;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.util.ConcurrentLinkedQueueAtomicSize;
+import org.apache.qpid.util.MessageQueue;
+import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize;
+
+/**
+ * Encapsulation of a supscription to a queue. <p/> Ties together the protocol session of a subscriber, the consumer tag
+ * that was given out by the broker and the channel id. <p/>
+ */
+public class SubscriptionImpl implements Subscription
+{
+
+ private static final Logger _suspensionlogger = Logger.getLogger("Suspension");
+ private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class);
+
+ public final AMQChannel channel;
+
+ public final AMQProtocolSession protocolSession;
+
+ public final AMQShortString consumerTag;
+
+ private final Object _sessionKey;
+
+ private MessageQueue<AMQMessage> _messages;
+
+ private Queue<AMQMessage> _resendQueue;
+
+ private final boolean _noLocal;
+
+ /** True if messages need to be acknowledged */
+ private final boolean _acks;
+ private FilterManager _filters;
+ private final boolean _isBrowser;
+ private final Boolean _autoClose;
+ private boolean _sentClose = false;
+
+ private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString();
+
+ private AMQQueue _queue;
+ private final AtomicBoolean _sendLock = new AtomicBoolean(false);
+
+
+ public static class Factory implements SubscriptionFactory
+ {
+ public Subscription createSubscription(int channel, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, boolean acks, FieldTable filters,
+ boolean noLocal, AMQQueue queue) throws AMQException
+ {
+ return new SubscriptionImpl(channel, protocolSession, consumerTag, acks, filters, noLocal, queue);
+ }
+
+ public SubscriptionImpl createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag)
+ throws AMQException
+ {
+ return new SubscriptionImpl(channel, protocolSession, consumerTag, false, null, false, null);
+ }
+ }
+
+ public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, boolean acks)
+ throws AMQException
+ {
+ this(channelId, protocolSession, consumerTag, acks, null, false, null);
+ }
+
+ public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, boolean acks, FieldTable filters,
+ boolean noLocal, AMQQueue queue)
+ throws AMQException
+ {
+ AMQChannel channel = protocolSession.getChannel(channelId);
+ if (channel == null)
+ {
+ throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session");
+ }
+
+ this.channel = channel;
+ this.protocolSession = protocolSession;
+ this.consumerTag = consumerTag;
+ _sessionKey = protocolSession.getKey();
+ _acks = acks;
+ _noLocal = noLocal;
+ _queue = queue;
+
+ _filters = FilterManagerFactory.createManager(filters);
+
+
+ if (_filters != null)
+ {
+ Object isBrowser = filters.get(AMQPFilterTypes.NO_CONSUME.getValue());
+ if (isBrowser != null)
+ {
+ _isBrowser = (Boolean) isBrowser;
+ }
+ else
+ {
+ _isBrowser = false;
+ }
+ }
+ else
+ {
+ _isBrowser = false;
+ }
+
+
+ if (_filters != null)
+ {
+ Object autoClose = filters.get(AMQPFilterTypes.AUTO_CLOSE.getValue());
+ if (autoClose != null)
+ {
+ _autoClose = (Boolean) autoClose;
+ }
+ else
+ {
+ _autoClose = false;
+ }
+ }
+ else
+ {
+ _autoClose = false;
+ }
+
+
+ if (filtersMessages())
+ {
+ _messages = new ConcurrentLinkedMessageQueueAtomicSize<AMQMessage>();
+ }
+ else
+ {
+ // Reference the DeliveryManager
+ _messages = null;
+ }
+ }
+
+
+ public SubscriptionImpl(int channel, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag)
+ throws AMQException
+ {
+ this(channel, protocolSession, consumerTag, false);
+ }
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof SubscriptionImpl) && equals((SubscriptionImpl) o);
+ }
+
+ /**
+ * Equality holds if the session matches and the channel and consumer tag are the same.
+ *
+ * @param psc The subscriptionImpl to compare
+ *
+ * @return equality
+ */
+ private boolean equals(SubscriptionImpl psc)
+ {
+ return _sessionKey.equals(psc._sessionKey)
+ && psc.channel == channel
+ && psc.consumerTag.equals(consumerTag);
+ }
+
+ public int hashCode()
+ {
+ return _sessionKey.hashCode();
+ }
+
+ public String toString()
+ {
+ String subscriber = "[channel=" + channel +
+ ", consumerTag=" + consumerTag +
+ ", session=" + protocolSession.getKey() +
+ ", resendQueue=" + (_resendQueue != null);
+
+ if (_resendQueue != null)
+ {
+ subscriber += ", resendSize=" + _resendQueue.size();
+ }
+
+
+ return subscriber + "]";
+ }
+
+ /**
+ * This method can be called by each of the publisher threads. As a result all changes to the channel object must be
+ * thread safe.
+ *
+ * @param msg The message to send
+ * @param queue the Queue it has been sent from
+ *
+ * @throws AMQException
+ */
+ public void send(AMQMessage msg, AMQQueue queue) throws AMQException
+ {
+ if (msg != null)
+ {
+ if (_isBrowser)
+ {
+ sendToBrowser(msg, queue);
+ }
+ else
+ {
+ sendToConsumer(channel.getStoreContext(), msg, queue);
+ }
+ }
+ else
+ {
+ _logger.error("Attempt to send Null message", new NullPointerException());
+ }
+ }
+
+ private void sendToBrowser(AMQMessage msg, AMQQueue queue) throws AMQException
+ {
+ // We don't decrement the reference here as we don't want to consume the message
+ // but we do want to send it to the client.
+
+ synchronized (channel)
+ {
+ long deliveryTag = channel.getNextDeliveryTag();
+
+ // We don't need to add the message to the unacknowledgedMap as we don't need to know if the client
+ // received the message. If it is lost in transit that is not important.
+// if (_acks)
+// {
+// channel.addUnacknowledgedBrowsedMessage(msg, deliveryTag, consumerTag, queue);
+// }
+
+ if (_sendLock.get())
+ {
+ _logger.error("Sending " + msg + " when subscriber(" + this + ") is closed!");
+ }
+
+ protocolSession.getProtocolOutputConverter().writeDeliver(msg, channel.getChannelId(), deliveryTag, consumerTag);
+ }
+ }
+
+ private void sendToConsumer(StoreContext storeContext, AMQMessage msg, AMQQueue queue)
+ throws AMQException
+ {
+ try
+ { // if we do not need to wait for client acknowledgements
+ // we can decrement the reference count immediately.
+
+ // By doing this _before_ the send we ensure that it
+ // doesn't get sent if it can't be dequeued, preventing
+ // duplicate delivery on recovery.
+
+ // The send may of course still fail, in which case, as
+ // the message is unacked, it will be lost.
+ if (!_acks)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("No ack mode so dequeuing message immediately: " + msg.getMessageId());
+ }
+ queue.dequeue(storeContext, msg);
+ }
+
+ synchronized (channel)
+ {
+ long deliveryTag = channel.getNextDeliveryTag();
+
+ if (_sendLock.get())
+ {
+ _logger.error("Sending " + msg + " when subscriber(" + this + ") is closed!");
+ }
+
+ if (_acks)
+ {
+ channel.addUnacknowledgedMessage(msg, deliveryTag, consumerTag, queue);
+ }
+
+ protocolSession.getProtocolOutputConverter().writeDeliver(msg, channel.getChannelId(), deliveryTag, consumerTag);
+
+ if (!_acks)
+ {
+ msg.decrementReference(storeContext);
+ }
+ }
+ }
+ finally
+ {
+ //Only set delivered if it actually was writen successfully..
+ // using a try->finally would set it even if an error occured.
+ // Is this what we want?
+
+ msg.setDeliveredToConsumer();
+ }
+ }
+
+ public boolean isSuspended()
+ {
+// if (_suspensionlogger.isInfoEnabled())
+// {
+// if (channel.isSuspended())
+// {
+// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") channel's is susupended");
+// }
+// if (_sendLock.get())
+// {
+// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") has sendLock set so closing.");
+// }
+// }
+ return channel.isSuspended() || _sendLock.get();
+ }
+
+ /**
+ * Callback indicating that a queue has been deleted.
+ *
+ * @param queue The queue to delete
+ */
+ public void queueDeleted(AMQQueue queue) throws AMQException
+ {
+ channel.queueDeleted(queue);
+ }
+
+ public boolean filtersMessages()
+ {
+ return _filters != null || _noLocal;
+ }
+
+ public boolean hasInterest(AMQMessage msg)
+ {
+ //check that the message hasn't been rejected
+ if (msg.isRejectedBy(this))
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Subscription:" + debugIdentity() + " rejected message:" + msg.debugIdentity());
+ }
+// return false;
+ }
+
+ final AMQProtocolSession publisher = msg.getPublisher();
+
+ //todo - client id should be recoreded and this test removed but handled below
+ if (_noLocal && publisher != null)
+ {
+ // We don't want local messages so check to see if message is one we sent
+ Object localInstance;
+ Object msgInstance;
+
+ if ((protocolSession.getClientProperties() != null) &&
+ (localInstance = protocolSession.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
+ {
+
+ if ((publisher.getClientProperties() != null) &&
+ (msgInstance = publisher.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
+ {
+ if (localInstance == msgInstance || localInstance.equals(msgInstance))
+ {
+// if (_logger.isTraceEnabled())
+// {
+// _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
+// msg.debugIdentity() + ")");
+// }
+ return false;
+ }
+ }
+ }
+ else
+ {
+
+ localInstance = protocolSession.getClientIdentifier();
+ //todo - client id should be recoreded and this test removed but handled here
+
+ msgInstance = publisher.getClientIdentifier();
+ if (localInstance == msgInstance || ((localInstance != null) && localInstance.equals(msgInstance)))
+ {
+// if (_logger.isTraceEnabled())
+// {
+// _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
+// msg.debugIdentity() + ")");
+// }
+ return false;
+ }
+ }
+
+
+ }
+
+
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("(" + debugIdentity() + ") checking filters for message (" + msg.debugIdentity());
+ }
+ return checkFilters(msg);
+
+ }
+
+ private String id = String.valueOf(System.identityHashCode(this));
+
+ private String debugIdentity()
+ {
+ return id;
+ }
+
+ private boolean checkFilters(AMQMessage msg)
+ {
+ if (_filters != null)
+ {
+// if (_logger.isTraceEnabled())
+// {
+// _logger.trace("(" + debugIdentity() + ") has filters.");
+// }
+ return _filters.allAllow(msg);
+ }
+ else
+ {
+// if (_logger.isTraceEnabled())
+// {
+// _logger.trace("(" + debugIdentity() + ") has no filters");
+// }
+
+ return true;
+ }
+ }
+
+ public Queue<AMQMessage> getPreDeliveryQueue()
+ {
+ return _messages;
+ }
+
+ public void enqueueForPreDelivery(AMQMessage msg, boolean deliverFirst)
+ {
+ if (_messages != null)
+ {
+ if (deliverFirst)
+ {
+ _messages.pushHead(msg);
+ }
+ else
+ {
+ _messages.offer(msg);
+ }
+ }
+ }
+
+ public boolean isAutoClose()
+ {
+ return _autoClose;
+ }
+
+ public void close()
+ {
+ boolean closed = false;
+ synchronized (_sendLock)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Setting SendLock true:" + debugIdentity());
+ }
+
+ closed = _sendLock.getAndSet(true);
+ }
+
+ if (closed)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Called close() on a closed subscription");
+ }
+
+ return;
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Closing subscription (" + debugIdentity() + "):" + this);
+ }
+
+ if (_resendQueue != null && !_resendQueue.isEmpty())
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Requeuing closing subscription (" + debugIdentity() + "):" + this);
+ }
+ requeue();
+ }
+
+ //remove references in PDQ
+ if (_messages != null)
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Clearing PDQ (" + debugIdentity() + "):" + this);
+ }
+
+ _messages.clear();
+ }
+ }
+
+ private void autoclose()
+ {
+ close();
+
+ if (_autoClose && !_sentClose)
+ {
+ _logger.info("Closing autoclose subscription (" + debugIdentity() + "):" + this);
+
+ ProtocolOutputConverter converter = protocolSession.getProtocolOutputConverter();
+ converter.confirmConsumerAutoClose(channel.getChannelId(), consumerTag);
+ _sentClose = true;
+
+ //fixme JIRA do this better
+ try
+ {
+ channel.unsubscribeConsumer(protocolSession, consumerTag);
+ }
+ catch (AMQException e)
+ {
+ // Occurs if we cannot find the subscriber in the channel with protocolSession and consumerTag.
+ }
+ }
+ }
+
+ private void requeue()
+ {
+ if (_queue != null)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Requeuing :" + _resendQueue.size() + " messages");
+ }
+
+ while (!_resendQueue.isEmpty())
+ {
+ AMQMessage resent = _resendQueue.poll();
+
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Removed for resending:" + resent.debugIdentity());
+ }
+
+ resent.release(_queue);
+ _queue.subscriberHasPendingResend(false, this, resent);
+
+ try
+ {
+ channel.getTransactionalContext().deliver(resent, _queue, true);
+ }
+ catch (AMQException e)
+ {
+ _logger.error("MESSAGE LOSS : Unable to re-deliver messages", e);
+ }
+ }
+
+ if (!_resendQueue.isEmpty())
+ {
+ _logger.error("[MESSAGES LOST]Unable to re-deliver messages as queue is null.");
+ }
+
+ _queue.subscriberHasPendingResend(false, this, null);
+ }
+ else
+ {
+ if (!_resendQueue.isEmpty())
+ {
+ _logger.error("Unable to re-deliver messages as queue is null.");
+ }
+ }
+
+ // Clear the messages
+ _resendQueue = null;
+ }
+
+
+ public boolean isClosed()
+ {
+ return _sendLock.get(); // This rather than _close is used to signify the subscriber is now closed.
+ }
+
+ public boolean isBrowser()
+ {
+ return _isBrowser;
+ }
+
+ public boolean wouldSuspend(AMQMessage msg)
+ {
+ return channel.wouldSuspend(msg);
+ }
+
+ public Queue<AMQMessage> getResendQueue()
+ {
+ if (_resendQueue == null)
+ {
+ _resendQueue = new ConcurrentLinkedQueueAtomicSize<AMQMessage>();
+ }
+ return _resendQueue;
+ }
+
+
+ public Queue<AMQMessage> getNextQueue(Queue<AMQMessage> messages)
+ {
+ if (_resendQueue != null && !_resendQueue.isEmpty())
+ {
+ return _resendQueue;
+ }
+
+ if (filtersMessages())
+ {
+ if (isAutoClose())
+ {
+ if (_messages.isEmpty())
+ {
+ autoclose();
+ return null;
+ }
+ }
+ return _messages;
+ }
+ else // we want the DM queue
+ {
+ return messages;
+ }
+ }
+
+ public void addToResendQueue(AMQMessage msg)
+ {
+ // add to our resend queue
+ getResendQueue().add(msg);
+
+ // Mark Queue has having content.
+ if (_queue == null)
+ {
+ _logger.error("Queue is null won't be able to resend messages");
+ }
+ else
+ {
+ _queue.subscriberHasPendingResend(true, this, msg);
+ }
+ }
+
+ public Object getSendLock()
+ {
+ return _sendLock;
+ }
+
+ public AMQChannel getChannel()
+ {
+ return channel;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java
new file mode 100644
index 0000000000..4df88baebc
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.List;
+
+/**
+ * Abstraction of actor that will determine the subscriber to whom
+ * a message will be sent.
+ */
+public interface SubscriptionManager
+{
+ public List<Subscription> getSubscriptions();
+ public boolean hasActiveSubscribers();
+ public Subscription nextSubscriber(AMQMessage msg);
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java
new file mode 100644
index 0000000000..b500247fa4
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java
@@ -0,0 +1,229 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.List;
+import java.util.ListIterator;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+
+/** Holds a set of subscriptions for a queue and manages the round robin-ing of deliver etc. */
+class SubscriptionSet implements WeightedSubscriptionManager
+{
+ private static final Logger _log = Logger.getLogger(SubscriptionSet.class);
+
+ /** List of registered subscribers */
+ private List<Subscription> _subscriptions = new CopyOnWriteArrayList<Subscription>();
+
+ /** Used to control the round robin delivery of content */
+ private int _currentSubscriber;
+ private final Object _subscriptionsChange = new Object();
+
+
+ /** Accessor for unit tests. */
+ int getCurrentSubscriber()
+ {
+ return _currentSubscriber;
+ }
+
+ public void addSubscriber(Subscription subscription)
+ {
+ synchronized (_subscriptionsChange)
+ {
+ _subscriptions.add(subscription);
+ }
+ }
+
+ /**
+ * Remove the subscription, returning it if it was found
+ *
+ * @param subscription
+ *
+ * @return null if no match was found
+ */
+ public Subscription removeSubscriber(Subscription subscription)
+ {
+ // TODO: possibly need O(1) operation here.
+
+ Subscription sub = null;
+ synchronized (_subscriptionsChange)
+ {
+ int subIndex = _subscriptions.indexOf(subscription);
+
+ if (subIndex != -1)
+ {
+ //we can't just return the passed in subscription as it is a new object
+ // and doesn't contain the stored state we need.
+ //NOTE while this may be removed now anyone with an iterator will still have it in the list!!
+ sub = _subscriptions.remove(subIndex);
+ }
+ else
+ {
+ _log.error("Unable to remove from index(" + subIndex + ")subscription:" + subscription);
+ }
+ }
+ if (sub != null)
+ {
+ return sub;
+ }
+ else
+ {
+ debugDumpSubscription(subscription);
+ return null;
+ }
+ }
+
+ private void debugDumpSubscription(Subscription subscription)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Subscription " + subscription + " not found. Dumping subscriptions:");
+ for (Subscription s : _subscriptions)
+ {
+ _log.debug("Subscription: " + s);
+ }
+ _log.debug("Subscription dump complete");
+ }
+ }
+
+ /**
+ * Return the next unsuspended subscription or null if not found. <p/> Performance note: This method can scan all
+ * items twice when looking for a subscription that is not suspended. The worst case occcurs when all subscriptions
+ * are suspended. However, it is does this without synchronisation and subscriptions may be added and removed
+ * concurrently. Also note that because of race conditions and when subscriptions are removed between calls to
+ * nextSubscriber, the IndexOutOfBoundsException also causes the scan to start at the beginning.
+ */
+ public Subscription nextSubscriber(AMQMessage msg)
+ {
+ if (_subscriptions.isEmpty())
+ {
+ return null;
+ }
+
+ try
+ {
+ final Subscription result = nextSubscriberImpl(msg);
+ if (result == null)
+ {
+ _currentSubscriber = 0;
+ return nextSubscriberImpl(msg);
+ }
+ else
+ {
+ return result;
+ }
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ _currentSubscriber = 0;
+ return nextSubscriber(msg);
+ }
+ }
+
+ private Subscription nextSubscriberImpl(AMQMessage msg)
+ {
+ final ListIterator<Subscription> iterator = _subscriptions.listIterator(_currentSubscriber);
+ while (iterator.hasNext())
+ {
+ Subscription subscription = iterator.next();
+ ++_currentSubscriber;
+ subscriberScanned();
+
+ if (!(subscription.isSuspended() || subscription.wouldSuspend(msg)))
+ {
+ if (subscription.hasInterest(msg))
+ {
+ // if the queue is not empty then this client is ready to receive a message.
+ //FIXME the queue could be full of sent messages.
+ // Either need to clean all PDQs after sending a message
+ // OR have a clean up thread that runs the PDQs expunging the messages.
+ if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty())
+ {
+ return subscription;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /** Overridden in test classes. */
+ protected void subscriberScanned()
+ {
+ }
+
+ public boolean isEmpty()
+ {
+ return _subscriptions.isEmpty();
+ }
+
+ public List<Subscription> getSubscriptions()
+ {
+ return _subscriptions;
+ }
+
+ public boolean hasActiveSubscribers()
+ {
+ for (Subscription s : _subscriptions)
+ {
+ if (!s.isSuspended())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int getWeight()
+ {
+ int count = 0;
+ for (Subscription s : _subscriptions)
+ {
+ if (!s.isSuspended())
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Notification that a queue has been deleted. This is called so that the subscription can inform the channel, which
+ * in turn can update its list of unacknowledged messages.
+ *
+ * @param queue
+ */
+ public void queueDeleted(AMQQueue queue) throws AMQException
+ {
+ for (Subscription s : _subscriptions)
+ {
+ s.queueDeleted(queue);
+ }
+ }
+
+ int size()
+ {
+ return _subscriptions.size();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java
new file mode 100644
index 0000000000..79ee6b93a3
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java
@@ -0,0 +1,121 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+/**
+ * Contains data that is only used in AMQMessage transiently, e.g. while the content
+ * body fragments are arriving.
+ *
+ * Having this data stored in a separate class means that the AMQMessage class avoids
+ * the small overhead of numerous guaranteed-null references.
+ *
+ * @author Apache Software Foundation
+ */
+public class TransientMessageData
+{
+ /**
+ * Stored temporarily until the header has been received at which point it is used when
+ * constructing the handle
+ */
+ private MessagePublishInfo _messagePublishInfo;
+
+ /**
+ * Also stored temporarily.
+ */
+ private ContentHeaderBody _contentHeaderBody;
+
+ /**
+ * Keeps a track of how many bytes we have received in body frames
+ */
+ private long _bodyLengthReceived = 0;
+
+ /**
+ * This is stored during routing, to know the queues to which this message should immediately be
+ * delivered. It is <b>cleared after delivery has been attempted</b>. Any persistent record of destinations is done
+ * by the message handle.
+ */
+ private List<AMQQueue> _destinationQueues = new LinkedList<AMQQueue>();
+
+ public MessagePublishInfo getMessagePublishInfo()
+ {
+ return _messagePublishInfo;
+ }
+
+ public void setMessagePublishInfo(MessagePublishInfo messagePublishInfo)
+ {
+ _messagePublishInfo = messagePublishInfo;
+ }
+
+ public List<AMQQueue> getDestinationQueues()
+ {
+ return _destinationQueues;
+ }
+
+ public void setDestinationQueues(List<AMQQueue> destinationQueues)
+ {
+ _destinationQueues = destinationQueues;
+ }
+
+ public ContentHeaderBody getContentHeaderBody()
+ {
+ return _contentHeaderBody;
+ }
+
+ public void setContentHeaderBody(ContentHeaderBody contentHeaderBody)
+ {
+ _contentHeaderBody = contentHeaderBody;
+ }
+
+ public long getBodyLengthReceived()
+ {
+ return _bodyLengthReceived;
+ }
+
+ public void addBodyLength(int value)
+ {
+ _bodyLengthReceived += value;
+ }
+
+ public boolean isAllContentReceived() throws AMQException
+ {
+ return _bodyLengthReceived == _contentHeaderBody.bodySize;
+ }
+
+ public void addDestinationQueue(AMQQueue queue)
+ {
+ _destinationQueues.add(queue);
+ }
+
+ public boolean isPersistent()
+ {
+ //todo remove literal values to a constant file such as AMQConstants in common
+ return _contentHeaderBody.properties instanceof BasicContentHeaderProperties &&
+ ((BasicContentHeaderProperties) _contentHeaderBody.properties).getDeliveryMode() == 2;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java
new file mode 100644
index 0000000000..373a64e2eb
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java
@@ -0,0 +1,227 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoreContext;
+
+/**
+ * @author Robert Greig (robert.j.greig@jpmorgan.com)
+ */
+public class WeakReferenceMessageHandle implements AMQMessageHandle
+{
+ private WeakReference<ContentHeaderBody> _contentHeaderBody;
+
+ private WeakReference<MessagePublishInfo> _messagePublishInfo;
+
+ private List<WeakReference<ContentChunk>> _contentBodies;
+
+ private boolean _redelivered;
+
+ private final MessageStore _messageStore;
+
+ private long _arrivalTime;
+
+
+ public WeakReferenceMessageHandle(MessageStore messageStore)
+ {
+ _messageStore = messageStore;
+ }
+
+ public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException
+ {
+ ContentHeaderBody chb = (_contentHeaderBody != null ? _contentHeaderBody.get() : null);
+ if (chb == null)
+ {
+ MessageMetaData mmd = loadMessageMetaData(context, messageId);
+ chb = mmd.getContentHeaderBody();
+ }
+ return chb;
+ }
+
+ private MessageMetaData loadMessageMetaData(StoreContext context, Long messageId)
+ throws AMQException
+ {
+ MessageMetaData mmd = _messageStore.getMessageMetaData(context, messageId);
+ populateFromMessageMetaData(mmd);
+ return mmd;
+ }
+
+ private void populateFromMessageMetaData(MessageMetaData mmd)
+ {
+ _arrivalTime = mmd.getArrivalTime();
+ _contentHeaderBody = new WeakReference<ContentHeaderBody>(mmd.getContentHeaderBody());
+ _messagePublishInfo = new WeakReference<MessagePublishInfo>(mmd.getMessagePublishInfo());
+ }
+
+ public int getBodyCount(StoreContext context, Long messageId) throws AMQException
+ {
+ if (_contentBodies == null)
+ {
+ MessageMetaData mmd = _messageStore.getMessageMetaData(context, messageId);
+ int chunkCount = mmd.getContentChunkCount();
+ _contentBodies = new ArrayList<WeakReference<ContentChunk>>(chunkCount);
+ for (int i = 0; i < chunkCount; i++)
+ {
+ _contentBodies.add(new WeakReference<ContentChunk>(null));
+ }
+ }
+ return _contentBodies.size();
+ }
+
+ public long getBodySize(StoreContext context, Long messageId) throws AMQException
+ {
+ return getContentHeaderBody(context, messageId).bodySize;
+ }
+
+ public ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws AMQException, IllegalArgumentException
+ {
+ if (index > _contentBodies.size() - 1)
+ {
+ throw new IllegalArgumentException("Index " + index + " out of valid range 0 to " +
+ (_contentBodies.size() - 1));
+ }
+ WeakReference<ContentChunk> wr = _contentBodies.get(index);
+ ContentChunk cb = wr.get();
+ if (cb == null)
+ {
+ cb = _messageStore.getContentBodyChunk(context, messageId, index);
+ _contentBodies.set(index, new WeakReference<ContentChunk>(cb));
+ }
+ return cb;
+ }
+
+ /**
+ * Content bodies are set <i>before</i> the publish and header frames
+ *
+ * @param storeContext
+ * @param messageId
+ * @param contentChunk
+ * @param isLastContentBody
+ * @throws AMQException
+ */
+ public void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentChunk, boolean isLastContentBody) throws AMQException
+ {
+ if (_contentBodies == null && isLastContentBody)
+ {
+ _contentBodies = new ArrayList<WeakReference<ContentChunk>>(1);
+ }
+ else
+ {
+ if (_contentBodies == null)
+ {
+ _contentBodies = new LinkedList<WeakReference<ContentChunk>>();
+ }
+ }
+ _contentBodies.add(new WeakReference<ContentChunk>(contentChunk));
+ _messageStore.storeContentBodyChunk(storeContext, messageId, _contentBodies.size() - 1,
+ contentChunk, isLastContentBody);
+ }
+
+ public MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException
+ {
+ MessagePublishInfo bpb = (_messagePublishInfo != null ? _messagePublishInfo.get() : null);
+ if (bpb == null)
+ {
+ MessageMetaData mmd = loadMessageMetaData(context, messageId);
+
+ bpb = mmd.getMessagePublishInfo();
+ }
+ return bpb;
+ }
+
+ public boolean isRedelivered()
+ {
+ return _redelivered;
+ }
+
+ public void setRedelivered(boolean redelivered)
+ {
+ _redelivered = redelivered;
+ }
+
+ public boolean isPersistent(StoreContext context, Long messageId) throws AMQException
+ {
+ //todo remove literal values to a constant file such as AMQConstants in common
+ ContentHeaderBody chb = getContentHeaderBody(context, messageId);
+ return chb.properties instanceof BasicContentHeaderProperties &&
+ ((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2;
+ }
+
+ /**
+ * This is called when all the content has been received.
+ *
+ * @param publishBody
+ * @param contentHeaderBody
+ * @throws AMQException
+ */
+ public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo publishBody,
+ ContentHeaderBody contentHeaderBody)
+ throws AMQException
+ {
+ // if there are no content bodies the list will be null so we must
+ // create en empty list here
+ if (contentHeaderBody.bodySize == 0)
+ {
+ _contentBodies = new LinkedList<WeakReference<ContentChunk>>();
+ }
+
+ final long arrivalTime = System.currentTimeMillis();
+
+
+ MessageMetaData mmd = new MessageMetaData(publishBody, contentHeaderBody, _contentBodies.size(), arrivalTime);
+
+ _messageStore.storeMessageMetaData(storeContext, messageId, mmd);
+
+ populateFromMessageMetaData(mmd);
+ }
+
+ public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException
+ {
+ _messageStore.removeMessage(storeContext, messageId);
+ }
+
+ public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
+ {
+ _messageStore.enqueueMessage(storeContext, queue.getName(), messageId);
+ }
+
+ public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
+ {
+ _messageStore.dequeueMessage(storeContext, queue.getName(), messageId);
+ }
+
+ public long getArrivalTime()
+ {
+ return _arrivalTime;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java
new file mode 100644
index 0000000000..6c71571807
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+public interface WeightedSubscriptionManager extends SubscriptionManager
+{
+ public int getWeight();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
new file mode 100644
index 0000000000..22fa0fab23
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
@@ -0,0 +1,213 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.registry;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.configuration.Configurator;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+/**
+ * An abstract application registry that provides access to configuration information and handles the
+ * construction and caching of configurable objects.
+ * <p/>
+ * Subclasses should handle the construction of the "registered objects" such as the exchange registry.
+ */
+public abstract class ApplicationRegistry implements IApplicationRegistry
+{
+ private static final Logger _logger = Logger.getLogger(ApplicationRegistry.class);
+
+ private static Map<Integer, IApplicationRegistry> _instanceMap = new HashMap<Integer, IApplicationRegistry>();
+
+ private final Map<Class<?>, Object> _configuredObjects = new HashMap<Class<?>, Object>();
+
+ protected final Configuration _configuration;
+
+ public static final int DEFAULT_INSTANCE = 1;
+ public static final String DEFAULT_APPLICATION_REGISTRY = "org.apache.qpid.server.util.NullApplicationRegistry";
+ public static String _APPLICATION_REGISTRY = DEFAULT_APPLICATION_REGISTRY;
+
+ static
+ {
+ Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownService()));
+ }
+
+ private static class ShutdownService implements Runnable
+ {
+ public void run()
+ {
+ _logger.info("Shutting down application registries...");
+ try
+ {
+ synchronized (ApplicationRegistry.class)
+ {
+ Iterator<IApplicationRegistry> keyIterator = _instanceMap.values().iterator();
+
+ while (keyIterator.hasNext())
+ {
+ IApplicationRegistry instance = keyIterator.next();
+
+ instance.close();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error shutting down message store: " + e, e);
+ }
+ }
+ }
+
+ public static void initialise(IApplicationRegistry instance) throws Exception
+ {
+ initialise(instance, DEFAULT_INSTANCE);
+ }
+
+ public static void initialise(IApplicationRegistry instance, int instanceID) throws Exception
+ {
+ if (instance != null)
+ {
+ _logger.info("Initialising Application Registry:" + instanceID);
+ _instanceMap.put(instanceID, instance);
+
+ try
+ {
+ instance.initialise();
+ }
+ catch (Exception e)
+ {
+ _instanceMap.remove(instanceID);
+ throw e;
+ }
+ }
+ else
+ {
+ remove(instanceID);
+ }
+ }
+
+ public static void remove(int instanceID)
+ {
+ try
+ {
+ _instanceMap.get(instanceID).close();
+ }
+ catch (Exception e)
+ {
+
+ }
+ finally
+ {
+ _instanceMap.remove(instanceID);
+ }
+ }
+
+
+ protected ApplicationRegistry(Configuration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public static IApplicationRegistry getInstance()
+ {
+ return getInstance(DEFAULT_INSTANCE);
+ }
+
+ public static IApplicationRegistry getInstance(int instanceID)
+ {
+ synchronized (IApplicationRegistry.class)
+ {
+ IApplicationRegistry instance = _instanceMap.get(instanceID);
+
+ if (instance == null)
+ {
+ try
+ {
+ _logger.info("Creating DEFAULT_APPLICATION_REGISTRY: " + _APPLICATION_REGISTRY + " : Instance:" + instanceID);
+ IApplicationRegistry registry = (IApplicationRegistry) Class.forName(_APPLICATION_REGISTRY).getConstructor((Class[]) null).newInstance((Object[]) null);
+ ApplicationRegistry.initialise(registry, instanceID);
+ _logger.info("Initialised Application Registry:" + instanceID);
+ return registry;
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error configuring application: " + e, e);
+ //throw new AMQBrokerCreationException(instanceID, "Unable to create Application Registry instance " + instanceID);
+ throw new RuntimeException("Unable to create Application Registry", e);
+ }
+ }
+ else
+ {
+ return instance;
+ }
+ }
+ }
+
+ public void close() throws Exception
+ {
+ for(VirtualHost virtualHost : getVirtualHostRegistry().getVirtualHosts())
+ {
+ virtualHost.close();
+ }
+
+ // close the rmi registry(if any) started for management
+ if (getInstance().getManagedObjectRegistry() != null)
+ {
+ getInstance().getManagedObjectRegistry().close();
+ }
+ }
+
+ public Configuration getConfiguration()
+ {
+ return _configuration;
+ }
+
+ public <T> T getConfiguredObject(Class<T> instanceType)
+ {
+ T instance = (T) _configuredObjects.get(instanceType);
+ if (instance == null)
+ {
+ 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);
+ _configuredObjects.put(instanceType, instance);
+ }
+ return instance;
+ }
+
+
+
+ public static void setDefaultApplicationRegistry(String clazz)
+ {
+ _APPLICATION_REGISTRY = clazz;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java
new file mode 100644
index 0000000000..1cca259a8d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java
@@ -0,0 +1,176 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.registry;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.SystemConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.management.JMXManagedObjectRegistry;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+import org.apache.qpid.server.management.ManagementConfiguration;
+import org.apache.qpid.server.management.NoopManagedObjectRegistry;
+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.virtualhost.VirtualHost;
+import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+import org.apache.qpid.AMQException;
+
+public class ConfigurationFileApplicationRegistry extends ApplicationRegistry
+{
+
+ private ManagedObjectRegistry _managedObjectRegistry;
+
+ private AuthenticationManager _authenticationManager;
+
+ private AccessManager _accessManager;
+
+ private PrincipalDatabaseManager _databaseManager;
+
+ private VirtualHostRegistry _virtualHostRegistry;
+
+
+ private final Map<String, VirtualHost> _virtualHosts = new ConcurrentHashMap<String, VirtualHost>();
+
+
+ public ConfigurationFileApplicationRegistry(File configurationURL) throws ConfigurationException
+ {
+ super(config(configurationURL));
+ }
+
+ // Our configuration class needs to make the interpolate method
+ // public so it can be called below from the config method.
+ private static class MyConfiguration extends CompositeConfiguration
+ {
+ public String interpolate(String obj)
+ {
+ return super.interpolate(obj);
+ }
+ }
+
+ private static final Configuration config(File url) throws ConfigurationException
+ {
+ // We have to override the interpolate methods so that
+ // interpolation takes place accross the entirety of the
+ // composite configuration. Without doing this each
+ // configuration object only interpolates variables defined
+ // inside itself.
+ final MyConfiguration conf = new MyConfiguration();
+ conf.addConfiguration(new SystemConfiguration()
+ {
+ protected String interpolate(String o)
+ {
+ return conf.interpolate(o);
+ }
+ });
+ conf.addConfiguration(new XMLConfiguration(url)
+ {
+ protected String interpolate(String o)
+ {
+ return conf.interpolate(o);
+ }
+ });
+ return conf;
+ }
+
+ public void initialise() throws Exception
+ {
+ initialiseManagedObjectRegistry();
+
+ _virtualHostRegistry = new VirtualHostRegistry();
+
+ _accessManager = new AccessManagerImpl("default", _configuration);
+
+ _databaseManager = new ConfigurationFilePrincipalDatabaseManager();
+
+ _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null);
+
+ _databaseManager.initialiseManagement(_configuration);
+
+ _managedObjectRegistry.start();
+
+ initialiseVirtualHosts();
+
+ }
+
+ private void initialiseVirtualHosts() throws Exception
+ {
+ for (String name : getVirtualHostNames())
+ {
+
+ _virtualHostRegistry.registerVirtualHost(new VirtualHost(name, getConfiguration().subset("virtualhosts.virtualhost." + name)));
+ }
+ }
+
+ private void initialiseManagedObjectRegistry() throws AMQException
+ {
+ ManagementConfiguration config = getConfiguredObject(ManagementConfiguration.class);
+ if (config.enabled)
+ {
+ _managedObjectRegistry = new JMXManagedObjectRegistry();
+ }
+ else
+ {
+ _managedObjectRegistry = new NoopManagedObjectRegistry();
+ }
+ }
+
+
+ public VirtualHostRegistry getVirtualHostRegistry()
+ {
+ return _virtualHostRegistry;
+ }
+
+ public AccessManager getAccessManager()
+ {
+ return _accessManager;
+ }
+
+ public ManagedObjectRegistry getManagedObjectRegistry()
+ {
+ return _managedObjectRegistry;
+ }
+
+ public PrincipalDatabaseManager getDatabaseManager()
+ {
+ return _databaseManager;
+ }
+
+ public AuthenticationManager getAuthenticationManager()
+ {
+ return _authenticationManager;
+ }
+
+ public Collection<String> getVirtualHostNames()
+ {
+ return getConfiguration().getList("virtualhosts.virtualhost.name");
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java
new file mode 100644
index 0000000000..5a48431288
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.registry;
+
+import java.util.Collection;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+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.virtualhost.VirtualHostRegistry;
+
+public interface IApplicationRegistry
+{
+ /**
+ * Initialise the application registry. All initialisation must be done in this method so that any components
+ * that need access to the application registry itself for initialisation are able to use it. Attempting to
+ * initialise in the constructor will lead to failures since the registry reference will not have been set.
+ */
+ void initialise() throws Exception;
+
+ void close() throws Exception;
+
+ /**
+ * This gets access to a "configured object". A configured object has fields populated from a the configuration
+ * object (Commons Configuration) automatically, where it has the appropriate attributes defined on fields.
+ * Application registry implementations can choose the refresh strategy or caching approach.
+ * @param instanceType the type of object you want initialised. This must be unique - i.e. you can only
+ * have a single object of this type in the system.
+ * @return the configured object
+ */
+ <T> T getConfiguredObject(Class<T> instanceType);
+
+ /**
+ * Get the low level configuration. For use cases where the configured object approach is not required
+ * you can get the complete configuration information.
+ * @return a Commons Configuration instance
+ */
+ Configuration getConfiguration();
+
+ ManagedObjectRegistry getManagedObjectRegistry();
+
+ PrincipalDatabaseManager getDatabaseManager();
+
+ AuthenticationManager getAuthenticationManager();
+
+ Collection<String> getVirtualHostNames();
+
+ VirtualHostRegistry getVirtualHostRegistry();
+
+ AccessManager getAccessManager();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java
new file mode 100644
index 0000000000..2dc7fcbc1e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java
@@ -0,0 +1,467 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management.MBeanDescription;
+import org.apache.qpid.server.management.AMQManagedObject;
+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.log4j.Logger;
+import org.apache.commons.configuration.ConfigurationException;
+
+import javax.management.JMException;
+import javax.management.remote.JMXPrincipal;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.security.auth.login.AccountNotFoundException;
+import javax.security.auth.Subject;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.util.Properties;
+import java.util.List;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+import java.security.Principal;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+
+/** MBean class for AMQUserManagementMBean. It implements all the management features exposed for managing users. */
+@MBeanDescription("User Management Interface")
+public class AMQUserManagementMBean extends AMQManagedObject implements UserManagement
+{
+
+ private static final Logger _logger = Logger.getLogger(AMQUserManagementMBean.class);
+
+ private PrincipalDatabase _principalDatabase;
+ private String _accessFileName;
+ private Properties _accessRights;
+ // private File _accessFile;
+ private ReentrantLock _accessRightsUpdate = new ReentrantLock();
+
+ // Setup for the TabularType
+ static TabularType _userlistDataType; // Datatype for representing User Lists
+
+ static CompositeType _userDataType; // Composite type for representing User
+ static String[] _userItemNames = {"Username", "read", "write", "admin"};
+
+ static
+ {
+ String[] userItemDesc = {"Broker Login username", "Management Console Read Permission",
+ "Management Console Write Permission", "Management Console Admin Permission"};
+
+ OpenType[] userItemTypes = new OpenType[4]; // User item types.
+ userItemTypes[0] = SimpleType.STRING; // For Username
+ userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read
+ userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write
+ userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin
+ String[] userDataIndex = {_userItemNames[0]};
+
+ try
+ {
+ _userDataType =
+ new CompositeType("User", "User Data", _userItemNames, userItemDesc, userItemTypes);
+
+ _userlistDataType = new TabularType("Users", "List of users", _userDataType, userDataIndex);
+ }
+ catch (OpenDataException e)
+ {
+ _logger.error("Tabular data setup for viewing users incorrect.");
+ _userlistDataType = null;
+ }
+ }
+
+
+ public AMQUserManagementMBean() throws JMException
+ {
+ super(UserManagement.class, UserManagement.TYPE);
+ }
+
+ public String getObjectInstanceName()
+ {
+ return UserManagement.TYPE;
+ }
+
+ public boolean setPassword(String username, char[] password)
+ {
+ try
+ {
+ //delegate password changes to the Principal Database
+ return _principalDatabase.updatePassword(new UsernamePrincipal(username), password);
+ }
+ catch (AccountNotFoundException e)
+ {
+ _logger.warn("Attempt to set password of non-existant user'" + username + "'");
+ return false;
+ }
+ }
+
+ public boolean setRights(String username, boolean read, boolean write, boolean admin)
+ {
+
+ if (_accessRights.get(username) == null)
+ {
+ // If the user doesn't exist in the user rights file check that they at least have an account.
+ if (_principalDatabase.getUser(username) == null)
+ {
+ return false;
+ }
+ }
+
+ try
+ {
+
+ _accessRightsUpdate.lock();
+
+ // Update the access rights
+ if (admin)
+ {
+ _accessRights.put(username, MBeanInvocationHandlerImpl.ADMIN);
+ }
+ else
+ {
+ if (read | write)
+ {
+ if (read)
+ {
+ _accessRights.put(username, MBeanInvocationHandlerImpl.READONLY);
+ }
+ if (write)
+ {
+ _accessRights.put(username, MBeanInvocationHandlerImpl.READWRITE);
+ }
+ }
+ else
+ {
+ _accessRights.remove(username);
+ }
+ }
+
+ saveAccessFile();
+ }
+ finally
+ {
+ if (_accessRightsUpdate.isHeldByCurrentThread())
+ {
+ _accessRightsUpdate.unlock();
+ }
+ }
+
+ return true;
+ }
+
+ public boolean createUser(String username, char[] password, boolean read, boolean write, boolean admin)
+ {
+ if (_principalDatabase.createPrincipal(new UsernamePrincipal(username), password))
+ {
+ _accessRights.put(username, "");
+
+ return setRights(username, read, write, admin);
+ }
+
+ return false;
+ }
+
+ public boolean deleteUser(String username)
+ {
+
+ try
+ {
+ if (_principalDatabase.deletePrincipal(new UsernamePrincipal(username)))
+ {
+ try
+ {
+ _accessRightsUpdate.lock();
+
+ _accessRights.remove(username);
+ saveAccessFile();
+ }
+ finally
+ {
+ if (_accessRightsUpdate.isHeldByCurrentThread())
+ {
+ _accessRightsUpdate.unlock();
+ }
+ }
+ return true;
+ }
+ }
+ catch (AccountNotFoundException e)
+ {
+ _logger.warn("Attempt to delete user (" + username + ") that doesn't exist");
+ }
+
+ return false;
+ }
+
+ public boolean reloadData()
+ {
+ try
+ {
+ try
+ {
+ loadAccessFile();
+ }
+ catch (ConfigurationException e)
+ {
+ _logger.info("Reload failed due to:" + e);
+ return false;
+ }
+
+ // Reload successful
+ return true;
+ }
+ catch (IOException e)
+ {
+ _logger.info("Reload failed due to:" + e);
+ // Reload unsuccessful
+ return false;
+ }
+ }
+
+
+ @MBeanOperation(name = "viewUsers", description = "All users with access rights to the system.")
+ public TabularData viewUsers()
+ {
+ // Table of users
+ // Username(string), Access rights Read,Write,Admin(bool,bool,bool)
+
+ if (_userlistDataType == null)
+ {
+ _logger.warn("TabluarData not setup correctly");
+ return null;
+ }
+
+ List<Principal> users = _principalDatabase.getUsers();
+
+ TabularDataSupport userList = new TabularDataSupport(_userlistDataType);
+
+ try
+ {
+ // Create the tabular list of message header contents
+ for (Principal user : users)
+ {
+ // Create header attributes list
+
+ String rights = (String) _accessRights.get(user.getName());
+
+ Boolean read = false;
+ Boolean write = false;
+ Boolean admin = false;
+
+ if (rights != null)
+ {
+ read = rights.equals(MBeanInvocationHandlerImpl.READONLY)
+ || rights.equals(MBeanInvocationHandlerImpl.READWRITE);
+ write = rights.equals(MBeanInvocationHandlerImpl.READWRITE);
+ admin = rights.equals(MBeanInvocationHandlerImpl.ADMIN);
+ }
+
+ Object[] itemData = {user.getName(), read, write, admin};
+ CompositeData messageData = new CompositeDataSupport(_userDataType, _userItemNames, itemData);
+ userList.put(messageData);
+ }
+ }
+ catch (OpenDataException e)
+ {
+ _logger.warn("Unable to create user list due to :" + e);
+ return null;
+ }
+
+ return userList;
+ }
+
+ /*** Broker Methods **/
+
+ /**
+ * setPrincipalDatabase
+ *
+ * @param database set The Database to use for user lookup
+ */
+ public void setPrincipalDatabase(PrincipalDatabase database)
+ {
+ _principalDatabase = database;
+ }
+
+ /**
+ * setAccessFile
+ *
+ * @param accessFile the file to use for updating.
+ *
+ * @throws java.io.IOException If the file cannot be accessed
+ * @throws org.apache.commons.configuration.ConfigurationException
+ * if checks on the file fail.
+ */
+ public void setAccessFile(String accessFile) throws IOException, ConfigurationException
+ {
+ _accessFileName = accessFile;
+
+ if (_accessFileName != null)
+ {
+ loadAccessFile();
+ }
+ else
+ {
+ _logger.warn("Access rights file specified is null. Access rights not changed.");
+ }
+ }
+
+ private void loadAccessFile() throws IOException, ConfigurationException
+ {
+ try
+ {
+ _accessRightsUpdate.lock();
+
+ Properties accessRights = new Properties();
+
+ File accessFile = new File(_accessFileName);
+
+ if (!accessFile.exists())
+ {
+ throw new ConfigurationException("'" + _accessFileName + "' does not exist");
+ }
+
+ if (!accessFile.canRead())
+ {
+ throw new ConfigurationException("Cannot read '" + _accessFileName + "'.");
+ }
+
+ if (!accessFile.canWrite())
+ {
+ _logger.warn("Unable to write to access file '" + _accessFileName + "' changes will not be preserved.");
+ }
+
+ accessRights.load(new FileInputStream(accessFile));
+ checkAccessRights(accessRights);
+ setAccessRights(accessRights);
+ }
+ finally
+ {
+ if (_accessRightsUpdate.isHeldByCurrentThread())
+ {
+ _accessRightsUpdate.unlock();
+ }
+ }
+ }
+
+ private void checkAccessRights(Properties accessRights)
+ {
+ Enumeration values = accessRights.propertyNames();
+
+ while (values.hasMoreElements())
+ {
+ String user = (String) values.nextElement();
+
+ if (_principalDatabase.getUser(user) == null)
+ {
+ _logger.warn("Access rights contains user '" + user + "' but there is no authentication data for that user");
+ }
+ }
+ }
+
+ private void saveAccessFile()
+ {
+ try
+ {
+ _accessRightsUpdate.lock();
+ try
+ {
+ // remove old temporary file
+ File tmp = new File(_accessFileName + ".tmp");
+ if (tmp.exists())
+ {
+ tmp.delete();
+ }
+
+ //remove old backup
+ File old = new File(_accessFileName + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+
+ // Rename current file
+ File rights = new File(_accessFileName);
+ rights.renameTo(old);
+
+ FileOutputStream output = new FileOutputStream(tmp);
+ _accessRights.store(output, "Generated by AMQUserManagementMBean Console : Last edited by user:" + getCurrentJMXUser());
+ output.close();
+
+ // Rename new file to main file
+ tmp.renameTo(rights);
+
+ // delete tmp
+ tmp.delete();
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Problem occured saving '" + _accessFileName + "' changes may not be preserved. :" + e);
+ }
+ }
+ finally
+ {
+ if (_accessRightsUpdate.isHeldByCurrentThread())
+ {
+ _accessRightsUpdate.unlock();
+ }
+ }
+ }
+
+ private String getCurrentJMXUser()
+ {
+ AccessControlContext acc = AccessController.getContext();
+ Subject subject = Subject.getSubject(acc);
+
+ // Retrieve JMXPrincipal from Subject
+ Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class);
+ if (principals == null || principals.isEmpty())
+ {
+ return "Unknown user principals were null";
+ }
+
+ Principal principal = principals.iterator().next();
+ return principal.getName();
+ }
+
+ /**
+ * user=read user=write user=readwrite user=admin
+ *
+ * @param accessRights The properties list of access rights to process
+ */
+ private void setAccessRights(Properties accessRights)
+ {
+ _logger.debug("Setting Access Rights:" + accessRights);
+ _accessRights = accessRights;
+ MBeanInvocationHandlerImpl.setAccessRights(_accessRights);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java
new file mode 100644
index 0000000000..d70a6dc8f4
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.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();
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java
new file mode 100644
index 0000000000..35d036d20f
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.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.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
+{
+ private static final Logger _logger = Logger.getLogger(AccessManagerImpl.class);
+
+ AccessManager _accessManager;
+
+ public AccessManagerImpl(String name, Configuration hostConfig) throws ConfigurationException
+ {
+ if (hostConfig == null)
+ {
+ _logger.warn("No Configuration specified. Using default access controls for VirtualHost:'" + name + "'");
+ return;
+ }
+
+ String accessClass = hostConfig.getString("security.access.class");
+ if (accessClass == null)
+ {
+ _logger.warn("No access control specified. Using default access controls for VirtualHost:'" + name + "'");
+ return;
+ }
+
+ Object o;
+ try
+ {
+ o = Class.forName(accessClass).newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Error initialising access control: " + e, e);
+ }
+
+ if (!(o instanceof AccessManager))
+ {
+ throw new ConfigurationException("Access control must implement the VirtualHostAccess interface");
+ }
+
+ initialiseAccessControl((AccessManager) o, hostConfig);
+
+ _accessManager = (AccessManager) o;
+
+ _logger.info("Initialised access control for virtualhost '" + name + "' successfully");
+
+ }
+
+
+ private void initialiseAccessControl(AccessManager accessManager, Configuration config)
+ throws ConfigurationException
+ {
+ String baseName = "security.access.attributes.attribute.";
+ List<String> argumentNames = config.getList(baseName + "name");
+ List<String> argumentValues = config.getList(baseName + "value");
+ for (int i = 0; i < argumentNames.size(); i++)
+ {
+ String argName = argumentNames.get(i);
+ if (argName == null || argName.length() == 0)
+ {
+ throw new ConfigurationException("Access Control argument names must have length >= 1 character");
+ }
+ if (Character.isLowerCase(argName.charAt(0)))
+ {
+ argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1);
+ }
+ String methodName = "set" + argName;
+ Method method = null;
+ try
+ {
+ method = accessManager.getClass().getMethod(methodName, String.class);
+ }
+ catch (NoSuchMethodException e)
+ {
+ //do nothing as method will be null
+ }
+
+ if (method == null)
+ {
+ throw new ConfigurationException("No method " + methodName + " found in class " + accessManager.getClass() +
+ " hence unable to configure access control. The method must be public and " +
+ "have a single String argument with a void return type");
+ }
+ try
+ {
+ method.invoke(accessManager, PropertyUtils.replaceProperties(argumentValues.get(i)));
+ }
+ catch (Exception e)
+ {
+ ConfigurationException ce = new ConfigurationException(e.getMessage(), e.getCause());
+ ce.initCause(e);
+ throw ce;
+ }
+ }
+ }
+
+ 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 (_accessManager == null)
+ {
+ if (ApplicationRegistry.getInstance().getAccessManager() == this)
+ {
+ _logger.warn("No Default access manager specified DENYING ALL ACCESS");
+ return new AccessResult(this, AccessResult.AccessStatus.REFUSED);
+ }
+ else
+ {
+ return ApplicationRegistry.getInstance().getAccessManager().isAuthorized(accessObject, user, rights);
+ }
+ }
+ else
+ {
+ return _accessManager.isAuthorized(accessObject, user, rights);
+ }
+ }
+
+ public String getName()
+ {
+ return "AccessManagerImpl";
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java
new file mode 100644
index 0000000000..b8d8fc605a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.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.server.security.access;
+
+public class AccessResult
+{
+ public enum AccessStatus
+ {
+ GRANTED, REFUSED
+ }
+
+ StringBuilder _authorizer;
+ AccessStatus _status;
+
+ public AccessResult(AccessManager authorizer, AccessStatus status)
+ {
+ _status = status;
+ _authorizer = new StringBuilder(authorizer.getName());
+ }
+
+ public void setAuthorizer(AccessManager authorizer)
+ {
+ _authorizer.append(authorizer.getName());
+ }
+
+ public String getAuthorizer()
+ {
+ return _authorizer.toString();
+ }
+
+ public void setStatus(AccessStatus status)
+ {
+ _status = status;
+ }
+
+ public AccessStatus getStatus()
+ {
+ return _status;
+ }
+
+ public void addAuthorizer(AccessManager accessManager)
+ {
+ _authorizer.insert(0, "->");
+ _authorizer.insert(0, accessManager.getName());
+ }
+
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java
new file mode 100644
index 0000000000..1b79a5a0e0
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+public class AccessRights
+{
+ public enum Rights
+ {
+ ANY,
+ READ,
+ WRITE,
+ READWRITE
+ }
+
+ Rights _right;
+
+ public AccessRights(Rights right)
+ {
+ _right = right;
+ }
+
+ public boolean allows(Rights rights)
+ {
+ switch (_right)
+ {
+ case ANY:
+ return (rights.equals(Rights.WRITE)
+ || rights.equals(Rights.READ)
+ || rights.equals(Rights.READWRITE)
+ || rights.equals(Rights.ANY));
+ case READ:
+ return rights.equals(Rights.READ) || rights.equals(Rights.ANY);
+ case WRITE:
+ return rights.equals(Rights.WRITE) || rights.equals(Rights.ANY);
+ case READWRITE:
+ return true;
+ }
+ return false;
+ }
+
+ public Rights getRights()
+ {
+ return _right;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java
new file mode 100644
index 0000000000..f51cf24caa
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.access;
+
+public interface Accessable
+{
+ void setAccessableName(String name);
+ String getAccessableName();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java
new file mode 100644
index 0000000000..1ddca3a64e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.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/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java
new file mode 100644
index 0000000000..bf40eeba4e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java
new file mode 100644
index 0000000000..291bc714ed
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java
new file mode 100644
index 0000000000..6ccadb2e7d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java
new file mode 100644
index 0000000000..b8762aa43b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.management.MBeanOperation;
+import org.apache.qpid.server.management.MBeanOperationParameter;
+import org.apache.qpid.server.management.MBeanAttribute;
+import org.apache.qpid.AMQException;
+
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.CompositeData;
+import javax.management.JMException;
+import javax.management.MBeanOperationInfo;
+import java.io.IOException;
+
+public interface UserManagement
+{
+ String TYPE = "UserManagement";
+
+ //********** Operations *****************//
+ /**
+ * set password for user
+ *
+ * @param username The username to create
+ * @param password The password for the user
+ *
+ * @return The result of the operation
+ */
+ @MBeanOperation(name = "setPassword", description = "Set password for user.",
+ impact = MBeanOperationInfo.ACTION)
+ boolean setPassword(@MBeanOperationParameter(name = "username", description = "Username")String username,
+ @MBeanOperationParameter(name = "password", description = "Password")char[] password);
+
+ /**
+ * set rights for users with given details
+ *
+ * @param username The username to create
+ * @param read The set of permission to give the new user
+ * @param write The set of permission to give the new user
+ * @param admin The set of permission to give the new user
+ *
+ * @return The result of the operation
+ */
+ @MBeanOperation(name = "setRights", description = "Set access rights for user.",
+ impact = MBeanOperationInfo.ACTION)
+ boolean setRights(@MBeanOperationParameter(name = "username", description = "Username")String username,
+ @MBeanOperationParameter(name = "read", description = "Administration read")boolean read,
+ @MBeanOperationParameter(name = "readAndWrite", description = "Administration write")boolean write,
+ @MBeanOperationParameter(name = "admin", description = "Administration rights")boolean admin);
+
+ /**
+ * Create users with given details
+ *
+ * @param username The username to create
+ * @param password The password for the user
+ * @param read The set of permission to give the new user
+ * @param write The set of permission to give the new user
+ * @param admin The set of permission to give the new user
+ *
+ * @return The result of the operation
+ */
+ @MBeanOperation(name = "createUser", description = "Create new user from system.",
+ impact = MBeanOperationInfo.ACTION)
+ boolean createUser(@MBeanOperationParameter(name = "username", description = "Username")String username,
+ @MBeanOperationParameter(name = "password", description = "Password")char[] password,
+ @MBeanOperationParameter(name = "read", description = "Administration read")boolean read,
+ @MBeanOperationParameter(name = "readAndWrite", description = "Administration write")boolean write,
+ @MBeanOperationParameter(name = "admin", description = "Administration rights")boolean admin);
+
+ /**
+ * View users returns all the users that are currently available to the system.
+ *
+ * @param username The user to delete
+ *
+ * @return The result of the operation
+ */
+ @MBeanOperation(name = "deleteUser", description = "Delete user from system.",
+ impact = MBeanOperationInfo.ACTION)
+ boolean deleteUser(@MBeanOperationParameter(name = "username", description = "Username")String username);
+
+
+ /**
+ * Reload the date from disk
+ *
+ * @return The result of the operation
+ */
+ @MBeanOperation(name = "reloadData", description = "Reload the authentication file from disk.",
+ impact = MBeanOperationInfo.ACTION)
+ boolean reloadData();
+
+ /**
+ * View users returns all the users that are currently available to the system.
+ *
+ * @return a table of users data (Username, read, write, admin)
+ */
+ @MBeanOperation(name = "viewUsers", description = "All users with access rights to the system.",
+ impact = MBeanOperationInfo.INFO)
+ TabularData viewUsers();
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java
new file mode 100644
index 0000000000..13151a66b8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.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;
+
+public class VirtualHostAccess
+{
+ private String _vhost;
+ private AccessRights _rights;
+
+ public VirtualHostAccess(String vhostaccess)
+ {
+ //format <vhost>(<rights>)
+ int hostend = vhostaccess.indexOf('(');
+
+ if (hostend == -1)
+ {
+ throw new IllegalArgumentException("VirtualHostAccess format string contains no access _rights");
+ }
+
+ _vhost = vhostaccess.substring(0, hostend);
+
+ String rights = vhostaccess.substring(hostend);
+
+ if (rights.indexOf('r') != -1)
+ {
+ if (rights.indexOf('w') != -1)
+ {
+ _rights = new AccessRights(AccessRights.Rights.READWRITE);
+ }
+ else
+ {
+ _rights = new AccessRights(AccessRights.Rights.READ);
+ }
+ }
+ else if (rights.indexOf('w') != -1)
+ {
+ _rights = new AccessRights(AccessRights.Rights.WRITE);
+ }
+ }
+
+ public AccessRights getAccessRights()
+ {
+ return _rights;
+ }
+
+ public String getVirtualHost()
+ {
+ return _vhost;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java
new file mode 100644
index 0000000000..0e3aea4de0
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.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.server.security.auth;
+
+public class AuthenticationResult
+{
+ public enum AuthenticationStatus
+ {
+ SUCCESS, CONTINUE, ERROR
+ }
+
+ public AuthenticationStatus status;
+ public byte[] challenge;
+
+ public AuthenticationResult(byte[] challenge, AuthenticationStatus status)
+ {
+ this.status = status;
+ this.challenge = challenge;
+ }
+
+ public AuthenticationResult(AuthenticationStatus status)
+ {
+ this.status = status;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
new file mode 100644
index 0000000000..10adfdd9fc
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
@@ -0,0 +1,599 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.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.commons.codec.binary.Base64;
+import org.apache.commons.codec.EncoderException;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.UnsupportedEncodingException;
+import java.io.PrintStream;
+import java.util.regex.Pattern;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+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.
+ *
+ * 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 Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
+{
+ private static final Logger _logger = Logger.getLogger(Base64MD5PasswordFilePrincipalDatabase.class);
+
+ private File _passwordFile;
+
+ private Pattern _regexp = Pattern.compile(":");
+
+ private Map<String, AuthenticationProviderInitialiser> _saslServers;
+
+ AMQUserManagementMBean _mbean;
+ private static final String DEFAULT_ENCODING = "utf-8";
+ private Map<String, User> _users = new HashMap<String, User>();
+ private ReentrantLock _userUpdate = new ReentrantLock();
+
+ public Base64MD5PasswordFilePrincipalDatabase()
+ {
+ _saslServers = new HashMap<String, AuthenticationProviderInitialiser>();
+
+ /**
+ * Create Authenticators for MD5 Password file.
+ */
+
+ // Accept Plain incomming and hash it for comparison to the file.
+ CRAMMD5HashedInitialiser cram = new CRAMMD5HashedInitialiser();
+ cram.initialise(this);
+ _saslServers.put(cram.getMechanismName(), cram);
+
+ //fixme The PDs should setup a PD Mangement MBean
+// try
+// {
+// _mbean = new AMQUserManagementMBean();
+// _mbean.setPrincipalDatabase(this);
+// }
+// catch (JMException e)
+// {
+// _logger.warn("User management disabled as unable to create MBean:" + e);
+// }
+ }
+
+ public void setPasswordFile(String passwordFile) throws IOException
+ {
+ File f = new File(passwordFile);
+ _logger.info("PasswordFilePrincipalDatabase using file " + f.getAbsolutePath());
+ _passwordFile = f;
+ if (!f.exists())
+ {
+ throw new FileNotFoundException("Cannot find password file " + f);
+ }
+ if (!f.canRead())
+ {
+ throw new FileNotFoundException("Cannot read password file " + f +
+ ". Check permissions.");
+ }
+
+ loadPasswordFile();
+ }
+
+ /**
+ * SASL Callback Mechanism - sets the Password in the PasswordCallback based on the value in the PasswordFile
+ *
+ * @param principal The Principal to set the password for
+ * @param callback The PasswordCallback to call setPassword on
+ *
+ * @throws AccountNotFoundException If the Principal cannont be found in this Database
+ */
+ public void setPassword(Principal principal, PasswordCallback callback) throws AccountNotFoundException
+ {
+ if (_passwordFile == null)
+ {
+ throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation");
+ }
+ if (principal == null)
+ {
+ throw new IllegalArgumentException("principal must not be null");
+ }
+
+ char[] pwd = lookupPassword(principal.getName());
+
+ if (pwd != null)
+ {
+ callback.setPassword(pwd);
+ }
+ else
+ {
+ throw new AccountNotFoundException("No account found for principal " + principal);
+ }
+ }
+
+ /**
+ * Used to verify that the presented Password is correct. Currently only used by Management Console
+ *
+ * @param principal The principal to authenticate
+ * @param password The password to check
+ *
+ * @return true if password is correct
+ *
+ * @throws AccountNotFoundException if the principal cannot be found
+ */
+ public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException
+ {
+ char[] pwd = lookupPassword(principal);
+
+ int index = 0;
+ boolean verified = true;
+
+ while (verified & index < password.length)
+ {
+ verified = (pwd[index] == password[index]);
+ index++;
+ }
+ return verified;
+ }
+
+ public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException
+ {
+ User user = _users.get(principal.getName());
+
+ if (user == null)
+ {
+ throw new AccountNotFoundException(principal.getName());
+ }
+
+ try
+ {
+ try
+ {
+ _userUpdate.lock();
+ char[] orig = user.getPassword();
+ user.setPassword(password);
+
+ try
+ {
+ savePasswordFile();
+ }
+ catch (IOException e)
+ {
+ _logger.error("Unable to save password file, password change for user'"
+ + principal + "' will revert at restart");
+ //revert the password change
+ user.setPassword(orig);
+ return false;
+ }
+ return true;
+ }
+ finally
+ {
+ if (_userUpdate.isHeldByCurrentThread())
+ {
+ _userUpdate.unlock();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ public boolean createPrincipal(Principal principal, char[] password)
+ {
+ if (_users.get(principal.getName()) != null)
+ {
+ return false;
+ }
+
+ User user = new User(principal.getName(), password);
+
+ try
+ {
+ _userUpdate.lock();
+ _users.put(user.getName(), user);
+
+ try
+ {
+ savePasswordFile();
+ return true;
+ }
+ catch (IOException e)
+ {
+ //remove the use on failure.
+ _users.remove(user.getName());
+ return false;
+ }
+ }
+ finally
+ {
+ if (_userUpdate.isHeldByCurrentThread())
+ {
+ _userUpdate.unlock();
+ }
+ }
+ }
+
+ public boolean deletePrincipal(Principal principal) throws AccountNotFoundException
+ {
+ User user = _users.get(principal.getName());
+
+ if (user == null)
+ {
+ throw new AccountNotFoundException(principal.getName());
+ }
+
+ try
+ {
+ _userUpdate.lock();
+ user.delete();
+
+ try
+ {
+ savePasswordFile();
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Unable to remove user '" + user.getName() + "' from password file.");
+ return false;
+ }
+
+ _users.remove(user.getName());
+ }
+ finally
+ {
+ if (_userUpdate.isHeldByCurrentThread())
+ {
+ _userUpdate.unlock();
+ }
+ }
+
+ return true;
+ }
+
+
+ public Map<String, AuthenticationProviderInitialiser> getMechanisms()
+ {
+ return _saslServers;
+ }
+
+ public List<Principal> getUsers()
+ {
+ return new LinkedList<Principal>(_users.values());
+ }
+
+ public Principal getUser(String username)
+ {
+ if (_users.containsKey(username))
+ {
+ return new UsernamePrincipal(username);
+ }
+ return null;
+ }
+
+ /**
+ * Looks up the password for a specified user in the password file. Note this code is <b>not</b> secure since it
+ * creates strings of passwords. It should be modified to create only char arrays which get nulled out.
+ *
+ * @param name The principal name to lookup
+ *
+ * @return a char[] for use in SASL.
+ */
+ private char[] lookupPassword(String name)
+ {
+ User user = _users.get(name);
+ if (user == null)
+ {
+ return null;
+ }
+ else
+ {
+ return user.getPassword();
+ }
+ }
+
+
+ private void loadPasswordFile() throws IOException
+ {
+ try
+ {
+ _userUpdate.lock();
+ _users.clear();
+
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new FileReader(_passwordFile));
+ String line;
+
+ while ((line = reader.readLine()) != null)
+ {
+ String[] result = _regexp.split(line);
+ if (result == null || result.length < 2 || result[0].startsWith("#"))
+ {
+ continue;
+ }
+
+ User user = new User(result);
+ _logger.info("Created user:" + user);
+ _users.put(user.getName(), user);
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ }
+ finally
+ {
+ if (_userUpdate.isHeldByCurrentThread())
+ {
+ _userUpdate.unlock();
+ }
+ }
+ }
+
+ private void savePasswordFile() throws IOException
+ {
+ try
+ {
+ _userUpdate.lock();
+
+ BufferedReader reader = null;
+ PrintStream writer = null;
+ File tmp = new File(_passwordFile.getAbsolutePath() + ".tmp");
+ if (tmp.exists())
+ {
+ tmp.delete();
+ }
+ try
+ {
+ writer = new PrintStream(tmp);
+ reader = new BufferedReader(new FileReader(_passwordFile));
+ String line;
+
+ while ((line = reader.readLine()) != null)
+ {
+ String[] result = _regexp.split(line);
+ if (result == null || result.length < 2 || result[0].startsWith("#"))
+ {
+ writer.write(line.getBytes(DEFAULT_ENCODING));
+ continue;
+ }
+
+ User user = _users.get(result[0]);
+
+ if (user == null)
+ {
+ writer.write(line.getBytes(DEFAULT_ENCODING));
+ writer.println();
+ }
+ else if (!user.isDeleted())
+ {
+ if (!user.isModified())
+ {
+ writer.write(line.getBytes(DEFAULT_ENCODING));
+ writer.println();
+ }
+ else
+ {
+ try
+ {
+ byte[] encodedPassword = user.getEncodePassword();
+
+ writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING));
+ writer.write(encodedPassword);
+ writer.println();
+
+ user.saved();
+ }
+ catch (Exception e)
+ {
+ _logger.warn("Unable to encode new password reverting to old password.");
+ writer.write(line.getBytes(DEFAULT_ENCODING));
+ writer.println();
+ }
+ }
+ }
+ }
+
+ for (User user : _users.values())
+ {
+ if (user.isModified())
+ {
+ byte[] encodedPassword;
+ try
+ {
+ encodedPassword = user.getEncodePassword();
+ writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING));
+ writer.write(encodedPassword);
+ writer.println();
+ user.saved();
+ }
+ catch (Exception e)
+ {
+ _logger.warn("Unable to get Encoded password for user'" + user.getName() + "' password not saved");
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+
+ if (writer != null)
+ {
+ writer.close();
+ }
+
+ // Swap temp file to main password file.
+ File old = new File(_passwordFile.getAbsoluteFile() + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+ _passwordFile.renameTo(old);
+ tmp.renameTo(_passwordFile);
+ tmp.delete();
+ }
+ }
+ finally
+ {
+ if (_userUpdate.isHeldByCurrentThread())
+ {
+ _userUpdate.unlock();
+ }
+ }
+ }
+
+ private class User implements Principal
+ {
+ String _name;
+ char[] _password;
+ byte[] _encodedPassword = null;
+ private boolean _modified = false;
+ private boolean _deleted = false;
+
+ User(String[] data) throws UnsupportedEncodingException
+ {
+ if (data.length != 2)
+ {
+ throw new IllegalArgumentException("User Data should be lenght 2, username, password");
+ }
+
+ _name = data[0];
+
+ byte[] encoded_password = data[1].getBytes(DEFAULT_ENCODING);
+
+ Base64 b64 = new Base64();
+ byte[] decoded = b64.decode(encoded_password);
+
+ _encodedPassword = encoded_password;
+
+ _password = new char[decoded.length];
+
+ int index = 0;
+ for (byte c : decoded)
+ {
+ _password[index++] = (char) c;
+ }
+ }
+
+ public User(String name, char[] password)
+ {
+ _name = name;
+ setPassword(password);
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public String toString()
+ {
+ if (_logger.isDebugEnabled())
+ {
+ return getName() + ((_encodedPassword == null) ? "" : ":" + new String(_encodedPassword));
+ }
+ else
+ {
+ return _name;
+ }
+ }
+
+ char[] getPassword()
+ {
+ return _password;
+ }
+
+ void setPassword(char[] password)
+ {
+ _password = password;
+ _modified = true;
+ _encodedPassword = null;
+ }
+
+
+ byte[] getEncodePassword() throws EncoderException, UnsupportedEncodingException, NoSuchAlgorithmException
+ {
+ if (_encodedPassword == null)
+ {
+ encodePassword();
+ }
+ return _encodedPassword;
+ }
+
+ private void encodePassword() throws EncoderException, UnsupportedEncodingException, NoSuchAlgorithmException
+ {
+ byte[] byteArray = new byte[_password.length];
+ int index = 0;
+ for (char c : _password)
+ {
+ byteArray[index++] = (byte) c;
+ }
+ _encodedPassword = (new Base64()).encode(byteArray);
+ }
+
+ public boolean isModified()
+ {
+ return _modified;
+ }
+
+ public boolean isDeleted()
+ {
+ return _deleted;
+ }
+
+ public void delete()
+ {
+ _deleted = true;
+ }
+
+ public void saved()
+ {
+ _modified = false;
+ }
+
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java
new file mode 100644
index 0000000000..2d3f5e5131
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java
@@ -0,0 +1,236 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.configuration.PropertyUtils;
+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.AMQException;
+
+import javax.management.JMException;
+
+public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatabaseManager
+{
+ private static final Logger _logger = Logger.getLogger(ConfigurationFilePrincipalDatabaseManager.class);
+
+ private static final String _base = "security.principal-databases.principal-database";
+
+ Map<String, PrincipalDatabase> _databases;
+
+ public ConfigurationFilePrincipalDatabaseManager() throws Exception
+ {
+ _logger.info("Initialising PrincipleDatabase authentication manager");
+ _databases = initialisePrincipalDatabases();
+ }
+
+ private Map<String, PrincipalDatabase> initialisePrincipalDatabases() throws Exception
+ {
+ Configuration config = ApplicationRegistry.getInstance().getConfiguration();
+ List<String> databaseNames = config.getList(_base + ".name");
+ List<String> databaseClasses = config.getList(_base + ".class");
+ Map<String, PrincipalDatabase> databases = new HashMap<String, PrincipalDatabase>();
+
+ if (databaseNames.size() == 0)
+ {
+ _logger.warn("No Principal databases specified. Broker running with NO AUTHENTICATION");
+ }
+
+ for (int i = 0; i < databaseNames.size(); i++)
+ {
+ Object o;
+ try
+ {
+ o = Class.forName(databaseClasses.get(i)).newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new Exception("Error initialising principal database: " + e, e);
+ }
+
+ if (!(o instanceof PrincipalDatabase))
+ {
+ throw new Exception("Principal databases must implement the PrincipalDatabase interface");
+ }
+
+ initialisePrincipalDatabase((PrincipalDatabase) o, config, i);
+
+ String name = databaseNames.get(i);
+ if ((name == null) || (name.length() == 0))
+ {
+ throw new Exception("Principal database names must have length greater than or equal to one character");
+ }
+
+ PrincipalDatabase pd = databases.get(name);
+ if (pd != null)
+ {
+ throw new Exception("Duplicate principal database name not provided");
+ }
+
+ _logger.info("Initialised principal database '" + name + "' successfully");
+ databases.put(name, (PrincipalDatabase) o);
+ }
+
+ return databases;
+ }
+
+ private void initialisePrincipalDatabase(PrincipalDatabase principalDatabase, Configuration config, int index)
+ throws FileNotFoundException, ConfigurationException
+ {
+ String baseName = _base + "(" + index + ").attributes.attribute.";
+ List<String> argumentNames = config.getList(baseName + "name");
+ List<String> argumentValues = config.getList(baseName + "value");
+ for (int i = 0; i < argumentNames.size(); i++)
+ {
+ String argName = argumentNames.get(i);
+ if ((argName == null) || (argName.length() == 0))
+ {
+ throw new ConfigurationException("Argument names must have length >= 1 character");
+ }
+
+ if (Character.isLowerCase(argName.charAt(0)))
+ {
+ argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1);
+ }
+
+ String methodName = "set" + argName;
+ Method method = null;
+ try
+ {
+ method = principalDatabase.getClass().getMethod(methodName, String.class);
+ }
+ catch (Exception e)
+ {
+ // do nothing.. as on error method will be null
+ }
+
+ if (method == null)
+ {
+ throw new ConfigurationException("No method " + methodName + " found in class "
+ + principalDatabase.getClass()
+ + " hence unable to configure principal database. The method must be public and "
+ + "have a single String argument with a void return type");
+ }
+
+ try
+ {
+ method.invoke(principalDatabase, PropertyUtils.replaceProperties(argumentValues.get(i)));
+ }
+ catch (Exception ite)
+ {
+ if (ite instanceof ConfigurationException)
+ {
+ throw(ConfigurationException) ite;
+ }
+ else
+ {
+ throw new ConfigurationException(ite.getMessage(), ite);
+ }
+ }
+ }
+ }
+
+ public Map<String, PrincipalDatabase> getDatabases()
+ {
+ return _databases;
+ }
+
+ public void initialiseManagement(Configuration config) throws ConfigurationException
+ {
+ try
+ {
+ AMQUserManagementMBean _mbean = new AMQUserManagementMBean();
+
+ String baseSecurity = "security.jmx";
+ List<String> principalDBs = config.getList(baseSecurity + ".principal-database");
+
+ if (principalDBs.size() == 0)
+ {
+ throw new ConfigurationException("No principal-database specified for jmx security(" + baseSecurity + ".principal-database)");
+ }
+
+ String databaseName = principalDBs.get(0);
+
+ PrincipalDatabase database = getDatabases().get(databaseName);
+
+ if (database == null)
+ {
+ throw new ConfigurationException("Principal-database '" + databaseName + "' not found");
+ }
+
+ _mbean.setPrincipalDatabase(database);
+
+ List<String> jmxaccesslist = config.getList(baseSecurity + ".access");
+
+ if (jmxaccesslist.size() == 0)
+ {
+ throw new ConfigurationException("No access control files specified for jmx security(" + baseSecurity + ".access)");
+ }
+
+ String jmxaccesssFile = null;
+
+ try
+ {
+ jmxaccesssFile = PropertyUtils.replaceProperties(jmxaccesslist.get(0));
+ }
+ catch (PropertyException e)
+ {
+ throw new ConfigurationException("Unable to parse access control filename '" + jmxaccesssFile + "'");
+ }
+
+ try
+ {
+ _mbean.setAccessFile(jmxaccesssFile);
+ }
+ catch (IOException e)
+ {
+ _logger.warn("Unable to load access file:" + jmxaccesssFile);
+ }
+
+ try
+ {
+ _mbean.register();
+ }
+ catch (AMQException e)
+ {
+ _logger.warn("Unable to register user management MBean");
+ }
+ }
+ catch (JMException e)
+ {
+ _logger.warn("User management disabled as unable to create MBean:" + e);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java
new file mode 100644
index 0000000000..352d41a0ba
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.auth.database;
+
+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.amqplain.AmqPlainInitialiser;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser;
+import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * 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 PlainPasswordFilePrincipalDatabase implements PrincipalDatabase
+{
+ private static final Logger _logger = Logger.getLogger(PlainPasswordFilePrincipalDatabase.class);
+
+ protected File _passwordFile;
+
+ protected Pattern _regexp = Pattern.compile(":");
+
+ protected Map<String, AuthenticationProviderInitialiser> _saslServers;
+
+ public PlainPasswordFilePrincipalDatabase()
+ {
+ _saslServers = new HashMap<String, AuthenticationProviderInitialiser>();
+
+ /**
+ * Create Authenticators for Plain Password file.
+ */
+
+ // Accept AMQPlain incomming and compare it to the file.
+ AmqPlainInitialiser amqplain = new AmqPlainInitialiser();
+ amqplain.initialise(this);
+
+ // Accept Plain incomming and compare it to the file.
+ PlainInitialiser plain = new PlainInitialiser();
+ plain.initialise(this);
+
+ // Accept MD5 incomming and Hash file value for comparison
+ CRAMMD5Initialiser cram = new CRAMMD5Initialiser();
+ cram.initialise(this);
+
+ _saslServers.put(amqplain.getMechanismName(), amqplain);
+ _saslServers.put(plain.getMechanismName(), plain);
+ _saslServers.put(cram.getMechanismName(), cram);
+ }
+
+ public void setPasswordFile(String passwordFile) throws FileNotFoundException
+ {
+ File f = new File(passwordFile);
+ _logger.info("PlainPasswordFile using file " + f.getAbsolutePath());
+ _passwordFile = f;
+ if (!f.exists())
+ {
+ throw new FileNotFoundException("Cannot find password file " + f);
+ }
+ if (!f.canRead())
+ {
+ throw new FileNotFoundException("Cannot read password file " + f +
+ ". Check permissions.");
+ }
+ }
+
+ public void setPassword(Principal principal, PasswordCallback callback) throws IOException,
+ AccountNotFoundException
+ {
+ if (_passwordFile == null)
+ {
+ throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation");
+ }
+ if (principal == null)
+ {
+ throw new IllegalArgumentException("principal must not be null");
+ }
+ char[] pwd = lookupPassword(principal.getName());
+ if (pwd != null)
+ {
+ callback.setPassword(pwd);
+ }
+ else
+ {
+ throw new AccountNotFoundException("No account found for principal " + principal);
+ }
+ }
+
+ public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException
+ {
+ try
+ {
+ char[] pwd = lookupPassword(principal);
+
+ return compareCharArray(pwd, password);
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ }
+
+ public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException
+ {
+ return false; // updates denied
+ }
+
+ public boolean createPrincipal(Principal principal, char[] password)
+ {
+ return false; // updates denied
+ }
+
+ public boolean deletePrincipal(Principal principal) throws AccountNotFoundException
+ {
+ return false; // updates denied
+ }
+
+ public Map<String, AuthenticationProviderInitialiser> getMechanisms()
+ {
+ return _saslServers;
+ }
+
+ public List<Principal> getUsers()
+ {
+ return new LinkedList<Principal>(); //todo
+ }
+
+ public Principal getUser(String username)
+ {
+ try
+ {
+ if (lookupPassword(username) != null)
+ {
+ return new UsernamePrincipal(username);
+ }
+ }
+ catch (IOException e)
+ {
+ //fall through to null return
+ }
+ return null;
+ }
+
+ private boolean compareCharArray(char[] a, char[] b)
+ {
+ boolean equal = false;
+ if (a.length == b.length)
+ {
+ equal = true;
+ int index = 0;
+ while (equal && index < a.length)
+ {
+ equal = a[index] == b[index];
+ index++;
+ }
+ }
+ return equal;
+ }
+
+
+ /**
+ * Looks up the password for a specified user in the password file. Note this code is <b>not</b> secure since it
+ * creates strings of passwords. It should be modified to create only char arrays which get nulled out.
+ *
+ * @param name the name of the principal to lookup
+ *
+ * @return char[] of the password
+ *
+ * @throws java.io.IOException whilst accessing the file
+ */
+ private char[] lookupPassword(String name) throws IOException
+ {
+ 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 < 2)
+ {
+ continue;
+ }
+
+ if (name.equals(result[0]))
+ {
+ return result[1].toCharArray();
+ }
+ }
+ }
+ return null;
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java
new file mode 100644
index 0000000000..5c372f6c2c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java
new file mode 100644
index 0000000000..a82f9ed40b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java
@@ -0,0 +1,100 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Map;
+import java.util.List;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+
+/** Represents a "user database" which is really a way of storing principals (i.e. usernames) and passwords. */
+public interface PrincipalDatabase
+{
+ /**
+ * Set the password for a given principal in the specified callback. This is used for certain SASL providers. The
+ * user database implementation should look up the password in any way it chooses and set it in the callback by
+ * calling its setPassword method.
+ *
+ * @param principal the principal
+ * @param callback the password callback that wants to receive the password
+ *
+ * @throws AccountNotFoundException if the account for specified principal could not be found
+ * @throws IOException if there was an error looking up the principal
+ */
+ void setPassword(Principal principal, PasswordCallback callback)
+ throws IOException, AccountNotFoundException;
+
+ /**
+ * Used to verify that the presented Password is correct. Currently only used by Management Console
+ * @param principal The principal to authenticate
+ * @param password The password to check
+ * @return true if password is correct
+ * @throws AccountNotFoundException if the principal cannot be found
+ */
+ boolean verifyPassword(String principal, char[] password)
+ throws AccountNotFoundException;
+
+ /**
+ * Update(Change) the password for the given principal
+ * @param principal Who's password is to be changed
+ * @param password The new password to use
+ * @return True if change was successful
+ * @throws AccountNotFoundException If the given principal doesn't exist in the Database
+ */
+ boolean updatePassword(Principal principal, char[] password)
+ throws AccountNotFoundException;
+
+ /**
+ * Create a new principal in the database
+ * @param principal The principal to create
+ * @param password The password to set for the principal
+ * @return True on a successful creation
+ */
+ boolean createPrincipal(Principal principal, char[] password);
+
+ /**
+ * Delete a principal
+ * @param principal The principal to delete
+ * @return True on a successful creation
+ * @throws AccountNotFoundException If the given principal doesn't exist in the Database
+ */
+ boolean deletePrincipal(Principal principal)
+ throws AccountNotFoundException;
+
+ /**
+ * Get the principal from the database with the given username
+ * @param username of the principal to lookup
+ * @return The Principal object for the given username or null if not found.
+ */
+ Principal getUser(String username);
+
+
+ public Map<String, AuthenticationProviderInitialiser> getMechanisms();
+
+
+ List<Principal> getUsers();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java
new file mode 100644
index 0000000000..2c553ae76a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.auth.database;
+
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+
+import java.util.Map;
+
+public interface PrincipalDatabaseManager
+{
+ public Map<String, PrincipalDatabase> getDatabases();
+
+ public void initialiseManagement(Configuration config) throws ConfigurationException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java
new file mode 100644
index 0000000000..73d58ca489
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser;
+import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.LinkedList;
+import java.security.Principal;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+public class PropertiesPrincipalDatabase implements PrincipalDatabase
+{
+ private Properties _users;
+
+ private Map<String, AuthenticationProviderInitialiser> _saslServers;
+
+ public PropertiesPrincipalDatabase(Properties users)
+ {
+ _users = users;
+
+ _saslServers = new HashMap<String, AuthenticationProviderInitialiser>();
+
+ /**
+ * Create Authenticators for Properties Principal Database.
+ */
+
+ // Accept MD5 incomming and use plain comparison with the file
+ PlainInitialiser cram = new PlainInitialiser();
+ cram.initialise(this);
+ // Accept Plain incomming and hash it for comparison to the file.
+ CRAMMD5Initialiser plain = new CRAMMD5Initialiser();
+ plain.initialise(this, CRAMMD5Initialiser.HashDirection.INCOMMING);
+
+ _saslServers.put(plain.getMechanismName(), cram);
+ _saslServers.put(cram.getMechanismName(), plain);
+ }
+
+ public void setPassword(Principal principal, PasswordCallback callback) throws IOException, AccountNotFoundException
+ {
+ if (principal == null)
+ {
+ throw new IllegalArgumentException("principal must not be null");
+ }
+ char[] pwd = _users.getProperty(principal.getName()).toCharArray();
+ if (pwd != null)
+ {
+ callback.setPassword(pwd);
+ }
+ else
+ {
+ throw new AccountNotFoundException("No account found for principal " + principal);
+ }
+ }
+
+ public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException
+ {
+ //fixme this is not correct as toCharArray is not safe based on the type of string.
+ char[] pwd = _users.getProperty(principal).toCharArray();
+
+ return compareCharArray(pwd, password);
+ }
+
+ public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException
+ {
+ return false; // updates denied
+ }
+
+ public boolean createPrincipal(Principal principal, char[] password)
+ {
+ return false; // updates denied
+ }
+
+ public boolean deletePrincipal(Principal principal) throws AccountNotFoundException
+ {
+ return false; // updates denied
+ }
+
+ private boolean compareCharArray(char[] a, char[] b)
+ {
+ boolean equal = false;
+ if (a.length == b.length)
+ {
+ equal = true;
+ int index = 0;
+ while (equal && index < a.length)
+ {
+ equal = a[index] == b[index];
+ index++;
+ }
+ }
+ return equal;
+ }
+
+ private char[] convertPassword(String password) throws UnsupportedEncodingException
+ {
+ byte[] passwdBytes = password.getBytes("utf-8");
+
+ char[] passwd = new char[passwdBytes.length];
+
+ int index = 0;
+
+ for (byte b : passwdBytes)
+ {
+ passwd[index++] = (char) b;
+ }
+
+ return passwd;
+ }
+
+
+ public Map<String, AuthenticationProviderInitialiser> getMechanisms()
+ {
+ return _saslServers;
+ }
+
+ public List<Principal> getUsers()
+ {
+ return new LinkedList<Principal>(); //todo
+ }
+
+ public Principal getUser(String username)
+ {
+ if (_users.getProperty(username) != null)
+ {
+ return new UsernamePrincipal(username);
+ }
+ else
+ {
+ return null;
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java
new file mode 100644
index 0000000000..6b86a46bd2
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.security.auth.database;
+
+import org.apache.commons.configuration.Configuration;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.HashMap;
+
+public class PropertiesPrincipalDatabaseManager implements PrincipalDatabaseManager
+{
+
+ Map<String, PrincipalDatabase> _databases = new HashMap<String, PrincipalDatabase>();
+
+ public PropertiesPrincipalDatabaseManager(String name, Properties users)
+ {
+ _databases.put(name, new PropertiesPrincipalDatabase(users));
+ }
+
+ public Map<String, PrincipalDatabase> getDatabases()
+ {
+ return _databases;
+ }
+
+ public void initialiseManagement(Configuration config)
+ {
+ //todo
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java
new file mode 100644
index 0000000000..bb94e0b7bf
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java
@@ -0,0 +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.auth.manager;
+
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+public interface AuthenticationManager
+{
+ String getMechanisms();
+
+ SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException;
+
+ AuthenticationResult authenticate(SaslServer server, byte[] response);
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
new file mode 100644
index 0000000000..ce5e0cd748
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.manager;
+
+import org.apache.log4j.Logger;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.SubsetConfiguration;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.server.security.auth.sasl.JCAProvider;
+import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.SaslServerFactory;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.Sasl;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.TreeMap;
+import java.security.Security;
+
+public class PrincipalDatabaseAuthenticationManager implements AuthenticationManager
+{
+ private static final Logger _logger = Logger.getLogger(PrincipalDatabaseAuthenticationManager.class);
+
+ /** The list of mechanisms, in the order in which they are configured (i.e. preferred order) */
+ private String _mechanisms;
+
+ /** Maps from the mechanism to the callback handler to use for handling those requests */
+ private Map<String, CallbackHandler> _callbackHandlerMap = new HashMap<String, CallbackHandler>();
+
+ /**
+ * Maps from the mechanism to the properties used to initialise the server. See the method Sasl.createSaslServer for
+ * details of the use of these properties. This map is populated during initialisation of each provider.
+ */
+ private Map<String, Map<String, ?>> _serverCreationProperties = new HashMap<String, Map<String, ?>>();
+
+ private AuthenticationManager _default = null;
+
+ public PrincipalDatabaseAuthenticationManager(String name, Configuration hostConfig) throws Exception
+ {
+ _logger.info("Initialising " + (name == null ? "Default" : "'" + name + "'")
+ + " PrincipleDatabase authentication manager.");
+
+ // Fixme This should be done per Vhost but allowing global hack isn't right but ...
+ // required as authentication is done before Vhost selection
+
+ Map<String, Class<? extends SaslServerFactory>> providerMap = new TreeMap<String, Class<? extends SaslServerFactory>>();
+
+
+ if (name == null || hostConfig == null)
+ {
+ initialiseAuthenticationMechanisms(providerMap, ApplicationRegistry.getInstance().getDatabaseManager().getDatabases());
+ }
+ else
+ {
+ String databaseName = hostConfig.getString("security.authentication.name");
+
+ 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;
+ }
+ else
+ {
+ PrincipalDatabase database = ApplicationRegistry.getInstance().getDatabaseManager().getDatabases().get(databaseName);
+
+ if (database == null)
+ {
+ throw new ConfigurationException("Requested database:" + databaseName + " was not found");
+ }
+
+ initialiseAuthenticationMechanisms(providerMap, database);
+ }
+ }
+
+ if (providerMap.size() > 0)
+ {
+ // Ensure we are used before the defaults
+ if (Security.insertProviderAt(new JCAProvider(providerMap), 1) == -1)
+ {
+ _logger.warn("Unable to set order of providers.");
+ }
+ }
+ else
+ {
+ _logger.warn("No additional SASL providers registered.");
+ }
+
+ }
+
+
+ private void initialiseAuthenticationMechanisms(Map<String, Class<? extends SaslServerFactory>> providerMap, Map<String, PrincipalDatabase> databases) throws Exception
+ {
+// Configuration config = ApplicationRegistry.getInstance().getConfiguration();
+// List<String> mechanisms = config.getList("security.sasl.mechanisms.mechanism.initialiser.class");
+//
+// // Maps from the mechanism to the properties used to initialise the server. See the method
+// // Sasl.createSaslServer for details of the use of these properties. This map is populated during initialisation
+// // of each provider.
+
+
+ if (databases.size() > 1)
+ {
+ _logger.warn("More than one principle database provided currently authentication mechanism will override each other.");
+ }
+
+ for (Map.Entry<String, PrincipalDatabase> entry : databases.entrySet())
+ {
+
+ // fixme As the database now provide the mechanisms they support, they will ...
+ // overwrite each other in the map. There should only be one database per vhost.
+ // But currently we must have authentication before vhost definition.
+ initialiseAuthenticationMechanisms(providerMap, entry.getValue());
+ }
+
+ }
+
+ private void initialiseAuthenticationMechanisms(Map<String, Class<? extends SaslServerFactory>> providerMap, PrincipalDatabase database) throws Exception
+ {
+ if (database == null || database.getMechanisms().size() == 0)
+ {
+ _logger.warn("No Database or no mechanisms to initialise authentication");
+ return;
+ }
+
+ for (Map.Entry<String, AuthenticationProviderInitialiser> mechanism : database.getMechanisms().entrySet())
+ {
+ initialiseAuthenticationMechanism(mechanism.getKey(), mechanism.getValue(), providerMap);
+ }
+ }
+
+ private void initialiseAuthenticationMechanism(String mechanism, AuthenticationProviderInitialiser initialiser,
+ Map<String, Class<? extends SaslServerFactory>> providerMap)
+ throws Exception
+ {
+ if (_mechanisms == null)
+ {
+ _mechanisms = mechanism;
+ }
+ else
+ {
+ // simple append should be fine since the number of mechanisms is small and this is a one time initialisation
+ _mechanisms = _mechanisms + " " + mechanism;
+ }
+ _callbackHandlerMap.put(mechanism, initialiser.getCallbackHandler());
+ _serverCreationProperties.put(mechanism, initialiser.getProperties());
+ Class<? extends SaslServerFactory> factory = initialiser.getServerFactoryClassForJCARegistration();
+ if (factory != null)
+ {
+ providerMap.put(mechanism, factory);
+ }
+ _logger.info("Initialised " + mechanism + " SASL provider successfully");
+ }
+
+ public String getMechanisms()
+ {
+ if (_default != null)
+ {
+ // Use the default AuthenticationManager if present
+ return _default.getMechanisms();
+ }
+ else
+ {
+ return _mechanisms;
+ }
+ }
+
+ public SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException
+ {
+ if (_default != null)
+ {
+ // Use the default AuthenticationManager if present
+ return _default.createSaslServer(mechanism, localFQDN);
+ }
+ else
+ {
+ return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, _serverCreationProperties.get(mechanism),
+ _callbackHandlerMap.get(mechanism));
+ }
+
+ }
+
+ public AuthenticationResult authenticate(SaslServer server, byte[] response)
+ {
+ // Use the default AuthenticationManager if present
+ if (_default != null)
+ {
+ return _default.authenticate(server, response);
+ }
+
+
+ try
+ {
+ // Process response from the client
+ byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]);
+
+ if (server.isComplete())
+ {
+ return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.SUCCESS);
+ }
+ else
+ {
+ return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE);
+ }
+ }
+ catch (SaslException e)
+ {
+ return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR);
+ }
+ }
+
+ public AuthenticationResult isAuthorize(VirtualHost vhost, String username)
+ {
+ return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java
new file mode 100644
index 0000000000..89e545d6f5
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.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.server.security.auth.sasl;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.SaslServerFactory;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+
+public interface AuthenticationProviderInitialiser
+{
+ /**
+ * @return the mechanism's name. This will be used in the list of mechanism's advertised to the
+ * client.
+ */
+ String getMechanismName();
+
+ /**
+ * Initialise the authentication provider.
+ * @param baseConfigPath the path in the config file that points to any config options for this provider. Each
+ * provider can have its own set of configuration options
+ * @param configuration the Apache Commons Configuration instance used to configure this provider
+ * @param principalDatabases the set of principal databases that are available
+ * @throws Exception needs refined Exception is too broad.
+ */
+ void initialise(String baseConfigPath, Configuration configuration,
+ Map<String, PrincipalDatabase> principalDatabases) throws Exception;
+
+ /**
+ * Initialise the authentication provider.
+ * @param db The principal database to initialise with
+ */
+ void initialise(PrincipalDatabase db);
+
+
+ /**
+ * @return the callback handler that should be used to process authentication requests for this mechanism. This will
+ * be called after initialise and will be stored by the authentication manager. The callback handler <b>must</b> be
+ * fully threadsafe.
+ */
+ CallbackHandler getCallbackHandler();
+
+ /**
+ * Get the properties that must be passed in to the Sasl.createSaslServer method.
+ * @return the properties, which may be null
+ */
+ Map<String, ?> getProperties();
+
+ /**
+ * Get the class that is the server factory. This is used for the JCA registration.
+ * @return null if no JCA registration is required, otherwise return the class
+ * that will be used in JCA registration
+ */
+ Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java
new file mode 100644
index 0000000000..fd4ad86055
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java
@@ -0,0 +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.server.security.auth.sasl;
+
+import java.security.Provider;
+import java.security.Security;
+import java.util.Map;
+
+import javax.security.sasl.SaslServerFactory;
+
+public final class JCAProvider extends Provider
+{
+ public JCAProvider(Map<String, Class<? extends SaslServerFactory>> providerMap)
+ {
+ super("AMQSASLProvider", 1.0, "A JCA provider that registers all " +
+ "AMQ SASL providers that want to be registered");
+ register(providerMap);
+ //Security.addProvider(this);
+ }
+
+ private void register(Map<String, Class<? extends SaslServerFactory>> providerMap)
+ {
+ for (Map.Entry<String, Class<? extends SaslServerFactory>> me :
+ providerMap.entrySet())
+ {
+ put("SaslServerFactory." + me.getKey(), me.getValue().getName());
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java
new file mode 100644
index 0000000000..dd0bd096c3
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java
@@ -0,0 +1,123 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.sasl;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Map;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AccountNotFoundException;
+import javax.security.sasl.AuthorizeCallback;
+
+import org.apache.commons.configuration.Configuration;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+
+public abstract class UsernamePasswordInitialiser implements AuthenticationProviderInitialiser
+{
+ protected static final Logger _logger = Logger.getLogger(UsernamePasswordInitialiser.class);
+
+ private ServerCallbackHandler _callbackHandler;
+
+ private class ServerCallbackHandler implements CallbackHandler
+ {
+ private final PrincipalDatabase _principalDatabase;
+
+ protected ServerCallbackHandler(PrincipalDatabase database)
+ {
+ _principalDatabase = database;
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ Principal username = null;
+ for (Callback callback : callbacks)
+ {
+ if (callback instanceof NameCallback)
+ {
+ username = new UsernamePrincipal(((NameCallback) callback).getDefaultName());
+ }
+ else if (callback instanceof PasswordCallback)
+ {
+ try
+ {
+ _principalDatabase.setPassword(username, (PasswordCallback) callback);
+ }
+ catch (AccountNotFoundException e)
+ {
+ // very annoyingly the callback handler does not throw anything more appropriate than
+ // IOException
+ IOException ioe = new IOException("Error looking up user " + e);
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+ else if (callback instanceof AuthorizeCallback)
+ {
+ ((AuthorizeCallback) callback).setAuthorized(true);
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(callback);
+ }
+ }
+ }
+ }
+
+ public void initialise(String baseConfigPath, Configuration configuration,
+ Map<String, PrincipalDatabase> principalDatabases) throws Exception
+ {
+ String principalDatabaseName = configuration.getString(baseConfigPath + ".principal-database");
+ PrincipalDatabase db = principalDatabases.get(principalDatabaseName);
+
+ initialise(db);
+ }
+
+ public void initialise(PrincipalDatabase db)
+ {
+ if (db == null)
+ {
+ throw new NullPointerException("Cannot initialise with a null Principal database.");
+ }
+
+ _callbackHandler = new ServerCallbackHandler(db);
+ }
+
+ public CallbackHandler getCallbackHandler()
+ {
+ return _callbackHandler;
+ }
+
+ public Map<String, ?> getProperties()
+ {
+ // there are no properties required for the CRAM-MD5 implementation
+ return null;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java
new file mode 100644
index 0000000000..d7c8383690
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.sasl;
+
+import java.security.Principal;
+
+/** A principal that is just a wrapper for a simple username. */
+public class UsernamePrincipal implements Principal
+{
+ private String _name;
+
+ public UsernamePrincipal(String name)
+ {
+ _name = name;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public String toString()
+ {
+ return _name;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java
new file mode 100644
index 0000000000..7acc6322d1
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.sasl.amqplain;
+
+import javax.security.sasl.SaslServerFactory;
+
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+
+public class AmqPlainInitialiser extends UsernamePasswordInitialiser
+{
+ public String getMechanismName()
+ {
+ return "AMQPLAIN";
+ }
+
+ public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+ {
+ return AmqPlainSaslServerFactory.class;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java
new file mode 100644
index 0000000000..7842f376fb
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java
@@ -0,0 +1,129 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.sasl.amqplain;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+
+public class AmqPlainSaslServer implements SaslServer
+{
+ public static final String MECHANISM = "AMQPLAIN";
+
+ private CallbackHandler _cbh;
+
+ private String _authorizationId;
+
+ private boolean _complete = false;
+
+ public AmqPlainSaslServer(CallbackHandler cbh)
+ {
+ _cbh = cbh;
+ }
+
+ public String getMechanismName()
+ {
+ return MECHANISM;
+ }
+
+ public byte[] evaluateResponse(byte[] response) throws SaslException
+ {
+ try
+ {
+ final FieldTable ft = FieldTableFactory.newFieldTable(ByteBuffer.wrap(response), response.length);
+ String username = (String) ft.getString("LOGIN");
+ // we do not care about the prompt but it throws if null
+ NameCallback nameCb = new NameCallback("prompt", username);
+ // we do not care about the prompt but it throws if null
+ PasswordCallback passwordCb = new PasswordCallback("prompt", false);
+ // TODO: should not get pwd as a String but as a char array...
+ String pwd = (String) ft.getString("PASSWORD");
+ passwordCb.setPassword(pwd.toCharArray());
+ AuthorizeCallback authzCb = new AuthorizeCallback(username, username);
+ Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb};
+ _cbh.handle(callbacks);
+ _complete = true;
+ if (authzCb.isAuthorized())
+ {
+ _authorizationId = authzCb.getAuthenticationID();
+ return null;
+ }
+ else
+ {
+ throw new SaslException("Authentication failed");
+ }
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ throw new SaslException("Unable to decode response: " + e, e);
+ }
+ catch (IOException e)
+ {
+ throw new SaslException("Error processing data: " + e, e);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException("Unable to obtain data from callback handler: " + e, e);
+ }
+ }
+
+ public boolean isComplete()
+ {
+ return _complete;
+ }
+
+ public String getAuthorizationID()
+ {
+ return _authorizationId;
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ public void dispose() throws SaslException
+ {
+ _cbh = null;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java
new file mode 100644
index 0000000000..67d20136bf
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.sasl.amqplain;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+
+public class AmqPlainSaslServerFactory implements SaslServerFactory
+{
+ public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props,
+ CallbackHandler cbh) throws SaslException
+ {
+ if (AmqPlainSaslServer.MECHANISM.equals(mechanism))
+ {
+ return new AmqPlainSaslServer(cbh);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ else
+ {
+ return new String[]{AmqPlainSaslServer.MECHANISM};
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java
new file mode 100644
index 0000000000..97f9a4e91a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.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.server.security.auth.sasl.crammd5;
+
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+
+import javax.security.sasl.SaslServerFactory;
+import java.util.Map;
+
+public class CRAMMD5HashedInitialiser extends UsernamePasswordInitialiser
+{
+ public String getMechanismName()
+ {
+ return CRAMMD5HashedSaslServer.MECHANISM;
+ }
+
+ public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+ {
+ return CRAMMD5HashedServerFactory.class;
+ }
+
+ public void initialise(PrincipalDatabase passwordFile)
+ {
+ super.initialise(passwordFile);
+ }
+
+ public Map<String, ?> getProperties()
+ {
+ return null;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java
new file mode 100644
index 0000000000..f6cab084ea
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.sasl.crammd5;
+
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslServerFactory;
+import javax.security.auth.callback.CallbackHandler;
+import java.util.Enumeration;
+import java.util.Map;
+
+public class CRAMMD5HashedSaslServer implements SaslServer
+{
+ public static final String MECHANISM = "CRAM-MD5-HASHED";
+
+ private SaslServer _realServer;
+
+ public CRAMMD5HashedSaslServer(String mechanism, String protocol, String serverName, Map<String, ?> props,
+ CallbackHandler cbh) throws SaslException
+ {
+ Enumeration factories = Sasl.getSaslServerFactories();
+
+ while (factories.hasMoreElements())
+ {
+ SaslServerFactory factory = (SaslServerFactory) factories.nextElement();
+
+ if (factory instanceof CRAMMD5HashedServerFactory)
+ {
+ continue;
+ }
+
+ String[] mechs = factory.getMechanismNames(props);
+
+ for (String mech : mechs)
+ {
+ if (mech.equals("CRAM-MD5"))
+ {
+ _realServer = factory.createSaslServer("CRAM-MD5", protocol, serverName, props, cbh);
+ return;
+ }
+ }
+ }
+
+ throw new RuntimeException("No default SaslServer found for mechanism:" + "CRAM-MD5");
+ }
+
+ public String getMechanismName()
+ {
+ return MECHANISM;
+ }
+
+ public byte[] evaluateResponse(byte[] response) throws SaslException
+ {
+ return _realServer.evaluateResponse(response);
+ }
+
+ public boolean isComplete()
+ {
+ return _realServer.isComplete();
+ }
+
+ public String getAuthorizationID()
+ {
+ return _realServer.getAuthorizationID();
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ return _realServer.unwrap(incoming, offset, len);
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ return _realServer.wrap(outgoing, offset, len);
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return _realServer.getNegotiatedProperty(propName);
+ }
+
+ public void dispose() throws SaslException
+ {
+ _realServer.dispose();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java
new file mode 100644
index 0000000000..5298b5cc63
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.sasl.crammd5;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+
+public class CRAMMD5HashedServerFactory implements SaslServerFactory
+{
+ public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map<String, ?> props,
+ CallbackHandler cbh) throws SaslException
+ {
+ if (mechanism.equals(CRAMMD5HashedSaslServer.MECHANISM))
+ {
+ return new CRAMMD5HashedSaslServer(mechanism, protocol, serverName, props, cbh);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props != null)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ }
+
+ return new String[]{CRAMMD5HashedSaslServer.MECHANISM};
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java
new file mode 100644
index 0000000000..264832888d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.sasl.crammd5;
+
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+
+import javax.security.sasl.SaslServerFactory;
+
+public class CRAMMD5Initialiser extends UsernamePasswordInitialiser
+{
+ private HashDirection _hashDirection;
+
+ public enum HashDirection
+ {
+ INCOMMING, PASSWORD_FILE
+ }
+
+
+ public String getMechanismName()
+ {
+ return "CRAM-MD5";
+ }
+
+ public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+ {
+ // since the CRAM-MD5 provider is registered as part of the JDK, we do not
+ // return the factory class here since we do not need to register it ourselves.
+ if (_hashDirection == HashDirection.PASSWORD_FILE)
+ {
+ return null;
+ }
+ else
+ {
+ //fixme we need a server that will correctly has the incomming plain text for comparison to file.
+ _logger.warn("we need a server that will correctly convert the incomming plain text for comparison to file.");
+ return null;
+ }
+ }
+
+ public void initialise(PrincipalDatabase passwordFile)
+ {
+ initialise(passwordFile, HashDirection.PASSWORD_FILE);
+ }
+
+ public void initialise(PrincipalDatabase passwordFile, HashDirection direction)
+ {
+ super.initialise(passwordFile);
+
+ _hashDirection = direction;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java
new file mode 100644
index 0000000000..1d16cd8755
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.sasl.plain;
+
+import javax.security.sasl.SaslServerFactory;
+
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+
+public class PlainInitialiser extends UsernamePasswordInitialiser
+{
+ public String getMechanismName()
+ {
+ return "PLAIN";
+ }
+
+ public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+ {
+ return PlainSaslServerFactory.class;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java
new file mode 100644
index 0000000000..36aeb77fe1
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java
@@ -0,0 +1,149 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.sasl.plain;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+public class PlainSaslServer implements SaslServer
+{
+ public static final String MECHANISM = "PLAIN";
+
+ private CallbackHandler _cbh;
+
+ private String _authorizationId;
+
+ private boolean _complete = false;
+
+ public PlainSaslServer(CallbackHandler cbh)
+ {
+ _cbh = cbh;
+ }
+
+ public String getMechanismName()
+ {
+ return MECHANISM;
+ }
+
+ public byte[] evaluateResponse(byte[] response) throws SaslException
+ {
+ try
+ {
+ int authzidNullPosition = findNullPosition(response, 0);
+ if (authzidNullPosition < 0)
+ {
+ throw new SaslException("Invalid PLAIN encoding, authzid null terminator not found");
+ }
+ int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1);
+ if (authcidNullPosition < 0)
+ {
+ throw new SaslException("Invalid PLAIN encoding, authcid null terminator not found");
+ }
+
+ // we do not currently support authcid in any meaningful way
+ // String authcid = new String(response, 0, authzidNullPosition, "utf8");
+ String authzid = new String(response, authzidNullPosition + 1, authcidNullPosition - 1, "utf8");
+
+ // we do not care about the prompt but it throws if null
+ NameCallback nameCb = new NameCallback("prompt", authzid);
+ // we do not care about the prompt but it throws if null
+ PasswordCallback passwordCb = new PasswordCallback("prompt", false);
+ // TODO: should not get pwd as a String but as a char array...
+ int passwordLen = response.length - authcidNullPosition - 1;
+ String pwd = new String(response, authcidNullPosition + 1, passwordLen, "utf8");
+ passwordCb.setPassword(pwd.toCharArray());
+ AuthorizeCallback authzCb = new AuthorizeCallback(authzid, authzid);
+ Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb};
+ _cbh.handle(callbacks);
+ _complete = true;
+ if (authzCb.isAuthorized())
+ {
+ _authorizationId = authzCb.getAuthenticationID();
+ return null;
+ }
+ else
+ {
+ throw new SaslException("Authentication failed");
+ }
+ }
+ catch (IOException e)
+ {
+ throw new SaslException("Error processing data: " + e, e);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException("Unable to obtain data from callback handler: " + e, e);
+ }
+ }
+
+ private int findNullPosition(byte[] response, int startPosition)
+ {
+ int position = startPosition;
+ while (position < response.length)
+ {
+ if (response[position] == (byte) 0)
+ {
+ return position;
+ }
+ position++;
+ }
+ return -1;
+ }
+
+ public boolean isComplete()
+ {
+ return _complete;
+ }
+
+ public String getAuthorizationID()
+ {
+ return _authorizationId;
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ public void dispose() throws SaslException
+ {
+ _cbh = null;
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java
new file mode 100644
index 0000000000..f0dd9eeb6d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.sasl.plain;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+
+public class PlainSaslServerFactory implements SaslServerFactory
+{
+ public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props,
+ CallbackHandler cbh) throws SaslException
+ {
+ if (PlainSaslServer.MECHANISM.equals(mechanism))
+ {
+ return new PlainSaslServer(cbh);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ else
+ {
+ return new String[]{PlainSaslServer.MECHANISM};
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java b/Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java
new file mode 100644
index 0000000000..f427cc7206
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.state;
+
+/**
+ * States used in the AMQ protocol. Used by the finite state machine to determine
+ * valid responses.
+ */
+public enum AMQState
+{
+ CONNECTION_NOT_STARTED,
+ CONNECTION_NOT_AUTH,
+ CONNECTION_NOT_TUNED,
+ CONNECTION_NOT_OPENED,
+ CONNECTION_OPEN,
+ CONNECTION_CLOSING,
+ CONNECTION_CLOSED
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java b/Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java
new file mode 100644
index 0000000000..f96900d0a9
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java
@@ -0,0 +1,284 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.BasicAckBody;
+import org.apache.qpid.framing.BasicCancelBody;
+import org.apache.qpid.framing.BasicConsumeBody;
+import org.apache.qpid.framing.BasicGetBody;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.BasicQosBody;
+import org.apache.qpid.framing.BasicRecoverBody;
+import org.apache.qpid.framing.BasicRejectBody;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.framing.ChannelFlowBody;
+import org.apache.qpid.framing.ChannelOpenBody;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.ConnectionCloseOkBody;
+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.ExchangeBoundBody;
+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.framing.QueuePurgeBody;
+import org.apache.qpid.framing.TxCommitBody;
+import org.apache.qpid.framing.TxRollbackBody;
+import org.apache.qpid.framing.TxSelectBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQMethodListener;
+import org.apache.qpid.server.handler.BasicAckMethodHandler;
+import org.apache.qpid.server.handler.BasicCancelMethodHandler;
+import org.apache.qpid.server.handler.BasicConsumeMethodHandler;
+import org.apache.qpid.server.handler.BasicGetMethodHandler;
+import org.apache.qpid.server.handler.BasicPublishMethodHandler;
+import org.apache.qpid.server.handler.BasicQosHandler;
+import org.apache.qpid.server.handler.BasicRecoverMethodHandler;
+import org.apache.qpid.server.handler.BasicRejectMethodHandler;
+import org.apache.qpid.server.handler.ChannelCloseHandler;
+import org.apache.qpid.server.handler.ChannelCloseOkHandler;
+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.ConnectionCloseOkMethodHandler;
+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.ExchangeBoundHandler;
+import org.apache.qpid.server.handler.ExchangeDeclareHandler;
+import org.apache.qpid.server.handler.ExchangeDeleteHandler;
+import org.apache.qpid.server.handler.QueueBindHandler;
+import org.apache.qpid.server.handler.QueueDeclareHandler;
+import org.apache.qpid.server.handler.QueueDeleteHandler;
+import org.apache.qpid.server.handler.QueuePurgeHandler;
+import org.apache.qpid.server.handler.TxCommitHandler;
+import org.apache.qpid.server.handler.TxRollbackHandler;
+import org.apache.qpid.server.handler.TxSelectHandler;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+
+/**
+ * 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
+{
+ private static final Logger _logger = Logger.getLogger(AMQStateManager.class);
+
+ private final VirtualHostRegistry _virtualHostRegistry;
+ private final AMQProtocolSession _protocolSession;
+ /** The current state */
+ private AMQState _currentState;
+
+ /**
+ * Maps from an AMQState instance to a Map from Class to StateTransitionHandler. The class must be a subclass of
+ * AMQFrame.
+ */
+ private final EnumMap<AMQState, Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>> _state2HandlersMap =
+ new EnumMap<AMQState, Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>>(
+ AMQState.class);
+
+ private CopyOnWriteArraySet<StateListener> _stateListeners = new CopyOnWriteArraySet<StateListener>();
+
+ public AMQStateManager(VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession)
+ {
+ this(AMQState.CONNECTION_NOT_STARTED, true, virtualHostRegistry, protocolSession);
+ }
+
+ protected AMQStateManager(AMQState initial, boolean register, VirtualHostRegistry virtualHostRegistry,
+ AMQProtocolSession protocolSession)
+ {
+ _virtualHostRegistry = virtualHostRegistry;
+ _protocolSession = protocolSession;
+ _currentState = initial;
+ if (register)
+ {
+ registerListeners();
+ }
+ }
+
+ protected void registerListeners()
+ {
+ Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>> frame2handlerMap;
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionStartOkBody.class, ConnectionStartOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionSecureOkBody.class, ConnectionSecureOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_AUTH, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionTuneOkBody.class, ConnectionTuneOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionOpenBody.class, ConnectionOpenMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap);
+
+ //
+ // ConnectionOpen handlers
+ //
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ChannelOpenBody.class, ChannelOpenHandler.getInstance());
+ frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseHandler.getInstance());
+ frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ frame2handlerMap.put(ExchangeDeclareBody.class, ExchangeDeclareHandler.getInstance());
+ frame2handlerMap.put(ExchangeDeleteBody.class, ExchangeDeleteHandler.getInstance());
+ frame2handlerMap.put(ExchangeBoundBody.class, ExchangeBoundHandler.getInstance());
+ frame2handlerMap.put(BasicAckBody.class, BasicAckMethodHandler.getInstance());
+ frame2handlerMap.put(BasicRecoverBody.class, BasicRecoverMethodHandler.getInstance());
+ frame2handlerMap.put(BasicConsumeBody.class, BasicConsumeMethodHandler.getInstance());
+ frame2handlerMap.put(BasicGetBody.class, BasicGetMethodHandler.getInstance());
+ frame2handlerMap.put(BasicCancelBody.class, BasicCancelMethodHandler.getInstance());
+ frame2handlerMap.put(BasicPublishBody.class, BasicPublishMethodHandler.getInstance());
+ frame2handlerMap.put(BasicQosBody.class, BasicQosHandler.getInstance());
+ frame2handlerMap.put(QueueBindBody.class, QueueBindHandler.getInstance());
+ frame2handlerMap.put(QueueDeclareBody.class, QueueDeclareHandler.getInstance());
+ frame2handlerMap.put(QueueDeleteBody.class, QueueDeleteHandler.getInstance());
+ frame2handlerMap.put(QueuePurgeBody.class, QueuePurgeHandler.getInstance());
+ frame2handlerMap.put(ChannelFlowBody.class, ChannelFlowHandler.getInstance());
+ frame2handlerMap.put(TxSelectBody.class, TxSelectHandler.getInstance());
+ frame2handlerMap.put(TxCommitBody.class, TxCommitHandler.getInstance());
+ frame2handlerMap.put(TxRollbackBody.class, TxRollbackHandler.getInstance());
+ frame2handlerMap.put(BasicRejectBody.class, BasicRejectMethodHandler.getInstance());
+
+ _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionCloseOkBody.class, ConnectionCloseOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_CLOSING, frame2handlerMap);
+
+ }
+
+ public AMQState getCurrentState()
+ {
+ return _currentState;
+ }
+
+ public void changeState(AMQState newState) throws AMQException
+ {
+ _logger.debug("State changing to " + newState + " from old state " + _currentState);
+ final AMQState oldState = _currentState;
+ _currentState = newState;
+
+ for (StateListener l : _stateListeners)
+ {
+ l.stateChanged(oldState, newState);
+ }
+ }
+
+ public void error(Exception e)
+ {
+ _logger.error("State manager received error notification[Current State:" + _currentState + "]: " + e, e);
+ for (StateListener l : _stateListeners)
+ {
+ l.error(e);
+ }
+ }
+
+ public <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws AMQException
+ {
+ StateAwareMethodListener<B> handler = findStateTransitionHandler(_currentState, evt.getMethod());
+ if (handler != null)
+ {
+
+ checkChannel(evt, _protocolSession);
+
+ handler.methodReceived(this, evt);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private <B extends AMQMethodBody> void checkChannel(AMQMethodEvent<B> evt, AMQProtocolSession protocolSession)
+ throws AMQException
+ {
+ if ((evt.getChannelId() != 0) && !(evt.getMethod() instanceof ChannelOpenBody)
+ && (protocolSession.getChannel(evt.getChannelId()) == null)
+ && !protocolSession.channelAwaitingClosure(evt.getChannelId()))
+ {
+ throw evt.getMethod().getChannelNotFoundException(evt.getChannelId());
+ }
+ }
+
+ protected <B extends AMQMethodBody> StateAwareMethodListener<B> findStateTransitionHandler(AMQState currentState,
+ B frame)
+ // throws IllegalStateTransitionException
+ {
+ final Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>> classToHandlerMap =
+ _state2HandlersMap.get(currentState);
+
+ final StateAwareMethodListener<B> handler =
+ (classToHandlerMap == null) ? null : (StateAwareMethodListener<B>) classToHandlerMap.get(frame.getClass());
+
+ if (handler == null)
+ {
+ _logger.debug("No state transition handler defined for receiving frame " + frame);
+
+ return null;
+ }
+ else
+ {
+ return handler;
+ }
+ }
+
+ public void addStateListener(StateListener listener)
+ {
+ _logger.debug("Adding state listener");
+ _stateListeners.add(listener);
+ }
+
+ public void removeStateListener(StateListener listener)
+ {
+ _stateListeners.remove(listener);
+ }
+
+ public VirtualHostRegistry getVirtualHostRegistry()
+ {
+ return _virtualHostRegistry;
+ }
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java b/Final/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java
new file mode 100644
index 0000000000..cec67a8a6d
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+import org.apache.qpid.AMQException;
+
+/**
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Not used! Delete.
+ */
+public class IllegalStateTransitionException extends AMQException
+{
+ private AMQState _originalState;
+
+ private Class _frame;
+
+ public IllegalStateTransitionException(AMQState originalState, Class frame)
+ {
+ super("No valid state transition defined for receiving frame " + frame + " from state " + originalState);
+ _originalState = originalState;
+ _frame = frame;
+ }
+
+ public AMQState getOriginalState()
+ {
+ return _originalState;
+ }
+
+ public Class getFrameClass()
+ {
+ return _frame;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java b/Final/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java
new file mode 100644
index 0000000000..e3af0bc486
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.state;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+/**
+ * A frame listener that is informed of the protocol state when invoked and has
+ * the opportunity to update state.
+ *
+ */
+public interface StateAwareMethodListener <B extends AMQMethodBody>
+{
+ void methodReceived(AMQStateManager stateManager, AMQMethodEvent<B> evt) throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java b/Final/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java
new file mode 100644
index 0000000000..00fc09867b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+import org.apache.qpid.AMQException;
+
+public interface StateListener
+{
+ void stateChanged(AMQState oldState, AMQState newState) throws AMQException;
+
+ void error(Throwable t);
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/Final/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java
new file mode 100644
index 0000000000..8ccb0be0a8
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MessageMetaData;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.exchange.Exchange;
+
+/**
+ * A simple message store that stores the messages in a threadsafe structure in memory.
+ */
+public class MemoryMessageStore implements MessageStore
+{
+ private static final Logger _log = Logger.getLogger(MemoryMessageStore.class);
+
+ private static final int DEFAULT_HASHTABLE_CAPACITY = 50000;
+
+ private static final String HASHTABLE_CAPACITY_CONFIG = "hashtable-capacity";
+
+ protected ConcurrentMap<Long, MessageMetaData> _metaDataMap;
+
+ protected ConcurrentMap<Long, List<ContentChunk>> _contentBodyMap;
+
+ private final AtomicLong _messageId = new AtomicLong(1);
+
+ public void configure()
+ {
+ _log.info("Using capacity " + DEFAULT_HASHTABLE_CAPACITY + " for hash tables");
+ _metaDataMap = new ConcurrentHashMap<Long, MessageMetaData>(DEFAULT_HASHTABLE_CAPACITY);
+ _contentBodyMap = new ConcurrentHashMap<Long, List<ContentChunk>>(DEFAULT_HASHTABLE_CAPACITY);
+ }
+
+ public void configure(String base, Configuration config)
+ {
+ int hashtableCapacity = config.getInt(base + "." + HASHTABLE_CAPACITY_CONFIG, DEFAULT_HASHTABLE_CAPACITY);
+ _log.info("Using capacity " + hashtableCapacity + " for hash tables");
+ _metaDataMap = new ConcurrentHashMap<Long, MessageMetaData>(hashtableCapacity);
+ _contentBodyMap = new ConcurrentHashMap<Long, List<ContentChunk>>(hashtableCapacity);
+ }
+
+ public void configure(VirtualHost virtualHost, String base, Configuration config) throws Exception
+ {
+ configure(base, config);
+ }
+
+ public void close() throws Exception
+ {
+ if (_metaDataMap != null)
+ {
+ _metaDataMap.clear();
+ _metaDataMap = null;
+ }
+ if (_contentBodyMap != null)
+ {
+ _contentBodyMap.clear();
+ _contentBodyMap = null;
+ }
+ }
+
+ public void removeMessage(StoreContext context, Long messageId)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Removing message with id " + messageId);
+ }
+ _metaDataMap.remove(messageId);
+ _contentBodyMap.remove(messageId);
+ }
+
+ public void createExchange(Exchange exchange) throws AMQException
+ {
+
+ }
+
+ public void removeExchange(Exchange exchange) throws AMQException
+ {
+
+ }
+
+ public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+
+ }
+
+ public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+
+ }
+
+ public void createQueue(AMQQueue queue) throws AMQException
+ {
+ // Not required to do anything
+ }
+
+ public void removeQueue(AMQShortString name) throws AMQException
+ {
+ // Not required to do anything
+ }
+
+ public void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException
+ {
+ // Not required to do anything
+ }
+
+ public void dequeueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException
+ {
+ // Not required to do anything
+ }
+
+ public void beginTran(StoreContext context) throws AMQException
+ {
+ // Not required to do anything
+ }
+
+ public void commitTran(StoreContext context) throws AMQException
+ {
+ // Not required to do anything
+ }
+
+ public void abortTran(StoreContext context) throws AMQException
+ {
+ // Not required to do anything
+ }
+
+ public boolean inTran(StoreContext context)
+ {
+ return false;
+ }
+
+ public List<AMQQueue> createQueues() throws AMQException
+ {
+ return null;
+ }
+
+ public Long getNewMessageId()
+ {
+ return _messageId.getAndIncrement();
+ }
+
+ public void storeContentBodyChunk(StoreContext context, Long messageId, int index, ContentChunk contentBody, boolean lastContentBody)
+ throws AMQException
+ {
+ List<ContentChunk> bodyList = _contentBodyMap.get(messageId);
+
+ if(bodyList == null && lastContentBody)
+ {
+ _contentBodyMap.put(messageId, Collections.singletonList(contentBody));
+ }
+ else
+ {
+ if (bodyList == null)
+ {
+ bodyList = new ArrayList<ContentChunk>();
+ _contentBodyMap.put(messageId, bodyList);
+ }
+
+ bodyList.add(index, contentBody);
+ }
+ }
+
+ public void storeMessageMetaData(StoreContext context, Long messageId, MessageMetaData messageMetaData)
+ throws AMQException
+ {
+ _metaDataMap.put(messageId, messageMetaData);
+ }
+
+ public MessageMetaData getMessageMetaData(StoreContext context,Long messageId) throws AMQException
+ {
+ return _metaDataMap.get(messageId);
+ }
+
+ public ContentChunk getContentBodyChunk(StoreContext context, Long messageId, int index) throws AMQException
+ {
+ List<ContentChunk> bodyList = _contentBodyMap.get(messageId);
+ return bodyList.get(index);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/Final/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java
new file mode 100644
index 0000000000..2a83d9b649
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java
@@ -0,0 +1,261 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import org.apache.commons.configuration.Configuration;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MessageMetaData;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+/**
+ * MessageStore defines the interface to a storage area, which can be used to preserve the state of messages, queues
+ * and exchanges in a transactional manner.
+ *
+ * <p/>All message store, remove, enqueue and dequeue operations are carried out against a {@link StoreContext} which
+ * encapsulates the transactional context they are performed in. Many such operations can be carried out in a single
+ * transaction.
+ *
+ * <p/>The storage and removal of queues and exchanges, are not carried out in a transactional context.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Accept transaction boundary demarcations: Begin, Commit, Abort.
+ * <tr><td> Store and remove queues.
+ * <tr><td> Store and remove exchanges.
+ * <tr><td> Store and remove messages.
+ * <tr><td> Bind and unbind queues to exchanges.
+ * <tr><td> Enqueue and dequeue messages to queues.
+ * <tr><td> Generate message identifiers.
+ * </table>
+ */
+public interface MessageStore
+{
+ /**
+ * Called after instantiation in order to configure the message store. A particular implementation can define
+ * whatever parameters it wants.
+ *
+ * @param virtualHost The virtual host using by this store
+ * @param base The base element identifier from which all configuration items are relative. For example, if
+ * the base element is "store", the all elements used by concrete classes will be "store.foo" etc.
+ * @param config The apache commons configuration object.
+ *
+ * @throws Exception If any error occurs that means the store is unable to configure itself.
+ */
+ void configure(VirtualHost virtualHost, String base, Configuration config) throws Exception;
+
+ /**
+ * Called to close and cleanup any resources used by the message store.
+ *
+ * @throws Exception If the close fails.
+ */
+ void close() throws Exception;
+
+ /**
+ * Removes the specified message from the store in the given transactional store context.
+ *
+ * @param storeContext The transactional context to remove the message in.
+ * @param messageId Identifies the message to remove.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void removeMessage(StoreContext storeContext, Long messageId) throws AMQException;
+
+ /**
+ * Makes the specified exchange persistent.
+ *
+ * @param exchange The exchange to persist.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void createExchange(Exchange exchange) throws AMQException;
+
+ /**
+ * Removes the specified persistent exchange.
+ *
+ * @param exchange The exchange to remove.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void removeExchange(Exchange exchange) throws AMQException;
+
+ /**
+ * Binds the specified queue to an exchange with a routing key.
+ *
+ * @param exchange The exchange to bind to.
+ * @param routingKey The routing key to bind by.
+ * @param queue The queue to bind.
+ * @param args Additional parameters.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException;
+
+ /**
+ * Unbinds the specified from an exchange under a particular routing key.
+ *
+ * @param exchange The exchange to unbind from.
+ * @param routingKey The routing key to unbind.
+ * @param queue The queue to unbind.
+ * @param args Additonal parameters.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException;
+
+ /**
+ * Makes the specified queue persistent.
+ *
+ * @param queue The queue to store.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void createQueue(AMQQueue queue) throws AMQException;
+
+ /**
+ * Removes the specified queue from the persistent store.
+ *
+ * @param name The queue to remove.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void removeQueue(AMQShortString name) throws AMQException;
+
+ /**
+ * Places a message onto a specified queue, in a given transactional context.
+ *
+ * @param context The transactional context for the operation.
+ * @param name The name of the queue to place the message on.
+ * @param messageId The message to enqueue.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException;
+
+ /**
+ * Extracts a message from a specified queue, in a given transactional context.
+ *
+ * @param context The transactional context for the operation.
+ * @param name The name of the queue to take the message from.
+ * @param messageId The message to dequeue.
+ *
+ * @throws AMQException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ void dequeueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException;
+
+ /**
+ * Begins a transactional context.
+ *
+ * @param context The transactional context to begin.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void beginTran(StoreContext context) throws AMQException;
+
+ /**
+ * Commits all operations performed within a given transactional context.
+ *
+ * @param context The transactional context to commit all operations for.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void commitTran(StoreContext context) throws AMQException;
+
+ /**
+ * Abandons all operations performed within a given transactional context.
+ *
+ * @param context The transactional context to abandon.
+ *
+ * @throws AMQException If the operation fails for any reason.
+ */
+ void abortTran(StoreContext context) throws AMQException;
+
+ /**
+ * Tests a transactional context to see if it has been begun but not yet committed or aborted.
+ *
+ * @param context The transactional context to test.
+ *
+ * @return <tt>true</tt> if the transactional context is live, <tt>false</tt> otherwise.
+ */
+ boolean inTran(StoreContext context);
+
+ /**
+ * Return a valid, currently unused message id.
+ *
+ * @return A fresh message id.
+ */
+ Long getNewMessageId();
+
+ /**
+ * Stores a chunk of message data.
+ *
+ * @param context The transactional context for the operation.
+ * @param messageId The message to store the data for.
+ * @param index The index of the data chunk.
+ * @param contentBody The content of the data chunk.
+ * @param lastContentBody Flag to indicate that this is the last such chunk for the message.
+ *
+ * @throws AMQException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ void storeContentBodyChunk(StoreContext context, Long messageId, int index, ContentChunk contentBody,
+ boolean lastContentBody) throws AMQException;
+
+ /**
+ * Stores message meta-data.
+ *
+ * @param context The transactional context for the operation.
+ * @param messageId The message to store the data for.
+ * @param messageMetaData The message meta data to store.
+ *
+ * @throws AMQException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ void storeMessageMetaData(StoreContext context, Long messageId, MessageMetaData messageMetaData) throws AMQException;
+
+ /**
+ * Retrieves message meta-data.
+ *
+ * @param context The transactional context for the operation.
+ * @param messageId The message to get the meta-data for.
+ *
+ * @return The message meta data.
+ *
+ * @throws AMQException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ MessageMetaData getMessageMetaData(StoreContext context, Long messageId) throws AMQException;
+
+ /**
+ * Retrieves a chunk of message data.
+ *
+ * @param context The transactional context for the operation.
+ * @param messageId The message to get the data chunk for.
+ * @param index The offset index of the data chunk within the message.
+ *
+ * @return A chunk of message data.
+ *
+ * @throws AMQException If the operation fails for any reason, or if the specified message does not exist.
+ */
+ ContentChunk getContentBodyChunk(StoreContext context, Long messageId, int index) throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java b/Final/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java
new file mode 100644
index 0000000000..3ee49d58cf
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.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.store;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A context that the store can use to associate with a transactional context. For example, it could store
+ * some kind of txn id.
+ *
+ * @author Apache Software Foundation
+ */
+public class StoreContext
+{
+ private static final Logger _logger = Logger.getLogger(StoreContext.class);
+
+ private String _name;
+ private Object _payload;
+
+ public StoreContext()
+ {
+ _name = super.toString();
+ }
+
+ public StoreContext(String name)
+ {
+ _name = name;
+ }
+
+ public Object getPayload()
+ {
+ return _payload;
+ }
+
+ public void setPayload(Object payload)
+ {
+ _logger.debug("public void setPayload(Object payload = " + payload + "): called");
+ _payload = payload;
+ }
+
+ /**
+ * Prints out the transactional context as a string, mainly for debugging purposes.
+ *
+ * @return The transactional context as a string.
+ */
+ public String toString()
+ {
+ return "<_name = " + _name + ", _payload = " + _payload + ">";
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java b/Final/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java
new file mode 100644
index 0000000000..a4ed859fa7
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.transport;
+
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.util.NewThreadExecutor;
+import org.apache.qpid.configuration.Configured;
+
+public class ConnectorConfiguration
+{
+ public static final String DEFAULT_PORT = "5672";
+
+ public static final String SSL_PORT = "8672";
+
+ @Configured(path = "connector.processors",
+ defaultValue = "4")
+ public int processors;
+
+ @Configured(path = "connector.port",
+ defaultValue = DEFAULT_PORT)
+ public int port;
+
+ @Configured(path = "connector.bind",
+ defaultValue = "wildcard")
+ public String bindAddress;
+
+ @Configured(path = "connector.socketReceiveBuffer",
+ defaultValue = "32767")
+ public int socketReceiveBufferSize;
+
+ @Configured(path = "connector.socketWriteBuffer",
+ defaultValue = "32767")
+ public int socketWriteBuferSize;
+
+ @Configured(path = "connector.tcpNoDelay",
+ defaultValue = "true")
+ public boolean tcpNoDelay;
+
+ @Configured(path = "advanced.filterchain[@enableExecutorPool]",
+ defaultValue = "false")
+ public boolean enableExecutorPool;
+
+ @Configured(path = "advanced.enablePooledAllocator",
+ defaultValue = "false")
+ public boolean enablePooledAllocator;
+
+ @Configured(path = "advanced.enableDirectBuffers",
+ defaultValue = "false")
+ public boolean enableDirectBuffers;
+
+ @Configured(path = "connector.ssl.enabled",
+ defaultValue = "false")
+ public boolean enableSSL;
+
+ @Configured(path = "connector.ssl.sslOnly",
+ defaultValue = "true")
+ public boolean sslOnly;
+
+ @Configured(path = "connector.ssl.port",
+ defaultValue = SSL_PORT)
+ public int sslPort;
+
+ @Configured(path = "connector.ssl.keystorePath",
+ defaultValue = "none")
+ public String keystorePath;
+
+ @Configured(path = "connector.ssl.keystorePassword",
+ defaultValue = "none")
+ public String keystorePassword;
+
+ @Configured(path = "connector.ssl.certType",
+ defaultValue = "SunX509")
+ public String certType;
+
+ public IoAcceptor createAcceptor()
+ {
+ return new org.apache.mina.transport.socket.nio.SocketAcceptor(processors, new NewThreadExecutor());
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java b/Final/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java
new file mode 100644
index 0000000000..bdd27f2d1c
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java
@@ -0,0 +1,705 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.transport;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.util.BlockingQueue;
+import org.apache.mina.util.ByteBufferUtil;
+import org.apache.mina.util.IdentityHashSet;
+import org.apache.mina.util.Queue;
+import org.apache.mina.util.Stack;
+
+/**
+ * A Thread-pooling filter. This filter forwards {@link IoHandler} events
+ * to its thread pool.
+ * <p/>
+ * This is an implementation of
+ * <a href="http://deuce.doc.wustl.edu/doc/pspdfs/lf.pdf">Leader/Followers
+ * thread pool</a> by Douglas C. Schmidt et al.
+ */
+public class ThreadPoolFilter extends IoFilterAdapter
+{
+ /**
+ * Default maximum size of thread pool (2G).
+ */
+ public static final int DEFAULT_MAXIMUM_POOL_SIZE = Integer.MAX_VALUE;
+
+ /**
+ * Default keep-alive time of thread pool (1 min).
+ */
+ public static final int DEFAULT_KEEP_ALIVE_TIME = 60 * 1000;
+
+ /**
+ * A queue which contains {@link Integer}s which represents reusable
+ * thread IDs. {@link Worker} first checks this queue and then
+ * uses {@link #threadId} when no reusable thread ID is available.
+ */
+ private static final Queue threadIdReuseQueue = new Queue();
+ private static int threadId = 0;
+
+ private static int acquireThreadId()
+ {
+ synchronized (threadIdReuseQueue)
+ {
+ Integer id = (Integer) threadIdReuseQueue.pop();
+ if (id == null)
+ {
+ return ++ threadId;
+ }
+ else
+ {
+ return id.intValue();
+ }
+ }
+ }
+
+ private static void releaseThreadId(int id)
+ {
+ synchronized (threadIdReuseQueue)
+ {
+ threadIdReuseQueue.push(new Integer(id));
+ }
+ }
+
+ private final String threadNamePrefix;
+ private final Map buffers = new IdentityHashMap();
+ private final BlockingQueue unfetchedSessionBuffers = new BlockingQueue();
+ private final Set allSessionBuffers = new IdentityHashSet();
+
+ private Worker leader;
+ private final Stack followers = new Stack();
+ private final Set allWorkers = new IdentityHashSet();
+
+ private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE;
+ private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME;
+
+ private boolean shuttingDown;
+
+ private int poolSize;
+ private final Object poolSizeLock = new Object();
+
+ /**
+ * Creates a new instance of this filter with default thread pool settings.
+ */
+ public ThreadPoolFilter()
+ {
+ this("IoThreadPool");
+ }
+
+ /**
+ * Creates a new instance of this filter with the specified thread name prefix
+ * and other default settings.
+ *
+ * @param threadNamePrefix the prefix of the thread names this pool will create.
+ */
+ public ThreadPoolFilter(String threadNamePrefix)
+ {
+ if (threadNamePrefix == null)
+ {
+ throw new NullPointerException("threadNamePrefix");
+ }
+ threadNamePrefix = threadNamePrefix.trim();
+ if (threadNamePrefix.length() == 0)
+ {
+ throw new IllegalArgumentException("threadNamePrefix is empty.");
+ }
+ this.threadNamePrefix = threadNamePrefix;
+ }
+
+ public String getThreadNamePrefix()
+ {
+ return threadNamePrefix;
+ }
+
+ public int getPoolSize()
+ {
+ synchronized (poolSizeLock)
+ {
+ return poolSize;
+ }
+ }
+
+ public int getMaximumPoolSize()
+ {
+ return maximumPoolSize;
+ }
+
+ public int getKeepAliveTime()
+ {
+ return keepAliveTime;
+ }
+
+ public void setMaximumPoolSize(int maximumPoolSize)
+ {
+ if (maximumPoolSize <= 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ this.maximumPoolSize = maximumPoolSize;
+ }
+
+ public void setKeepAliveTime(int keepAliveTime)
+ {
+ this.keepAliveTime = keepAliveTime;
+ }
+
+ public void init()
+ {
+ shuttingDown = false;
+ leader = new Worker();
+ leader.start();
+ leader.lead();
+ }
+
+ public void destroy()
+ {
+ shuttingDown = true;
+ int expectedPoolSize = 0;
+ while (getPoolSize() != expectedPoolSize)
+ {
+ List allWorkers;
+ synchronized (poolSizeLock)
+ {
+ allWorkers = new ArrayList(this.allWorkers);
+ }
+
+ // You may not interrupt the current thread.
+ if (allWorkers.remove(Thread.currentThread()))
+ {
+ expectedPoolSize = 1;
+ }
+
+ for (Iterator i = allWorkers.iterator(); i.hasNext();)
+ {
+ Worker worker = (Worker) i.next();
+ while (worker.isAlive())
+ {
+ worker.interrupt();
+ try
+ {
+ // This timeout will help us from
+ // infinite lock-up and interrupt workers again.
+ worker.join(100);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+
+ this.allSessionBuffers.clear();
+ this.unfetchedSessionBuffers.clear();
+ this.buffers.clear();
+ this.followers.clear();
+ this.leader = null;
+ }
+
+ private void increasePoolSize(Worker worker)
+ {
+ synchronized (poolSizeLock)
+ {
+ poolSize++;
+ allWorkers.add(worker);
+ }
+ }
+
+ private void decreasePoolSize(Worker worker)
+ {
+ synchronized (poolSizeLock)
+ {
+ poolSize--;
+ allWorkers.remove(worker);
+ }
+ }
+
+ private void fireEvent(NextFilter nextFilter, IoSession session,
+ EventType type, Object data)
+ {
+ final BlockingQueue unfetchedSessionBuffers = this.unfetchedSessionBuffers;
+ final Set allSessionBuffers = this.allSessionBuffers;
+ final Event event = new Event(type, nextFilter, data);
+
+ synchronized (unfetchedSessionBuffers)
+ {
+ final SessionBuffer buf = getSessionBuffer(session);
+ final Queue eventQueue = buf.eventQueue;
+
+ synchronized (buf)
+ {
+ eventQueue.push(event);
+ }
+
+ if (!allSessionBuffers.contains(buf))
+ {
+ allSessionBuffers.add(buf);
+ unfetchedSessionBuffers.push(buf);
+ }
+ }
+ }
+
+ /**
+ * Implement this method to fetch (or pop) a {@link SessionBuffer} from
+ * the given <tt>unfetchedSessionBuffers</tt>. The default implementation
+ * simply pops the buffer from it. You could prioritize the fetch order.
+ *
+ * @return A non-null {@link SessionBuffer}
+ */
+ protected SessionBuffer fetchSessionBuffer(Queue unfetchedSessionBuffers)
+ {
+ return (SessionBuffer) unfetchedSessionBuffers.pop();
+ }
+
+ private SessionBuffer getSessionBuffer(IoSession session)
+ {
+ final Map buffers = this.buffers;
+ SessionBuffer buf = (SessionBuffer) buffers.get(session);
+ if (buf == null)
+ {
+ synchronized (buffers)
+ {
+ buf = (SessionBuffer) buffers.get(session);
+ if (buf == null)
+ {
+ buf = new SessionBuffer(session);
+ buffers.put(session, buf);
+ }
+ }
+ }
+ return buf;
+ }
+
+ private void removeSessionBuffer(SessionBuffer buf)
+ {
+ final Map buffers = this.buffers;
+ final IoSession session = buf.session;
+ synchronized (buffers)
+ {
+ buffers.remove(session);
+ }
+ }
+
+ protected static class SessionBuffer
+ {
+ private final IoSession session;
+
+ private final Queue eventQueue = new Queue();
+
+ private SessionBuffer(IoSession session)
+ {
+ this.session = session;
+ }
+
+ public IoSession getSession()
+ {
+ return session;
+ }
+
+ public Queue getEventQueue()
+ {
+ return eventQueue;
+ }
+ }
+
+ private class Worker extends Thread
+ {
+ private final int id;
+ private final Object promotionLock = new Object();
+ private boolean dead;
+
+ private Worker()
+ {
+ int id = acquireThreadId();
+ this.id = id;
+ this.setName(threadNamePrefix + '-' + id);
+ increasePoolSize(this);
+ }
+
+ public boolean lead()
+ {
+ final Object promotionLock = this.promotionLock;
+ synchronized (promotionLock)
+ {
+ if (dead)
+ {
+ return false;
+ }
+
+ leader = this;
+ promotionLock.notify();
+ }
+
+ return true;
+ }
+
+ public void run()
+ {
+ for (; ;)
+ {
+ if (!waitForPromotion())
+ {
+ break;
+ }
+
+ SessionBuffer buf = fetchBuffer();
+ giveUpLead();
+ if (buf == null)
+ {
+ break;
+ }
+
+ processEvents(buf);
+ follow();
+ releaseBuffer(buf);
+ }
+
+ decreasePoolSize(this);
+ releaseThreadId(id);
+ }
+
+ private SessionBuffer fetchBuffer()
+ {
+ BlockingQueue unfetchedSessionBuffers = ThreadPoolFilter.this.unfetchedSessionBuffers;
+ synchronized (unfetchedSessionBuffers)
+ {
+ while (!shuttingDown)
+ {
+ try
+ {
+ unfetchedSessionBuffers.waitForNewItem();
+ }
+ catch (InterruptedException e)
+ {
+ continue;
+ }
+
+ return ThreadPoolFilter.this.fetchSessionBuffer(unfetchedSessionBuffers);
+ }
+ }
+
+ return null;
+ }
+
+ private void processEvents(SessionBuffer buf)
+ {
+ final IoSession session = buf.session;
+ final Queue eventQueue = buf.eventQueue;
+ for (; ;)
+ {
+ Event event;
+ synchronized (buf)
+ {
+ event = (Event) eventQueue.pop();
+ if (event == null)
+ {
+ break;
+ }
+ }
+ processEvent(event.getNextFilter(), session,
+ event.getType(), event.getData());
+ }
+ }
+
+ private void follow()
+ {
+ final Object promotionLock = this.promotionLock;
+ final Stack followers = ThreadPoolFilter.this.followers;
+ synchronized (promotionLock)
+ {
+ if (this != leader)
+ {
+ synchronized (followers)
+ {
+ followers.push(this);
+ }
+ }
+ }
+ }
+
+ private void releaseBuffer(SessionBuffer buf)
+ {
+ final BlockingQueue unfetchedSessionBuffers = ThreadPoolFilter.this.unfetchedSessionBuffers;
+ final Set allSessionBuffers = ThreadPoolFilter.this.allSessionBuffers;
+ final Queue eventQueue = buf.eventQueue;
+
+ synchronized (unfetchedSessionBuffers)
+ {
+ if (eventQueue.isEmpty())
+ {
+ allSessionBuffers.remove(buf);
+ removeSessionBuffer(buf);
+ }
+ else
+ {
+ unfetchedSessionBuffers.push(buf);
+ }
+ }
+ }
+
+ private boolean waitForPromotion()
+ {
+ final Object promotionLock = this.promotionLock;
+
+ long startTime = System.currentTimeMillis();
+ long currentTime = System.currentTimeMillis();
+
+ synchronized (promotionLock)
+ {
+ while (this != leader && !shuttingDown)
+ {
+ // Calculate remaining keep-alive time
+ int keepAliveTime = getKeepAliveTime();
+ if (keepAliveTime > 0)
+ {
+ keepAliveTime -= (currentTime - startTime);
+ }
+ else
+ {
+ keepAliveTime = Integer.MAX_VALUE;
+ }
+
+ // Break the loop if there's no remaining keep-alive time.
+ if (keepAliveTime <= 0)
+ {
+ break;
+ }
+
+ // Wait for promotion
+ try
+ {
+ promotionLock.wait(keepAliveTime);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ // Update currentTime for the next iteration
+ currentTime = System.currentTimeMillis();
+ }
+
+ boolean timeToLead = this == leader && !shuttingDown;
+
+ if (!timeToLead)
+ {
+ // time to die
+ synchronized (followers)
+ {
+ followers.remove(this);
+ }
+
+ // Mark as dead explicitly when we've got promotionLock.
+ dead = true;
+ }
+
+ return timeToLead;
+ }
+ }
+
+ private void giveUpLead()
+ {
+ final Stack followers = ThreadPoolFilter.this.followers;
+ Worker worker;
+ do
+ {
+ synchronized (followers)
+ {
+ worker = (Worker) followers.pop();
+ }
+
+ if (worker == null)
+ {
+ // Increase the number of threads if we
+ // are not shutting down and we can increase the number.
+ if (!shuttingDown
+ && getPoolSize() < getMaximumPoolSize())
+ {
+ worker = new Worker();
+ worker.lead();
+ worker.start();
+ }
+
+ // This loop should end because:
+ // 1) lead() is called already,
+ // 2) or it is shutting down and there's no more threads left.
+ break;
+ }
+ }
+ while (!worker.lead());
+ }
+ }
+
+ protected static class EventType
+ {
+ public static final EventType OPENED = new EventType("OPENED");
+
+ public static final EventType CLOSED = new EventType("CLOSED");
+
+ public static final EventType READ = new EventType("READ");
+
+ public static final EventType WRITTEN = new EventType("WRITTEN");
+
+ public static final EventType RECEIVED = new EventType("RECEIVED");
+
+ public static final EventType SENT = new EventType("SENT");
+
+ public static final EventType IDLE = new EventType("IDLE");
+
+ public static final EventType EXCEPTION = new EventType("EXCEPTION");
+
+ private final String value;
+
+ private EventType(String value)
+ {
+ this.value = value;
+ }
+
+ public String toString()
+ {
+ return value;
+ }
+ }
+
+ protected static class Event
+ {
+ private final EventType type;
+ private final NextFilter nextFilter;
+ private final Object data;
+
+ public Event(EventType type, NextFilter nextFilter, Object data)
+ {
+ this.type = type;
+ this.nextFilter = nextFilter;
+ this.data = data;
+ }
+
+ public Object getData()
+ {
+ return data;
+ }
+
+
+ public NextFilter getNextFilter()
+ {
+ return nextFilter;
+ }
+
+
+ public EventType getType()
+ {
+ return type;
+ }
+ }
+
+ public void sessionCreated(NextFilter nextFilter, IoSession session)
+ {
+ nextFilter.sessionCreated(session);
+ }
+
+ public void sessionOpened(NextFilter nextFilter,
+ IoSession session)
+ {
+ fireEvent(nextFilter, session, EventType.OPENED, null);
+ }
+
+ public void sessionClosed(NextFilter nextFilter,
+ IoSession session)
+ {
+ fireEvent(nextFilter, session, EventType.CLOSED, null);
+ }
+
+ public void sessionIdle(NextFilter nextFilter,
+ IoSession session, IdleStatus status)
+ {
+ fireEvent(nextFilter, session, EventType.IDLE, status);
+ }
+
+ public void exceptionCaught(NextFilter nextFilter,
+ IoSession session, Throwable cause)
+ {
+ fireEvent(nextFilter, session, EventType.EXCEPTION, cause);
+ }
+
+ public void messageReceived(NextFilter nextFilter,
+ IoSession session, Object message)
+ {
+ ByteBufferUtil.acquireIfPossible(message);
+ fireEvent(nextFilter, session, EventType.RECEIVED, message);
+ }
+
+ public void messageSent(NextFilter nextFilter,
+ IoSession session, Object message)
+ {
+ ByteBufferUtil.acquireIfPossible(message);
+ fireEvent(nextFilter, session, EventType.SENT, message);
+ }
+
+ protected void processEvent(NextFilter nextFilter,
+ IoSession session, EventType type,
+ Object data)
+ {
+ if (type == EventType.RECEIVED)
+ {
+ nextFilter.messageReceived(session, data);
+ ByteBufferUtil.releaseIfPossible(data);
+ }
+ else if (type == EventType.SENT)
+ {
+ nextFilter.messageSent(session, data);
+ ByteBufferUtil.releaseIfPossible(data);
+ }
+ else if (type == EventType.EXCEPTION)
+ {
+ nextFilter.exceptionCaught(session, (Throwable) data);
+ }
+ else if (type == EventType.IDLE)
+ {
+ nextFilter.sessionIdle(session, (IdleStatus) data);
+ }
+ else if (type == EventType.OPENED)
+ {
+ nextFilter.sessionOpened(session);
+ }
+ else if (type == EventType.CLOSED)
+ {
+ nextFilter.sessionClosed(session);
+ }
+ }
+
+ public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest)
+ {
+ nextFilter.filterWrite(session, writeRequest);
+ }
+
+ public void filterClose(NextFilter nextFilter, IoSession session) throws Exception
+ {
+ nextFilter.filterClose(session);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java
new file mode 100644
index 0000000000..988f589339
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.txn;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.NoConsumersException;
+import org.apache.qpid.server.store.StoreContext;
+
+import java.util.List;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class CleanupMessageOperation implements TxnOp
+{
+ private static final Logger _log = Logger.getLogger(CleanupMessageOperation.class);
+
+ private final AMQMessage _msg;
+
+ private final List<RequiredDeliveryException> _returns;
+
+ public CleanupMessageOperation(AMQMessage msg, List<RequiredDeliveryException> returns)
+ {
+ _msg = msg;
+ _returns = returns;
+ }
+
+ public void prepare(StoreContext context) throws AMQException
+ { }
+
+ public void undoPrepare()
+ {
+ // don't need to do anything here, if the store's txn failed
+ // when processing prepare then the message was not stored
+ // or enqueued on any queues and can be discarded
+ }
+
+ public void commit(StoreContext context)
+ {
+ // No-op can't be done here has this is before the message has been attempted to be delivered.
+ /*try
+ {
+ _msg.checkDeliveredToConsumer();
+ }
+ catch (NoConsumersException e)
+ {
+ _returns.add(e);
+ }*/
+ }
+
+ public void rollback(StoreContext context)
+ {
+ // NO OP
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java
new file mode 100644
index 0000000000..9068f871cb
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java
@@ -0,0 +1,262 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.txn;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.ack.TxAck;
+import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.NoConsumersException;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoreContext;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/** A transactional context that only supports local transactions. */
+public class LocalTransactionalContext implements TransactionalContext
+{
+ private static final Logger _log = Logger.getLogger(LocalTransactionalContext.class);
+
+ private final TxnBuffer _txnBuffer = new TxnBuffer();
+
+ private final List<DeliveryDetails> _postCommitDeliveryList = new LinkedList<DeliveryDetails>();
+
+ /**
+ * We keep hold of the ack operation so that we can consolidate acks, i.e. multiple acks within a txn are
+ * consolidated into a single operation
+ */
+ private TxAck _ackOp;
+
+ private List<RequiredDeliveryException> _returnMessages;
+
+ private final MessageStore _messageStore;
+
+ private final StoreContext _storeContext;
+
+ private boolean _inTran = false;
+
+ /** Are there messages to deliver. NOT Has the message been delivered */
+ private boolean _messageDelivered = false;
+
+ private static class DeliveryDetails
+ {
+ public AMQMessage message;
+ public AMQQueue queue;
+ private boolean deliverFirst;
+
+ public DeliveryDetails(AMQMessage message, AMQQueue queue, boolean deliverFirst)
+ {
+ this.message = message;
+ this.queue = queue;
+ this.deliverFirst = deliverFirst;
+ }
+ }
+
+ public LocalTransactionalContext(MessageStore messageStore, StoreContext storeContext,
+ List<RequiredDeliveryException> returnMessages)
+ {
+ _messageStore = messageStore;
+ _storeContext = storeContext;
+ _returnMessages = returnMessages;
+ // _txnBuffer.enlist(new StoreMessageOperation(messageStore));
+ }
+
+ public StoreContext getStoreContext()
+ {
+ return _storeContext;
+ }
+
+ public void rollback() throws AMQException
+ {
+ _txnBuffer.rollback(_storeContext);
+ // Hack to deal with uncommitted non-transactional writes
+ if (_messageStore.inTran(_storeContext))
+ {
+ _messageStore.abortTran(_storeContext);
+ _inTran = false;
+ }
+
+ _postCommitDeliveryList.clear();
+ }
+
+ public void deliver(AMQMessage message, AMQQueue queue, boolean deliverFirst) throws AMQException
+ {
+ // A publication will result in the enlisting of several
+ // TxnOps. The first is an op that will store the message.
+ // Following that (and ordering is important), an op will
+ // be added for every queue onto which the message is
+ // enqueued. Finally a cleanup op will be added to decrement
+ // the reference associated with the routing.
+ // message.incrementReference();
+ _postCommitDeliveryList.add(new DeliveryDetails(message, queue, deliverFirst));
+ _messageDelivered = true;
+ _txnBuffer.enlist(new CleanupMessageOperation(message, _returnMessages));
+ /*_txnBuffer.enlist(new DeliverMessageOperation(message, queue));
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Incrementing ref count on message and enlisting cleanup operation - id " +
+ message.getMessageId());
+ }
+ message.incrementReference();
+ _messageDelivered = true;
+
+ */
+ }
+
+ private void checkAck(long deliveryTag, UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException
+ {
+ if (!unacknowledgedMessageMap.contains(deliveryTag))
+ {
+ throw new AMQException("Ack with delivery tag " + deliveryTag + " not known for channel");
+ }
+ }
+
+ public void acknowledgeMessage(long deliveryTag, long lastDeliveryTag, boolean multiple,
+ UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException
+ {
+ // check that the tag exists to give early failure
+ if (!multiple || (deliveryTag > 0))
+ {
+ checkAck(deliveryTag, unacknowledgedMessageMap);
+ }
+ // we use a single txn op for all acks and update this op
+ // as new acks come in. If this is the first ack in the txn
+ // we will need to create and enlist the op.
+ if (_ackOp == null)
+ {
+ beginTranIfNecessary();
+ _ackOp = new TxAck(unacknowledgedMessageMap);
+ _txnBuffer.enlist(_ackOp);
+ }
+ // update the op to include this ack request
+ if (multiple && (deliveryTag == 0))
+ {
+ // if have signalled to ack all, that refers only
+ // to all at this time
+ _ackOp.update(lastDeliveryTag, multiple);
+ }
+ else
+ {
+ _ackOp.update(deliveryTag, multiple);
+ }
+ }
+
+ public void messageFullyReceived(boolean persistent) throws AMQException
+ {
+ // Not required in this transactional context
+ }
+
+ public void messageProcessed(AMQProtocolSession protocolSession) throws AMQException
+ {
+ // Not required in this transactional context
+ }
+
+ public void beginTranIfNecessary() throws AMQException
+ {
+ if (!_inTran)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Starting transaction on message store: " + this);
+ }
+
+ _messageStore.beginTran(_storeContext);
+ _inTran = true;
+ }
+ }
+
+ public void commit() throws AMQException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Committing transactional context: " + this);
+ }
+
+ if (_ackOp != null)
+ {
+
+ _messageDelivered = true;
+ _ackOp.consolidate();
+ // already enlisted, after commit will reset regardless of outcome
+ _ackOp = null;
+ }
+
+ if (_messageDelivered && _inTran)
+ {
+ _txnBuffer.enlist(new StoreMessageOperation(_messageStore));
+ }
+ // fixme fail commit here ... QPID-440
+ try
+ {
+ _txnBuffer.commit(_storeContext);
+ }
+ finally
+ {
+ _messageDelivered = false;
+ _inTran = _messageStore.inTran(_storeContext);
+ }
+
+ try
+ {
+ postCommitDelivery(_returnMessages);
+ }
+ catch (AMQException e)
+ {
+ // OK so what do we do now...?
+ _log.error("Failed to deliver messages following txn commit: " + e, e);
+ }
+ }
+
+ private void postCommitDelivery(List<RequiredDeliveryException> returnMessages) throws AMQException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Performing post commit delivery");
+ }
+
+ try
+ {
+ for (DeliveryDetails dd : _postCommitDeliveryList)
+ {
+ dd.queue.process(_storeContext, dd.message, dd.deliverFirst);
+
+ try
+ {
+ dd.message.checkDeliveredToConsumer();
+ }
+ catch (NoConsumersException nce)
+ {
+ returnMessages.add(nce);
+ }
+ }
+ }
+ finally
+ {
+ _postCommitDeliveryList.clear();
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
new file mode 100644
index 0000000000..b3d69543d4
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
@@ -0,0 +1,227 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.txn;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.ack.UnacknowledgedMessage;
+import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.NoConsumersException;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoreContext;
+
+/** @author Apache Software Foundation */
+public class NonTransactionalContext implements TransactionalContext
+{
+ private static final Logger _log = Logger.getLogger(NonTransactionalContext.class);
+
+ /** Channel is useful for logging */
+ private final AMQChannel _channel;
+
+ /** Where to put undeliverable messages */
+ private final List<RequiredDeliveryException> _returnMessages;
+
+ private Set<Long> _browsedAcks;
+
+ private final MessageStore _messageStore;
+
+ private StoreContext _storeContext;
+
+ /** Whether we are in a transaction */
+ private boolean _inTran;
+
+ public NonTransactionalContext(MessageStore messageStore, StoreContext storeContext, AMQChannel channel,
+ List<RequiredDeliveryException> returnMessages, Set<Long> browsedAcks)
+ {
+ _channel = channel;
+ _storeContext = storeContext;
+ _returnMessages = returnMessages;
+ _messageStore = messageStore;
+ _browsedAcks = browsedAcks;
+ }
+
+
+ public StoreContext getStoreContext()
+ {
+ return _storeContext;
+ }
+
+ public void beginTranIfNecessary() throws AMQException
+ {
+ if (!_inTran)
+ {
+ _messageStore.beginTran(_storeContext);
+ _inTran = true;
+ }
+ }
+
+ public void commit() throws AMQException
+ {
+ // Does not apply to this context
+ }
+
+ public void rollback() throws AMQException
+ {
+ // Does not apply to this context
+ }
+
+ public void deliver(AMQMessage message, AMQQueue queue, boolean deliverFirst) throws AMQException
+ {
+ try
+ {
+ queue.process(_storeContext, message, deliverFirst);
+ //following check implements the functionality
+ //required by the 'immediate' flag:
+ message.checkDeliveredToConsumer();
+ }
+ catch (NoConsumersException e)
+ {
+ _returnMessages.add(e);
+ }
+ }
+
+ public void acknowledgeMessage(final long deliveryTag, long lastDeliveryTag,
+ boolean multiple, final UnacknowledgedMessageMap unacknowledgedMessageMap)
+ throws AMQException
+ {
+ if (multiple)
+ {
+ if (deliveryTag == 0)
+ {
+
+ //Spec 2.1.6.11 ... If the multiple field is 1, and the delivery tag is zero,
+ // tells the server to acknowledge all outstanding mesages.
+ _log.info("Multiple ack on delivery tag 0. ACKing all messages. Current count:" +
+ unacknowledgedMessageMap.size());
+ unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
+ {
+ public boolean callback(UnacknowledgedMessage message) throws AMQException
+ {
+ if (!_browsedAcks.contains(deliveryTag))
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Discarding message: " + message.message.getMessageId());
+ }
+
+ //Message has been ack so discard it. This will dequeue and decrement the reference.
+ message.discard(_storeContext);
+ }
+ else
+ {
+ _browsedAcks.remove(deliveryTag);
+ }
+ return false;
+ }
+
+ public void visitComplete()
+ {
+ unacknowledgedMessageMap.clear();
+ }
+ });
+ }
+ else
+ {
+ if (!unacknowledgedMessageMap.contains(deliveryTag))
+ {
+ throw new AMQException("Multiple ack on delivery tag " + deliveryTag + " not known for channel");
+ }
+
+ LinkedList<UnacknowledgedMessage> acked = new LinkedList<UnacknowledgedMessage>();
+ unacknowledgedMessageMap.drainTo(acked, deliveryTag);
+ for (UnacknowledgedMessage msg : acked)
+ {
+ if (!_browsedAcks.contains(deliveryTag))
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Discarding message: " + msg.message.getMessageId());
+ }
+
+ //Message has been ack so discard it. This will dequeue and decrement the reference.
+ msg.discard(_storeContext);
+ }
+ else
+ {
+ _browsedAcks.remove(deliveryTag);
+ }
+ }
+ }
+ }
+ else
+ {
+ UnacknowledgedMessage msg;
+ msg = unacknowledgedMessageMap.remove(deliveryTag);
+
+ if (msg == null)
+ {
+ _log.info("Single ack on delivery tag " + deliveryTag + " not known for channel:" +
+ _channel.getChannelId());
+ throw new AMQException("Single ack on delivery tag " + deliveryTag + " not known for channel:" +
+ _channel.getChannelId());
+ }
+
+ if (!_browsedAcks.contains(deliveryTag))
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Discarding message: " + msg.message.getMessageId());
+ }
+
+ //Message has been ack so discard it. This will dequeue and decrement the reference.
+ msg.discard(_storeContext);
+ }
+ else
+ {
+ _browsedAcks.remove(deliveryTag);
+ }
+
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Received non-multiple ack for messaging with delivery tag " + deliveryTag + " msg id " +
+ msg.message.getMessageId());
+ }
+ }
+ }
+
+ public void messageFullyReceived(boolean persistent) throws AMQException
+ {
+ if (persistent)
+ {
+ _messageStore.commitTran(_storeContext);
+ _inTran = false;
+ }
+ }
+
+ public void messageProcessed(AMQProtocolSession protocolSession) throws AMQException
+ {
+ _channel.processReturns(protocolSession);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java
new file mode 100644
index 0000000000..0e4d6c2030
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.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.txn;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoreContext;
+
+/**
+ * A transactional operation to store messages in an underlying persistent store. When this operation
+ * commits it will do everything to ensure that all messages are safely committed to persistent
+ * storage.
+ */
+public class StoreMessageOperation implements TxnOp
+{
+ private final MessageStore _messsageStore;
+
+ public StoreMessageOperation(MessageStore messageStore)
+ {
+ _messsageStore = messageStore;
+ }
+
+ public void prepare(StoreContext context) throws AMQException
+ {
+ }
+
+ public void undoPrepare()
+ {
+ }
+
+ public void commit(StoreContext context) throws AMQException
+ {
+ _messsageStore.commitTran(context);
+ }
+
+ public void rollback(StoreContext context) throws AMQException
+ {
+ _messsageStore.abortTran(context);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java
new file mode 100644
index 0000000000..fee25c07df
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.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.server.txn;
+
+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.store.StoreContext;
+
+/**
+ * TransactionalContext provides a context in which transactional operations on {@link AMQMessage}s are performed.
+ * Different levels of transactional support for the delivery of messages may be provided by different implementations
+ * of this interface.
+ *
+ * <p/>The fundamental transactional operations that can be performed on a message queue are 'enqueue' and 'dequeue'.
+ * In this interface, these have been recast as the {@link #messageFullyReceived} and {@link #acknowledgeMessage}
+ * operations. This interface essentially provides a way to make enqueueing and dequeuing transactional.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Explicitly accept a transaction start notification.
+ * <tr><td> Commit all pending operations in a transaction.
+ * <tr><td> Rollback all pending operations in a transaction.
+ * <tr><td> Deliver a message to a queue as part of a transaction.
+ * <tr><td> Redeliver a message to a queue as part of a transaction.
+ * <tr><td> Mark a message as acknowledged as part of a transaction.
+ * <tr><td> Accept notification that a message has been completely received as part of a transaction.
+ * <tr><td> Accept notification that a message has been fully processed as part of a transaction.
+ * <tr><td> Associate a message store context with this transaction context.
+ * </table>
+ *
+ * @todo The 'fullyReceived' and 'messageProcessed' events sit uncomfortably in the responsibilities of a transactional
+ * context. They are non-transactional operations, used to trigger other side-effects. Consider moving them
+ * somewhere else, a seperate interface for example.
+ *
+ * @todo This transactional context could be written as a wrapper extension to a Queue implementation, that provides
+ * transactional management of the enqueue and dequeue operations, with added commit/rollback methods. Any
+ * queue implementation could be made transactional by wrapping it as a transactional queue. This would mean
+ * that the enqueue/dequeue operations do not need to be recast as deliver/acknowledge operations, which may be
+ * conceptually neater.
+ *
+ * For example:
+ * <pre>
+ * public interface Transactional
+ * {
+ * public void commit();
+ * public void rollback();
+ * }
+ *
+ * public interface TransactionalQueue<E> extends Transactional, SizeableQueue<E>
+ * {}
+ *
+ * public class Queues
+ * {
+ * ...
+ * // For transactional messaging, take a transactional view onto the queue.
+ * public static <E> TransactionalQueue<E> getTransactionalQueue(SizeableQueue<E> queue) { ... }
+ *
+ * // For non-transactional messaging, take a non-transactional view onto the queue.
+ * public static <E> TransactionalQueue<E> getNonTransactionalQueue(SizeableQueue<E> queue) { ... }
+ * }
+ * </pre>
+ */
+public interface TransactionalContext
+{
+ /**
+ * Explicitly begins the transaction, if it has not already been started. {@link #commit} or {@link #rollback}
+ * should automatically begin the next transaction in the chain.
+ *
+ * @throws AMQException If the transaction cannot be started for any reason.
+ */
+ void beginTranIfNecessary() throws AMQException;
+
+ /**
+ * Makes all pending operations on the transaction permanent and visible.
+ *
+ * @throws AMQException If the transaction cannot be committed for any reason.
+ */
+ void commit() throws AMQException;
+
+ /**
+ * Erases all pending operations on the transaction.
+ *
+ * @throws AMQException If the transaction cannot be committed for any reason.
+ */
+ void rollback() throws AMQException;
+
+ /**
+ * Delivers the specified message to the specified queue. A 'deliverFirst' flag may be set if the message is a
+ * redelivery, and should be placed on the front of the queue.
+ *
+ * <p/>This is an 'enqueue' operation.
+ *
+ * @param message The message to deliver.
+ * @param queue The queue to deliver the message to.
+ * @param deliverFirst <tt>true</tt> to place the message on the front of the queue for redelivery, <tt>false</tt>
+ * for normal FIFO message ordering.
+ *
+ * @throws AMQException If the message cannot be delivered for any reason.
+ */
+ void deliver(AMQMessage message, AMQQueue queue, boolean deliverFirst) throws AMQException;
+
+ /**
+ * Acknowledges a message or many messages as delivered. All messages up to a specified one, may be acknowledged by
+ * setting the 'multiple' flag. It is also possible for the acknowledged message id to be zero, when the 'multiple'
+ * flag is set, in which case an acknowledgement up to the latest delivered message should be done.
+ *
+ * <p/>This is a 'dequeue' operation.
+ *
+ * @param deliveryTag The id of the message to acknowledge, or zero, if using multiple acknowledgement
+ * up to the latest message.
+ * @param lastDeliveryTag The latest message delivered.
+ * @param multiple <tt>true</tt> if all message ids up the acknowledged one or latest delivered, are
+ * to be acknowledged, <tt>false</tt> otherwise.
+ * @param unacknowledgedMessageMap The unacknowledged messages in the transaction, to remove the acknowledged message
+ * from.
+ *
+ * @throws AMQException If the message cannot be acknowledged for any reason.
+ */
+ void acknowledgeMessage(long deliveryTag, long lastDeliveryTag, boolean multiple,
+ UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException;
+
+ /**
+ * Notifies the transactional context that a message has been fully received. The actual message that was received
+ * is not specified. This event may be used to trigger a process related to the receipt of the message, for example,
+ * flushing its data to disk.
+ *
+ * @param persistent <tt>true</tt> if the received message is persistent, <tt>false</tt> otherwise.
+ *
+ * @throws AMQException If the fully received event cannot be processed for any reason.
+ */
+ void messageFullyReceived(boolean persistent) throws AMQException;
+
+ /**
+ * Notifies the transactional context that a message has been delivered, succesfully or otherwise. The actual
+ * message that was delivered is not specified. This event may be used to trigger a process related to the
+ * outcome of the delivery of the message, for example, cleaning up failed deliveries.
+ *
+ * @param protocolSession The protocol session of the deliverable message.
+ *
+ * @throws AMQException If the message processed event cannot be handled for any reason.
+ */
+ void messageProcessed(AMQProtocolSession protocolSession) throws AMQException;
+
+ /**
+ * Gets the message store context associated with this transactional context.
+ *
+ * @return The message store context associated with this transactional context.
+ */
+ StoreContext getStoreContext();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java
new file mode 100644
index 0000000000..46a68b6a23
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.txn;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.store.StoreContext;
+
+/** Holds a list of TxnOp instance representing transactional operations. */
+public class TxnBuffer
+{
+ private final List<TxnOp> _ops = new ArrayList<TxnOp>();
+ private static final Logger _log = Logger.getLogger(TxnBuffer.class);
+
+ public TxnBuffer()
+ {
+ }
+
+ public void commit(StoreContext context) throws AMQException
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Committing " + _ops.size() + " ops to commit.:" + _ops);
+ }
+
+ if (prepare(context))
+ {
+ for (TxnOp op : _ops)
+ {
+ op.commit(context);
+ }
+ }
+ _ops.clear();
+ }
+
+ private boolean prepare(StoreContext context) throws AMQException
+ {
+ for (int i = 0; i < _ops.size(); i++)
+ {
+ TxnOp op = _ops.get(i);
+ try
+ {
+ op.prepare(context);
+ }
+ catch (AMQException e)
+ {
+ undoPrepare(i);
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ undoPrepare(i);
+ throw e;
+ }
+ }
+ return true;
+ }
+
+ private void undoPrepare(int lastPrepared)
+ {
+ //compensate previously prepared ops
+ for (int j = 0; j < lastPrepared; j++)
+ {
+ _ops.get(j).undoPrepare();
+ }
+ }
+
+
+
+ public void rollback(StoreContext context) throws AMQException
+ {
+ for (TxnOp op : _ops)
+ {
+ op.rollback(context);
+ }
+ _ops.clear();
+ }
+
+ public void enlist(TxnOp op)
+ {
+ _ops.add(op);
+ }
+
+ public void cancel(TxnOp op)
+ {
+ _ops.remove(op);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java
new file mode 100644
index 0000000000..919c078cf0
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.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.server.txn;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.store.StoreContext;
+
+/**
+ * This provides the abstraction of an individual operation within a
+ * transaction. It is used by the TxnBuffer class.
+ */
+public interface TxnOp
+{
+ /**
+ * Do the part of the operation that updates persistent state
+ */
+ public void prepare(StoreContext context) throws AMQException;
+ /**
+ * Complete the operation started by prepare. Can now update in
+ * memory state or make netork transfers.
+ */
+ public void commit(StoreContext context) throws AMQException;
+ /**
+ * This is not the same as rollback. Unfortunately the use of an
+ * in memory reference count as a locking mechanism and a test for
+ * whether a message should be deleted means that as things are,
+ * handling an acknowledgement unavoidably alters both memory and
+ * persistent state on prepare. This is needed to 'compensate' or
+ * undo the in-memory change if the peristent update of later ops
+ * fails.
+ */
+ public void undoPrepare();
+ /**
+ * Rolls back the operation.
+ */
+ public void rollback(StoreContext context) throws AMQException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java b/Final/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java
new file mode 100644
index 0000000000..e730e2f3c3
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.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.server.util;
+
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+
+public class CircularBuffer implements Iterable
+{
+
+ private static final Logger _logger = Logger.getLogger(CircularBuffer.class);
+
+ private final Object[] _log;
+ private int _size;
+ private int _index;
+
+ public CircularBuffer(int size)
+ {
+ _log = new Object[size];
+ }
+
+ public void add(Object o)
+ {
+ _log[_index++] = o;
+ _size = Math.min(_size+1, _log.length);
+ if(_index >= _log.length)
+ {
+ _index = 0;
+ }
+ }
+
+ public Object get(int i)
+ {
+ if(i >= _log.length)
+ {
+ throw new ArrayIndexOutOfBoundsException(i);
+ }
+ return _log[index(i)];
+ }
+
+ public int size() {
+ return _size;
+ }
+
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ private int i = 0;
+
+ public boolean hasNext()
+ {
+ return i < _size;
+ }
+
+ public Object next()
+ {
+ return get(i++);
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public String toString()
+ {
+ StringBuilder s = new StringBuilder();
+ boolean first = true;
+ for(Object o : this)
+ {
+ if(!first)
+ {
+ s.append(", ");
+ }
+ else
+ {
+ first = false;
+ }
+ s.append(o);
+ }
+ return s.toString();
+ }
+
+ public void dump()
+ {
+ for(Object o : this)
+ {
+ _logger.info(o);
+ }
+ }
+
+ int index(int i)
+ {
+ return _size == _log.length ? (_index + i) % _log.length : i;
+ }
+
+ public static void main(String[] artgv)
+ {
+ String[] items = new String[]{
+ "A","B","C","D","E","F","G","H","I","J","K"
+ };
+ CircularBuffer buffer = new CircularBuffer(5);
+ for(String s : items)
+ {
+ buffer.add(s);
+ _logger.info(buffer);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java b/Final/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java
new file mode 100644
index 0000000000..cf5e71a6e2
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class ConcurrentLinkedQueueNoSize<E> extends ConcurrentLinkedQueue<E>
+{
+ public int size()
+ {
+ if (isEmpty())
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java b/Final/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java
new file mode 100644
index 0000000000..eda97e0ed2
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+
+/**
+ * Dynamic proxy that records invocations in a fixed size circular buffer,
+ * dumping details on hitting an exception.
+ * <p>
+ * Useful in debugging.
+ * <p>
+ */
+public class LoggingProxy implements InvocationHandler
+{
+ private final Object _target;
+ private final CircularBuffer _log;
+
+ public LoggingProxy(Object target, int size)
+ {
+ _target = target;
+ _log = new CircularBuffer(size);
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ try
+ {
+ entered(method, args);
+ Object result = method.invoke(_target, args);
+ returned(method, result);
+ return result;
+ }
+ catch(InvocationTargetException e)
+ {
+ dump();
+ throw e.getTargetException();
+ }
+ }
+
+ void dump()
+ {
+ _log.dump();
+ }
+
+ CircularBuffer getBuffer()
+ {
+ return _log;
+ }
+
+ private synchronized void entered(Method method, Object[] args)
+ {
+ if (args == null)
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "() entered");
+ }
+ else
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "(" + Arrays.toString(args) + ") entered");
+ }
+ }
+
+ private synchronized void returned(Method method, Object result)
+ {
+ if (method.getReturnType() == Void.TYPE)
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "() returned");
+ }
+ else
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "() returned " + result);
+ }
+ }
+
+ public Object getProxy(Class... c)
+ {
+ return Proxy.newProxyInstance(_target.getClass().getClassLoader(), c, this);
+ }
+
+ public int getBufferSize() {
+ return _log.size();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java
new file mode 100644
index 0000000000..150b98b424
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.MapConfiguration;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+import org.apache.qpid.server.management.NoopManagedObjectRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+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.virtualhost.VirtualHost;
+import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+
+public class NullApplicationRegistry extends ApplicationRegistry
+{
+ private ManagedObjectRegistry _managedObjectRegistry;
+
+ private AuthenticationManager _authenticationManager;
+
+ private VirtualHostRegistry _virtualHostRegistry;
+
+ private AccessManager _accessManager;
+
+ private PrincipalDatabaseManager _databaseManager;
+
+
+ public NullApplicationRegistry()
+ {
+ super(new MapConfiguration(new HashMap()));
+ }
+
+ public void initialise() throws Exception
+ {
+ _configuration.addProperty("store.class", "org.apache.qpid.server.store.MemoryMessageStore");
+
+ Properties users = new Properties();
+
+ users.put("guest", "guest");
+
+ _databaseManager = new PropertiesPrincipalDatabaseManager("default", users);
+
+ _accessManager = new AllowAll();
+
+ _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null);
+
+ _managedObjectRegistry = new NoopManagedObjectRegistry();
+ _virtualHostRegistry = new VirtualHostRegistry();
+ VirtualHost dummyHost = new VirtualHost("test", getConfiguration());
+ _virtualHostRegistry.registerVirtualHost(dummyHost);
+ _virtualHostRegistry.setDefaultVirtualHostName("test");
+
+ _configuration.addProperty("heartbeat.delay", 10 * 60); // 10 minutes
+
+ }
+
+ public Configuration getConfiguration()
+ {
+ return _configuration;
+ }
+
+
+ public ManagedObjectRegistry getManagedObjectRegistry()
+ {
+ return _managedObjectRegistry;
+ }
+
+ public PrincipalDatabaseManager getDatabaseManager()
+ {
+ return _databaseManager;
+ }
+
+ public AuthenticationManager getAuthenticationManager()
+ {
+ return _authenticationManager;
+ }
+
+ public Collection<String> getVirtualHostNames()
+ {
+ String[] hosts = {"test"};
+ return Arrays.asList(hosts);
+ }
+
+ public VirtualHostRegistry getVirtualHostRegistry()
+ {
+ return _virtualHostRegistry;
+ }
+
+ public AccessManager getAccessManager()
+ {
+ return _accessManager;
+ }
+}
+
+
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java b/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java
new file mode 100644
index 0000000000..85d804457e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.virtualhost;
+
+import java.io.IOException;
+
+import org.apache.qpid.server.management.MBeanAttribute;
+
+/**
+ * The management interface exposed to allow management of an Exchange.
+ * @version 0.1
+ */
+public interface ManagedVirtualHost
+{
+ static final String TYPE = "VirtualHost";
+
+ /**
+ * Returns the name of the managed virtualHost.
+ * @return the name of the exchange.
+ * @throws java.io.IOException
+ */
+ @MBeanAttribute(name="Name", description= TYPE + " Name")
+ String getName() throws IOException;
+
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
new file mode 100644
index 0000000000..b95772b680
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
@@ -0,0 +1,259 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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);
+ _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;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java b/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java
new file mode 100644
index 0000000000..27917fac8a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.virtualhost;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public class VirtualHostRegistry
+{
+ private final Map<String, VirtualHost> _registry = new ConcurrentHashMap<String,VirtualHost>();
+
+
+ private String _defaultVirtualHostName;
+
+ public synchronized void registerVirtualHost(VirtualHost host) throws Exception
+ {
+ if(_registry.containsKey(host.getName()))
+ {
+ throw new Exception("Virtual Host with name " + host.getName() + " already registered.");
+ }
+ _registry.put(host.getName(),host);
+ }
+
+ public VirtualHost getVirtualHost(String name)
+ {
+ if(name == null || name.trim().length() == 0 )
+ {
+ name = getDefaultVirtualHostName();
+ }
+
+ return _registry.get(name);
+ }
+
+ private String getDefaultVirtualHostName()
+ {
+ return _defaultVirtualHostName;
+ }
+
+ public void setDefaultVirtualHostName(String defaultVirtualHostName)
+ {
+ _defaultVirtualHostName = defaultVirtualHostName;
+ }
+
+
+ public Collection<VirtualHost> getVirtualHosts()
+ {
+ return new ArrayList<VirtualHost>(_registry.values());
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java
new file mode 100644
index 0000000000..edc900f401
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java
@@ -0,0 +1,652 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.configuration.Configuration;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.tools.messagestore.commands.Clear;
+import org.apache.qpid.tools.messagestore.commands.Command;
+import org.apache.qpid.tools.messagestore.commands.Copy;
+import org.apache.qpid.tools.messagestore.commands.Dump;
+import org.apache.qpid.tools.messagestore.commands.Help;
+import org.apache.qpid.tools.messagestore.commands.List;
+import org.apache.qpid.tools.messagestore.commands.Load;
+import org.apache.qpid.tools.messagestore.commands.Quit;
+import org.apache.qpid.tools.messagestore.commands.Select;
+import org.apache.qpid.tools.messagestore.commands.Show;
+import org.apache.qpid.tools.messagestore.commands.Move;
+import org.apache.qpid.tools.messagestore.commands.Purge;
+import org.apache.qpid.tools.utils.CommandParser;
+import org.apache.qpid.tools.utils.Console;
+import org.apache.qpid.tools.utils.SimpleCommandParser;
+import org.apache.qpid.tools.utils.SimpleConsole;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * MessageStoreTool.
+ */
+public class MessageStoreTool
+{
+ /** Text outputted at the start of each console.*/
+ private static final String BOILER_PLATE = "MessageStoreTool - for examining Persistent Qpid Broker MessageStore instances";
+
+ /** I/O Wrapper. */
+ protected Console _console;
+
+ /** Batch mode flag. */
+ protected boolean _batchMode;
+
+ /** Internal State object. */
+ private State _state = new State();
+
+ private HashMap<String, Command> _commands = new HashMap<String, Command>();
+
+ /** SLF4J Logger. */
+ private static Logger _devlog = LoggerFactory.getLogger(MessageStoreTool.class);
+
+ /** Loaded configuration file. */
+ private Configuration _config;
+
+ /** Control used for main run loop. */
+ private boolean _running = true;
+ private boolean _initialised = false;
+
+ //---------------------------------------------------------------------------------------------------/
+
+ public static void main(String[] args) throws Configuration.InitException
+ {
+
+ MessageStoreTool tool = new MessageStoreTool(args);
+
+ tool.start();
+ }
+
+
+ public MessageStoreTool(String[] args) throws Configuration.InitException
+ {
+ this(args, System.in, System.out);
+ }
+
+ public MessageStoreTool(String[] args, InputStream in, OutputStream out) throws Configuration.InitException
+ {
+ BufferedReader consoleReader = new BufferedReader(new InputStreamReader(in));
+ BufferedWriter consoleWriter = new BufferedWriter(new OutputStreamWriter(out));
+
+ Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook(this)));
+ _batchMode = false;
+
+ _console = new SimpleConsole(consoleWriter, consoleReader);
+
+ _config = new Configuration();
+
+ setOptions();
+ _config.processCommandline(args);
+ }
+
+
+ private void setOptions()
+ {
+ Option help = new Option("h", "help", false, "print this message");
+ Option version = new Option("v", "version", false, "print the version information and exit");
+ Option configFile =
+ OptionBuilder.withArgName("file").hasArg()
+ .withDescription("use given configuration file By "
+ + "default looks for a file named "
+ + Configuration.DEFAULT_CONFIG_FILE + " in " + Configuration.QPID_HOME)
+ .withLongOpt("config")
+ .create("c");
+
+ _config.setOption(help);
+ _config.setOption(version);
+ _config.setOption(configFile);
+ }
+
+ public State getState()
+ {
+ return _state;
+ }
+
+ public Map<String, Command> getCommands()
+ {
+ return _commands;
+ }
+
+ public void setConfigurationFile(String configfile) throws Configuration.InitException
+ {
+ _config.loadConfig(new File(configfile));
+ setup();
+ }
+
+ public Console getConsole()
+ {
+ return _console;
+ }
+
+ public void setConsole(Console console)
+ {
+ _console = console;
+ }
+
+ /**
+ * Simple ShutdownHook to cleanly shutdown the databases
+ */
+ class ShutdownHook implements Runnable
+ {
+ MessageStoreTool _tool;
+
+ ShutdownHook(MessageStoreTool messageStoreTool)
+ {
+ _tool = messageStoreTool;
+ }
+
+ public void run()
+ {
+ _tool.quit();
+ }
+ }
+
+ public void quit()
+ {
+ _running = false;
+
+ if (_initialised)
+ {
+ ApplicationRegistry.remove(1);
+ }
+
+ _console.println("...exiting");
+
+ _console.close();
+ }
+
+ public void setBatchMode(boolean batchmode)
+ {
+ _batchMode = batchmode;
+ }
+
+ /**
+ * Main loop
+ */
+ protected void start()
+ {
+ setup();
+
+ if (!_initialised)
+ {
+ System.exit(1);
+ }
+
+ _console.println("");
+
+ _console.println(BOILER_PLATE);
+
+ runCLI();
+ }
+
+ private void setup()
+ {
+ loadDefaultVirtualHosts();
+
+ loadCommands();
+
+ _state.clearAll();
+ }
+
+ private void loadCommands()
+ {
+ _commands.clear();
+ //todo Dynamically load the classes that exis in com.redhat.etp.qpid.commands
+ _commands.put("close", new Clear(this));
+ _commands.put("copy", new Copy(this));
+ _commands.put("dump", new Dump(this));
+ _commands.put("help", new Help(this));
+ _commands.put("list", new List(this));
+ _commands.put("load", new Load(this));
+ _commands.put("move", new Move(this));
+ _commands.put("purge", new Purge(this));
+ _commands.put("quit", new Quit(this));
+ _commands.put("select", new Select(this));
+ _commands.put("show", new Show(this));
+ }
+
+ private void loadDefaultVirtualHosts()
+ {
+ final File configFile = _config.getConfigFile();
+
+ loadVirtualHosts(configFile);
+ }
+
+ private void loadVirtualHosts(File configFile)
+ {
+
+ if (!configFile.exists())
+ {
+ _devlog.error("Config file not found:" + configFile.getAbsolutePath());
+ return;
+ }
+ else
+ {
+ _devlog.debug("using config file :" + configFile.getAbsolutePath());
+ }
+
+ try
+ {
+ ConfigurationFileApplicationRegistry registry = new ConfigurationFileApplicationRegistry(configFile);
+
+ ApplicationRegistry.remove(1);
+
+ ApplicationRegistry.initialise(registry);
+
+ checkMessageStores();
+ _initialised = true;
+ }
+ catch (ConfigurationException e)
+ {
+ _console.println("Unable to load configuration due to configuration error: " + e.getMessage());
+ e.printStackTrace();
+ }
+ catch (Exception e)
+ {
+ _console.println("Unable to load configuration due to: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+
+ }
+
+ private void checkMessageStores()
+ {
+ Collection<VirtualHost> vhosts = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts();
+
+ boolean warning = false;
+ for (VirtualHost vhost : vhosts)
+ {
+ if (vhost.getMessageStore() instanceof MemoryMessageStore)
+ {
+ _console.println("WARNING: Virtualhost '" + vhost.getName() + "' is using a MemoryMessageStore. "
+ + "Changes will not persist.");
+ warning = true;
+ }
+ }
+
+ if (warning)
+ {
+ _console.println("");
+ _console.println("Please ensure you are using the correct config file currently using '"
+ + _config.getConfigFile().getAbsolutePath() + "'");
+ _console.println("New config file can be specifed by 'load <config file>' or -c on the commandline.");
+ _console.println("");
+ }
+ }
+
+ private void runCLI()
+ {
+ while (_running)
+ {
+ if (!_batchMode)
+ {
+ printPrompt();
+ }
+
+ String[] args = _console.readCommand();
+
+ while (args != null)
+ {
+ exec(args);
+
+ if (_running)
+ {
+ if (!_batchMode)
+ {
+ printPrompt();
+ }
+
+ args = _console.readCommand();
+ }
+ }
+ }
+ }
+
+ private void printPrompt()
+ {
+ _console.print(prompt());
+ }
+
+
+ /**
+ * Execute a script (batch mode).
+ *
+ * @param script The file script
+ */
+ protected void runScripts(String script)
+ {
+ //Store Current State
+ boolean oldBatch = _batchMode;
+ CommandParser oldParser = _console.getCommandParser();
+ setBatchMode(true);
+
+ try
+ {
+ _devlog.debug("Running script '" + script + "'");
+
+ _console.setCommandParser(new SimpleCommandParser(new BufferedReader(new FileReader(script))));
+
+ start();
+ }
+ catch (java.io.FileNotFoundException e)
+ {
+ _devlog.error("Script not found: '" + script + "' due to:" + e.getMessage());
+ }
+
+ //Restore previous state
+ _console.setCommandParser(oldParser);
+ setBatchMode(oldBatch);
+ }
+
+ public String prompt()
+ {
+ String state = _state.toString();
+ if (state != null && state.length() != 0)
+ {
+ return state + ":bdb$ ";
+ }
+ else
+ {
+ return "bdb$ ";
+ }
+ }
+
+ /**
+ * Execute the command.
+ *
+ * @param args [command, arg0, arg1...].
+ */
+ protected void exec(String[] args)
+ {
+ // Comment lines start with a #
+ if (args.length == 0 || args[0].startsWith("#"))
+ {
+ return;
+ }
+
+ final String command = args[0];
+
+ Command cmd = _commands.get(command);
+
+ if (cmd == null)
+ {
+ _console.println("Command not understood: " + command);
+ }
+ else
+ {
+ cmd.execute(args);
+ }
+ }
+
+
+ /**
+ * Displays usage info.
+ */
+ protected static void help()
+ {
+ System.out.println(BOILER_PLATE);
+ System.out.println("Usage: java " + MessageStoreTool.class + " [Options]");
+ System.out.println(" [-c <broker config file>] : Defaults to \"$QPID_HOME/etc/config.xml\"");
+ }
+
+
+ /**
+ * This class is used to store the current state of the tool.
+ *
+ * This is then interrogated by the various commands to augment their behaviour.
+ *
+ *
+ */
+ public class State
+ {
+ private VirtualHost _vhost = null;
+ private AMQQueue _queue = null;
+ private Exchange _exchange = null;
+ private java.util.List<Long> _msgids = null;
+
+ public State()
+ {
+ }
+
+ public void setQueue(AMQQueue queue)
+ {
+ _queue = queue;
+ }
+
+ public AMQQueue getQueue()
+ {
+ return _queue;
+ }
+
+ public void setVhost(VirtualHost vhost)
+ {
+ _vhost = vhost;
+ }
+
+ public VirtualHost getVhost()
+ {
+ return _vhost;
+ }
+
+ public Exchange getExchange()
+ {
+ return _exchange;
+ }
+
+ public void setExchange(Exchange exchange)
+ {
+ _exchange = exchange;
+ }
+
+ public String toString()
+ {
+ StringBuilder status = new StringBuilder();
+
+ if (_vhost != null)
+ {
+ status.append(_vhost.getName());
+
+ if (_exchange != null)
+ {
+ status.append("[");
+ status.append(_exchange.getName());
+ status.append("]");
+
+ if (_queue != null)
+ {
+ status.append("->'");
+ status.append(_queue.getName());
+ status.append("'");
+
+ if (_msgids != null)
+ {
+ status.append(printMessages());
+ }
+ }
+ }
+ }
+
+ return status.toString();
+ }
+
+
+ public String printMessages()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ Long previous = null;
+
+ Long start = null;
+ for (Long id : _msgids)
+ {
+ if (previous != null)
+ {
+ if (id == previous + 1)
+ {
+ if (start == null)
+ {
+ start = previous;
+ }
+ }
+ else
+ {
+ if (start != null)
+ {
+ sb.append(",");
+ sb.append(start);
+ sb.append("-");
+ sb.append(id);
+ start = null;
+ }
+ else
+ {
+ sb.append(",");
+ sb.append(previous);
+ }
+ }
+ }
+
+ previous = id;
+ }
+
+ if (start != null)
+ {
+ sb.append(",");
+ sb.append(start);
+ sb.append("-");
+ sb.append(_msgids.get(_msgids.size() - 1));
+ }
+ else
+ {
+ sb.append(",");
+ sb.append(previous);
+ }
+
+ // surround list in ()
+ sb.replace(0, 1, "(");
+ sb.append(")");
+ return sb.toString();
+ }
+
+ public void clearAll()
+ {
+ _vhost = null;
+ clearExchange();
+ }
+
+ public void clearExchange()
+ {
+ _exchange = null;
+ clearQueue();
+ }
+
+ public void clearQueue()
+ {
+ _queue = null;
+ clearMessages();
+ }
+
+ public void clearMessages()
+ {
+ _msgids = null;
+ }
+
+ /**
+ * A common location to provide parsing of the message id string
+ * utilised by a number of the commands.
+ * The String is comma separated list of ids that can be individual ids
+ * or a range (4-10)
+ *
+ * @param msgString string of msg ids to parse 1,2,4-10
+ */
+ public void setMessages(String msgString)
+ {
+ StringTokenizer tok = new StringTokenizer(msgString, ",");
+
+ if (tok.hasMoreTokens())
+ {
+ _msgids = new LinkedList<Long>();
+ }
+
+ while (tok.hasMoreTokens())
+ {
+ String next = tok.nextToken();
+ if (next.contains("-"))
+ {
+ Long start = Long.parseLong(next.substring(0, next.indexOf("-")));
+ Long end = Long.parseLong(next.substring(next.indexOf("-") + 1));
+
+ if (end >= start)
+ {
+ for (long l = start; l <= end; l++)
+ {
+ _msgids.add(l);
+ }
+ }
+ }
+ else
+ {
+ _msgids.add(Long.parseLong(next));
+ }
+ }
+
+ }
+
+ public void setMessages(java.util.List<Long> msgids)
+ {
+ _msgids = msgids;
+ }
+
+ public java.util.List<Long> getMessages()
+ {
+ return _msgids;
+ }
+ }//Class State
+
+}//Class MessageStoreTool
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java
new file mode 100644
index 0000000000..5444197cb4
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.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.tools.messagestore.commands;
+
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+import org.apache.qpid.tools.utils.Console;
+
+public abstract class AbstractCommand implements Command
+{
+ protected Console _console;
+ protected MessageStoreTool _tool;
+
+ public AbstractCommand(MessageStoreTool tool)
+ {
+ _console = tool.getConsole();
+ _tool = tool;
+ }
+
+ public void setOutput(Console out)
+ {
+ _console = out;
+ }
+
+ protected void commandError(String message, String[] args)
+ {
+ _console.print(getCommand() + " : " + message);
+
+ if (args != null)
+ {
+ for (int i = 1; i < args.length; i++)
+ {
+ _console.print(args[i]);
+ }
+ }
+ _console.println("");
+ _console.println(help());
+ }
+
+
+ public abstract String help();
+
+ public abstract String usage();
+
+ public abstract String getCommand();
+
+
+ public abstract void execute(String... args);
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java
new file mode 100644
index 0000000000..b0006b3fe6
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore.commands;
+
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+
+public class Clear extends AbstractCommand
+{
+ public Clear(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Clears any selection.";
+ }
+
+ public String usage()
+ {
+ return "clear [ all | virtualhost | exchange | queue | msgs ]";
+ }
+
+ public String getCommand()
+ {
+ return "clear";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 0;
+ assert args[0].equals(getCommand());
+
+ if (args.length < 1)
+ {
+ doClose("all");
+ }
+ else
+ {
+ doClose(args[1]);
+ }
+ }
+
+ private void doClose(String type)
+ {
+ if (type.equals("virtualhost")
+ || type.equals("all"))
+ {
+ _tool.getState().clearAll();
+ }
+
+ if (type.equals("exchange"))
+ {
+ _tool.getState().clearExchange();
+ }
+
+ if (type.equals("queue"))
+ {
+ _tool.getState().clearQueue();
+ }
+
+ if (type.equals("msgs"))
+ {
+ _tool.getState().clearMessages();
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java
new file mode 100644
index 0000000000..bfa775a34a
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore.commands;
+
+import org.apache.qpid.tools.utils.Console;
+
+public interface Command
+{
+ public void setOutput(Console out);
+
+ public String help();
+
+ public abstract String usage();
+
+ String getCommand();
+
+ public void execute(String... args);
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java
new file mode 100644
index 0000000000..a5b3a87616
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.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.tools.messagestore.commands;
+
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+
+public class Copy extends Move
+{
+ public Copy(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Copy messages between queues.";/*\n" +
+ "The currently selected message set will be copied to the specifed queue.\n" +
+ "Alternatively the values can be provided on the command line."; */
+ }
+
+ public String usage()
+ {
+ return "copy to=<queue> [from=<queue>] [msgids=<msgids eg, 1,2,4-10>]";
+ }
+
+ public String getCommand()
+ {
+ return "copy";
+ }
+
+ protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue)
+ {
+ fromQueue.copyMessagesToAnotherQueue(start, end, toQueue.getName().toString(), _storeContext);
+ }
+
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java
new file mode 100644
index 0000000000..eea53252c6
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore.commands;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+import org.apache.qpid.tools.utils.Console;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Dump extends Show
+{
+ private static final int LINE_SIZE = 8;
+ private static final String DEFAULT_ENCODING = "utf-8";
+ private static final boolean SPACE_BYTES = true;
+ private static final String BYTE_SPACER = " ";
+ private static final String NON_PRINTING_ASCII_CHAR = "?";
+
+ protected boolean _content = true;
+
+ public Dump(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Dump selected message content. Default: show=content";
+ }
+
+ public String usage()
+ {
+ return getCommand() + " [show=[all],[msgheaders],[_amqHeaders],[routing],[content]] [id=<msgid e.g. 1,2,4-10>]";
+ }
+
+ public String getCommand()
+ {
+ return "dump";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 0;
+ assert args[0].equals(getCommand());
+
+
+ if (args.length >= 2)
+ {
+ for (String arg : args)
+ {
+ if (arg.startsWith("show="))
+ {
+ _content = arg.contains("content") || arg.contains("all");
+ }
+ }
+
+ parseArgs(args);
+ }
+
+ performShow();
+ }
+
+
+ protected List<List> createMessageData(java.util.List<Long> msgids, List<AMQMessage> messages, boolean showHeaders, boolean showRouting,
+ boolean showMessageHeaders)
+ {
+
+ List<List> display = new LinkedList<List>();
+
+ List<String> hex = new LinkedList<String>();
+ List<String> ascii = new LinkedList<String>();
+ display.add(hex);
+ display.add(ascii);
+
+ for (AMQMessage msg : messages)
+ {
+ if (!includeMsg(msg, msgids))
+ {
+ continue;
+ }
+
+ //Add divider between messages
+ hex.add(Console.ROW_DIVIDER);
+ ascii.add(Console.ROW_DIVIDER);
+
+ // Show general message information
+ hex.add(Show.Columns.ID.name());
+ ascii.add(msg.getMessageId().toString());
+
+ hex.add(Console.ROW_DIVIDER);
+ ascii.add(Console.ROW_DIVIDER);
+
+ if (showRouting)
+ {
+ addShowInformation(hex, ascii, msg, "Routing Details", true, false, false);
+ }
+ if (showHeaders)
+ {
+ addShowInformation(hex, ascii, msg, "Headers", false, true, false);
+ }
+ if (showMessageHeaders)
+ {
+ addShowInformation(hex, ascii, msg, null, false, false, true);
+ }
+
+ // Add Content Body seciont
+ hex.add("Content Body");
+ ascii.add("");
+ hex.add(Console.ROW_DIVIDER);
+ ascii.add(Console.ROW_DIVIDER);
+
+ Iterator bodies = msg.getContentBodyIterator();
+ if (bodies.hasNext())
+ {
+
+ hex.add("Hex");
+ hex.add(Console.ROW_DIVIDER);
+
+
+ ascii.add("ASCII");
+ ascii.add(Console.ROW_DIVIDER);
+
+ while (bodies.hasNext())
+ {
+ ContentChunk chunk = (ContentChunk) bodies.next();
+
+ //Duplicate so we don't destroy original data :)
+ ByteBuffer hexBuffer = chunk.getData().duplicate();
+
+ ByteBuffer charBuffer = hexBuffer.duplicate();
+
+ Hex hexencoder = new Hex();
+
+ while (hexBuffer.hasRemaining())
+ {
+ byte[] line = new byte[LINE_SIZE];
+
+ int bufsize = hexBuffer.remaining();
+ if (bufsize < LINE_SIZE)
+ {
+ hexBuffer.get(line, 0, bufsize);
+ }
+ else
+ {
+ bufsize = line.length;
+ hexBuffer.get(line);
+ }
+
+ byte[] encoded = hexencoder.encode(line);
+
+ try
+ {
+ String encStr = new String(encoded, 0, bufsize * 2, DEFAULT_ENCODING);
+ String hexLine = "";
+
+ int strKength = encStr.length();
+ for (int c = 0; c < strKength; c++)
+ {
+ hexLine += encStr.charAt(c);
+
+ if (c % 2 == 1 && SPACE_BYTES)
+ {
+ hexLine += BYTE_SPACER;
+ }
+ }
+
+ hex.add(hexLine);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ _console.println(e.getMessage());
+ return null;
+ }
+ }
+
+ while (charBuffer.hasRemaining())
+ {
+ String asciiLine = "";
+
+ for (int pos = 0; pos < LINE_SIZE; pos++)
+ {
+ if (charBuffer.hasRemaining())
+ {
+ byte ch = charBuffer.get();
+
+ if (isPrintable(ch))
+ {
+ asciiLine += (char) ch;
+ }
+ else
+ {
+ asciiLine += NON_PRINTING_ASCII_CHAR;
+ }
+
+ if (SPACE_BYTES)
+ {
+ asciiLine += BYTE_SPACER;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ ascii.add(asciiLine);
+ }
+ }
+ }
+ else
+ {
+ List<String> result = new LinkedList<String>();
+
+ display.add(result);
+ result.add("No ContentBodies");
+ }
+ }
+
+ // if hex is empty then we have no data to display
+ if (hex.size() == 0)
+ {
+ return null;
+ }
+
+ return display;
+ }
+
+ private void addShowInformation(List<String> column1, List<String> column2, AMQMessage msg,
+ String title, boolean routing, boolean headers, boolean messageHeaders)
+ {
+ List<AMQMessage> single = new LinkedList<AMQMessage>();
+ single.add(msg);
+
+ List<List> routingData = super.createMessageData(null, single, headers, routing, messageHeaders);
+
+ //Reformat data
+ if (title != null)
+ {
+ column1.add(title);
+ column2.add("");
+ column1.add(Console.ROW_DIVIDER);
+ column2.add(Console.ROW_DIVIDER);
+ }
+
+ // look at all columns in the routing Data
+ for (List item : routingData)
+ {
+ // the item should be:
+ // Title
+ // *divider
+ // value
+ // otherwise we can't reason about the correct value
+ if (item.size() == 3)
+ {
+ //Filter out the columns we are not interested in.
+
+ String columnName = item.get(0).toString();
+
+ if (!(columnName.equals(Show.Columns.ID.name())
+ || columnName.equals(Show.Columns.Size.name())))
+ {
+ column1.add(columnName);
+ column2.add(item.get(2).toString());
+ }
+ }
+ }
+ column1.add(Console.ROW_DIVIDER);
+ column2.add(Console.ROW_DIVIDER);
+ }
+
+ private boolean isPrintable(byte c)
+ {
+ return c > 31 && c < 127;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java
new file mode 100644
index 0000000000..0f9546541b
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.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.tools.messagestore.commands;
+
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+import org.apache.qpid.tools.utils.Console;
+
+import java.util.LinkedList;
+import java.util.Map;
+
+public class Help extends AbstractCommand
+{
+ public Help(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Provides detailed help on commands.";
+ }
+
+ public String getCommand()
+ {
+ return "help";
+ }
+
+ public String usage()
+ {
+ return "help [<command>]";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 0;
+ assert args[0].equals(getCommand());
+
+ if (args.length > 1)
+ {
+ Command command = _tool.getCommands().get(args[1]);
+ if (command != null)
+ {
+ _console.println(command.help());
+ _console.println("Usage:" + command.usage());
+ }
+ else
+ {
+ commandError("Command not found: ", args);
+ }
+ }
+ else
+ {
+ java.util.List<java.util.List> data = new LinkedList<java.util.List>();
+
+ java.util.List<String> commandName = new LinkedList<String>();
+ java.util.List<String> commandDescription = new LinkedList<String>();
+
+ data.add(commandName);
+ data.add(commandDescription);
+
+ //Set up Headers
+ commandName.add("Command");
+ commandDescription.add("Description");
+
+ commandName.add(Console.ROW_DIVIDER);
+ commandDescription.add(Console.ROW_DIVIDER);
+
+ //Add current Commands with descriptions
+ Map<String, Command> commands = _tool.getCommands();
+
+ for (Command command : commands.values())
+ {
+ commandName.add(command.getCommand());
+ commandDescription.add(command.help());
+ }
+
+ _console.printMap("Available Commands", data);
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java
new file mode 100644
index 0000000000..df8b59ec19
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore.commands;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+import org.apache.qpid.tools.utils.Console;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+public class List extends AbstractCommand
+{
+
+ public List(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public void setOutput(Console out)
+ {
+ _console = out;
+ }
+
+ public String help()
+ {
+ return "list available items.";
+ }
+
+ public String usage()
+ {
+ return "list queues [<exchange>] | exchanges | bindings [<exchange>] | all";
+ }
+
+ public String getCommand()
+ {
+ return "list";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 0;
+ assert args[0].equals(getCommand());
+
+ if (args.length > 1)
+ {
+ if ((args[1].equals("exchanges"))
+ || (args[1].equals("queues"))
+ || (args[1].equals("bindings"))
+ || (args[1].equals("all")))
+ {
+ if (args.length == 2)
+ {
+ doList(args[1]);
+ }
+ else if (args.length == 3)
+ {
+ doList(args[1], args[2]);
+ }
+ }
+ else
+ {
+ commandError("Unknown options. ", args);
+ }
+ }
+ else if (args.length < 2)
+ {
+ doList("all");
+ }
+ else
+ {
+ doList(args[1]);
+ }
+ }
+
+ private void doList(String... listItem)
+ {
+ if (_tool.getState().getVhost() == null)
+ {
+ _console.println("No Virtualhost open. Open a Virtualhost first.");
+ listVirtualHosts();
+ return;
+ }
+
+ VirtualHost vhost = _tool.getState().getVhost();
+
+ java.util.List<String> data = null;
+
+ if (listItem[0].equals("queues"))
+ {
+ if (listItem.length > 1)
+ {
+ data = listQueues(vhost, new AMQShortString(listItem[1]));
+ }
+ else
+ {
+ Exchange exchange = _tool.getState().getExchange();
+ data = listQueues(vhost, exchange);
+ }
+ }
+
+ if (listItem[0].equals("exchanges"))
+ {
+ data = listExchanges(vhost);
+ }
+
+ if (listItem[0].equals("bindings"))
+ {
+
+ if (listItem.length > 1)
+ {
+ data = listBindings(vhost, new AMQShortString(listItem[1]));
+ }
+ else
+ {
+ Exchange exchange = _tool.getState().getExchange();
+
+ data = listBindings(vhost, exchange);
+ }
+ }
+
+ if (data != null)
+ {
+ if (data.size() == 1)
+ {
+ _console.println("No '" + listItem[0] + "' to display,");
+ }
+ else
+ {
+ _console.displayList(true, data.toArray(new String[0]));
+ }
+ }
+
+
+ if (listItem[0].equals("all"))
+ {
+
+ boolean displayed = false;
+ Exchange exchange = _tool.getState().getExchange();
+
+ //Do the display here for each one so that they are pretty printed
+ data = listQueues(vhost, exchange);
+ if (data != null)
+ {
+ displayed = true;
+ _console.displayList(true, data.toArray(new String[0]));
+ }
+
+ if (exchange == null)
+ {
+ data = listExchanges(vhost);
+ if (data != null)
+ {
+ displayed = true;
+ _console.displayList(true, data.toArray(new String[0]));
+ }
+ }
+
+ data = listBindings(vhost, exchange);
+ if (data != null)
+ {
+ displayed = true;
+ _console.displayList(true, data.toArray(new String[0]));
+ }
+
+ if (!displayed)
+ {
+ _console.println("Nothing to list");
+ }
+ }
+ }
+
+ private void listVirtualHosts()
+ {
+ Collection<VirtualHost> vhosts = ApplicationRegistry.getInstance()
+ .getVirtualHostRegistry().getVirtualHosts();
+
+ String[] data = new String[vhosts.size() + 1];
+
+ data[0] = "Available VirtualHosts";
+
+ int index = 1;
+ for (VirtualHost vhost : vhosts)
+ {
+ data[index] = vhost.getName();
+ index++;
+ }
+
+ _console.displayList(true, data);
+ }
+
+ private java.util.List<String> listBindings(VirtualHost vhost, AMQShortString exchangeName)
+ {
+ return listBindings(vhost, vhost.getExchangeRegistry().getExchange(exchangeName));
+ }
+
+ private java.util.List<String> listBindings(VirtualHost vhost, Exchange exchange)
+ {
+ Collection<AMQShortString> queues = vhost.getQueueRegistry().getQueueNames();
+
+ if (queues == null || queues.size() == 0)
+ {
+ return null;
+ }
+
+ java.util.List<String> data = new LinkedList<String>();
+
+ data.add("Current Bindings");
+
+ for (AMQShortString queue : queues)
+ {
+ if (exchange != null)
+ {
+ if (exchange.isBound(queue))
+ {
+ data.add(queue.toString());
+ }
+ }
+ else
+ {
+ data.add(queue.toString());
+ }
+ }
+
+ return data;
+ }
+
+ private java.util.List<String> listExchanges(VirtualHost vhost)
+ {
+ Collection<AMQShortString> queues = vhost.getExchangeRegistry().getExchangeNames();
+
+ if (queues == null || queues.size() == 0)
+ {
+ return null;
+ }
+
+ java.util.List<String> data = new LinkedList<String>();
+
+ data.add("Available Exchanges");
+
+ for (AMQShortString queue : queues)
+ {
+ data.add(queue.toString());
+ }
+
+ return data;
+ }
+
+ private java.util.List<String> listQueues(VirtualHost vhost, AMQShortString exchangeName)
+ {
+ return listQueues(vhost, vhost.getExchangeRegistry().getExchange(exchangeName));
+ }
+
+ private java.util.List<String> listQueues(VirtualHost vhost, Exchange exchange)
+ {
+ Collection<AMQQueue> queues = vhost.getQueueRegistry().getQueues();
+
+ if (queues == null || queues.size() == 0)
+ {
+ return null;
+ }
+
+ java.util.List<String> data = new LinkedList<String>();
+
+ data.add("Available Queues");
+
+ for (AMQQueue queue : queues)
+ {
+ if (exchange != null)
+ {
+ if (exchange.isBound(queue))
+ {
+ data.add(queue.getName().toString());
+ }
+ }
+ else
+ {
+ data.add(queue.getName().toString());
+ }
+ }
+
+ if (exchange != null)
+ {
+ if (queues.size() == 1)
+ {
+ return null;
+ }
+ }
+
+ return data;
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java
new file mode 100644
index 0000000000..244a311c30
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.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.tools.messagestore.commands;
+
+import org.apache.qpid.configuration.Configuration;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+
+public class Load extends AbstractCommand
+{
+ public Load(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Loads specified broker configuration file.";
+ }
+
+ public String usage()
+ {
+ return "load <configuration file>";
+ }
+
+ public String getCommand()
+ {
+ return "load";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 0;
+ assert args[0].equals(getCommand());
+
+ if (args.length > 2)
+ {
+ _console.print("load " + args[1] + ": additional options not understood:");
+ for (int i = 2; i < args.length; i++)
+ {
+ _console.print(args[i] + " ");
+ }
+ _console.println("");
+ }
+ else if (args.length < 2)
+ {
+ _console.println("Enter Configuration file.");
+ String input = _console.readln();
+ if (input != null)
+ {
+ doLoad(input);
+ }
+ else
+ {
+ _console.println("Did not recognise config file.");
+ }
+ }
+ else
+ {
+ doLoad(args[1]);
+ }
+ }
+
+ private void doLoad(String configfile)
+ {
+ _console.println("Loading Configuration:" + configfile);
+
+ try
+ {
+ _tool.setConfigurationFile(configfile);
+ }
+ catch (Configuration.InitException e)
+ {
+ _console.println("Unable to open config file due to: '" + e.getMessage() + "'");
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java
new file mode 100644
index 0000000000..25cff27445
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore.commands;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Move extends AbstractCommand
+{
+
+ /**
+ * Since the Coopy command is not associated with a real channel we can safely create our own store context
+ * for use in the few methods that require one.
+ */
+ protected StoreContext _storeContext = new StoreContext();
+
+ public Move(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Move messages between queues.";/*\n" +
+ "The currently selected message set will be moved to the specifed queue.\n" +
+ "Alternatively the values can be provided on the command line.";*/
+ }
+
+ public String usage()
+ {
+ return "move to=<queue> [from=<queue>] [msgids=<msgids eg, 1,2,4-10>]";
+ }
+
+ public String getCommand()
+ {
+ return "move";
+ }
+
+ public void execute(String... args)
+ {
+ AMQQueue toQueue = null;
+ AMQQueue fromQueue = _tool.getState().getQueue();
+ java.util.List<Long> msgids = _tool.getState().getMessages();
+
+ if (args.length >= 2)
+ {
+ for (String arg : args)
+ {
+ if (arg.startsWith("to="))
+ {
+ String queueName = arg.substring(arg.indexOf("=") + 1);
+ toQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName));
+ }
+
+ if (arg.startsWith("from="))
+ {
+ String queueName = arg.substring(arg.indexOf("=") + 1);
+ fromQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName));
+ }
+
+ if (arg.startsWith("msgids="))
+ {
+ String msgidStr = arg.substring(arg.indexOf("=") + 1);
+
+ // Record the current message selection
+ java.util.List<Long> currentIDs = _tool.getState().getMessages();
+
+ // Use the ToolState class to perform the messasge parsing
+ _tool.getState().setMessages(msgidStr);
+ msgids = _tool.getState().getMessages();
+
+ // Reset the original selection of messages
+ _tool.getState().setMessages(currentIDs);
+ }
+ }
+ }
+
+ if (!checkRequirements(fromQueue, toQueue, msgids))
+ {
+ return;
+ }
+
+ processIDs(fromQueue, toQueue, msgids);
+ }
+
+ private void processIDs(AMQQueue fromQueue, AMQQueue toQueue, java.util.List<Long> msgids)
+ {
+ Long previous = null;
+ Long start = null;
+
+ if (msgids == null)
+ {
+ msgids = allMessageIDs(fromQueue);
+ }
+
+ if (msgids == null || msgids.size() == 0)
+ {
+ _console.println("No Messages to move.");
+ return;
+ }
+
+ for (long id : msgids)
+ {
+ if (previous != null)
+ {
+ if (id == previous + 1)
+ {
+ if (start == null)
+ {
+ start = previous;
+ }
+ }
+ else
+ {
+ if (start != null)
+ {
+ //move a range of ids
+ doCommand(fromQueue, start, id, toQueue);
+ start = null;
+ }
+ else
+ {
+ //move a single id
+ doCommand(fromQueue, id, id, toQueue);
+ }
+ }
+ }
+
+ previous = id;
+ }
+
+ if (start != null)
+ {
+ //move a range of ids
+ doCommand(fromQueue, start, previous, toQueue);
+ }
+ }
+
+ private List<Long> allMessageIDs(AMQQueue fromQueue)
+ {
+ List<Long> ids = new LinkedList<Long>();
+
+ if (fromQueue != null)
+ {
+ List<AMQMessage> messages = fromQueue.getMessagesOnTheQueue();
+ if (messages != null)
+ {
+ for (AMQMessage msg : messages)
+ {
+ ids.add(msg.getMessageId());
+ }
+ }
+ }
+
+ return ids;
+ }
+
+ protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, List<Long> msgids)
+ {
+ if (toQueue == null)
+ {
+ _console.println("Destination queue not specifed.");
+ _console.println(usage());
+ return false;
+ }
+
+ if (fromQueue == null)
+ {
+ _console.println("Source queue not specifed.");
+ _console.println(usage());
+ return false;
+ }
+
+ return true;
+ }
+
+ protected void doCommand(AMQQueue fromQueue, long start, long id, AMQQueue toQueue)
+ {
+ fromQueue.moveMessagesToAnotherQueue(start, id, toQueue.getName().toString(), _storeContext);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java
new file mode 100644
index 0000000000..f187e26593
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.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.tools.messagestore.commands;
+
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+
+public class Purge extends Move
+{
+ public Purge(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Purge messages from a queue.\n" +
+ "The currently selected message set will be purged from the specifed queue.\n" +
+ "Alternatively the values can be provided on the command line.";
+ }
+
+ public String usage()
+ {
+ return "purge from=<queue> [msgids=<msgids eg, 1,2,4-10>]";
+ }
+
+ public String getCommand()
+ {
+ return "purge";
+ }
+
+
+ protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, java.util.List<Long> msgids)
+ {
+ if (fromQueue == null)
+ {
+ _console.println("Source queue not specifed.");
+ _console.println(usage());
+ return false;
+ }
+
+ return true;
+ }
+
+ protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue)
+ {
+ fromQueue.removeMessagesFromQueue(start, end, _storeContext);
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java
new file mode 100644
index 0000000000..a81bc07c38
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.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.tools.messagestore.commands;
+
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+
+public class Quit extends AbstractCommand
+{
+ public Quit(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Quit the tool.";
+ }
+
+ public String usage()
+ {
+ return "quit";
+ }
+
+ public String getCommand()
+ {
+ return "quit";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 0;
+ assert args[0].equals("quit");
+
+ _tool.quit();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java
new file mode 100644
index 0000000000..fd7d4c3f13
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore.commands;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+
+public class Select extends AbstractCommand
+{
+
+ public Select(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Perform a selection.";
+ }
+
+ public String usage()
+ {
+ return "select virtualhost <name> |exchange <name> |queue <name> | msg id=<msgids eg. 1,2,4-10>";
+ }
+
+ public String getCommand()
+ {
+ return "select";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 2;
+ assert args[0].equals("select");
+
+ if (args.length < 3)
+ {
+ if (args[1].equals("show"))
+ {
+ doSelect(args[1], null);
+ }
+ else
+ {
+ _console.print("select : unknown command:");
+ _console.println(help());
+ }
+ }
+ else
+ {
+ if (args[1].equals("virtualhost")
+ || args[1].equals("vhost")
+ || args[1].equals("exchange")
+ || args[1].equals("queue")
+ || args[1].equals("msg")
+ )
+ {
+ doSelect(args[1], args[2]);
+ }
+ else
+ {
+ _console.println(help());
+ }
+ }
+ }
+
+ private void doSelect(String type, String item)
+ {
+ if (type.equals("virtualhost"))
+ {
+
+ VirtualHost vhost = ApplicationRegistry.getInstance()
+ .getVirtualHostRegistry().getVirtualHost(item);
+
+ if (vhost == null)
+ {
+ _console.println("Virtualhost '" + item + "' not found.");
+ }
+ else
+ {
+ _tool.getState().setVhost(vhost);
+ }
+ }
+
+ if (type.equals("exchange"))
+ {
+
+ VirtualHost vhost = _tool.getState().getVhost();
+
+ if (vhost == null)
+ {
+ _console.println("No Virtualhost open. Open a Virtualhost first.");
+ return;
+ }
+
+
+ Exchange exchange = vhost.getExchangeRegistry().getExchange(new AMQShortString(item));
+
+ if (exchange == null)
+ {
+ _console.println("Exchange '" + item + "' not found.");
+ }
+ else
+ {
+ _tool.getState().setExchange(exchange);
+ }
+
+ if (_tool.getState().getQueue() != null)
+ {
+ if (!exchange.isBound(_tool.getState().getQueue()))
+ {
+ _tool.getState().setQueue(null);
+ }
+ }
+ }
+
+ if (type.equals("queue"))
+ {
+ VirtualHost vhost = _tool.getState().getVhost();
+
+ if (vhost == null)
+ {
+ _console.println("No Virtualhost open. Open a Virtualhost first.");
+ return;
+ }
+
+ AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(item));
+
+ if (queue == null)
+ {
+ _console.println("Queue '" + item + "' not found.");
+ }
+ else
+ {
+ _tool.getState().setQueue(queue);
+
+ if (_tool.getState().getExchange() == null)
+ {
+ for (AMQShortString exchangeName : vhost.getExchangeRegistry().getExchangeNames())
+ {
+ Exchange exchange = vhost.getExchangeRegistry().getExchange(exchangeName);
+ if (exchange.isBound(queue))
+ {
+ _tool.getState().setExchange(exchange);
+ break;
+ }
+ }
+ }
+
+ //remove the message selection
+ _tool.getState().setMessages((java.util.List<Long>) null);
+ }
+ }
+
+ if (type.equals("msg"))
+ {
+ if (item.startsWith("id="))
+ {
+ StringTokenizer tok = new StringTokenizer(item.substring(item.indexOf("=") + 1), ",");
+
+ java.util.List<Long> msgids = null;
+
+ if (tok.hasMoreTokens())
+ {
+ msgids = new LinkedList<Long>();
+ }
+
+ while (tok.hasMoreTokens())
+ {
+ String next = tok.nextToken();
+ if (next.contains("-"))
+ {
+ Long start = Long.parseLong(next.substring(0, next.indexOf("-")));
+ Long end = Long.parseLong(next.substring(next.indexOf("-") + 1));
+
+ if (end >= start)
+ {
+ for (long l = start; l <= end; l++)
+ {
+ msgids.add(l);
+ }
+ }
+ }
+ else
+ {
+ msgids.add(Long.parseLong(next));
+ }
+ }
+
+ _tool.getState().setMessages(msgids);
+ }
+
+ }
+
+ if (type.equals("show"))
+ {
+ _console.println(_tool.getState().toString());
+ if (_tool.getState().getMessages() != null)
+ {
+ _console.print("Msgs:");
+ for (Long l : _tool.getState().getMessages())
+ {
+ _console.print(" " + l);
+ }
+ _console.println("");
+ }
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java
new file mode 100644
index 0000000000..5988cdabfc
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java
@@ -0,0 +1,513 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.messagestore.commands;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.tools.messagestore.MessageStoreTool;
+import org.apache.qpid.tools.utils.Console;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class Show extends AbstractCommand
+{
+ protected boolean _amqHeaders = false;
+ protected boolean _routing = false;
+ protected boolean _msgHeaders = false;
+
+ public Show(MessageStoreTool tool)
+ {
+ super(tool);
+ }
+
+ public String help()
+ {
+ return "Shows the messages headers.";
+ }
+
+ public String usage()
+ {
+ return getCommand() + " [show=[all],[msgheaders],[amqheaders],[routing]] [id=<msgid e.g. 1,2,4-10>]";
+ }
+
+ public String getCommand()
+ {
+ return "show";
+ }
+
+ public void execute(String... args)
+ {
+ assert args.length > 0;
+ assert args[0].equals(getCommand());
+
+ if (args.length < 2)
+ {
+ parseArgs("all");
+ }
+ else
+ {
+ parseArgs(args);
+ }
+
+ performShow();
+ }
+
+ protected void parseArgs(String... args)
+ {
+ List<Long> msgids = null;
+
+ if (args.length >= 2)
+ {
+ for (String arg : args)
+ {
+ if (arg.startsWith("show="))
+ {
+ _msgHeaders = arg.contains("msgheaders") || arg.contains("all");
+ _amqHeaders = arg.contains("amqheaders") || arg.contains("all");
+ _routing = arg.contains("routing") || arg.contains("all");
+ }
+
+ if (arg.startsWith("id="))
+ {
+ _tool.getState().setMessages(msgids);
+ }
+ }//for args
+ }// if args > 2
+ }
+
+ protected void performShow()
+ {
+ if (_tool.getState().getVhost() == null)
+ {
+ _console.println("No Virtualhost selected. 'DuSelect' a Virtualhost first.");
+ return;
+ }
+
+ AMQQueue _queue = _tool.getState().getQueue();
+
+ List<Long> msgids = _tool.getState().getMessages();
+
+ if (_queue != null)
+ {
+ List<AMQMessage> messages = _queue.getMessagesOnTheQueue();
+ if (messages == null || messages.size() == 0)
+ {
+ _console.println("No messages on queue");
+ return;
+ }
+
+ List<List> data = createMessageData(msgids, messages, _amqHeaders, _routing, _msgHeaders);
+ if (data != null)
+ {
+ _console.printMap(null, data);
+ }
+ else
+ {
+ String message = "No data to display.";
+ if (msgids != null)
+ {
+ message += " Is message selection correct? " + _tool.getState().printMessages();
+ }
+ _console.println(message);
+ }
+
+ }
+ else
+ {
+ _console.println("No Queue specified to show.");
+ }
+ }
+
+ /**
+ * Create the list data for display from the messages.
+ *
+ * @param msgids The list of message ids to display
+ * @param messages A list of messages to format and display.
+ * @param showHeaders should the header info be shown
+ * @param showRouting show the routing info be shown
+ * @param showMessageHeaders show the msg headers be shown
+ * @return the formated data lists for printing
+ */
+ protected List<List> createMessageData(List<Long> msgids, List<AMQMessage> messages, boolean showHeaders, boolean showRouting,
+ boolean showMessageHeaders)
+ {
+
+ // Currenly exposed message properties
+// //Printing the content Body
+// msg.getContentBodyIterator();
+// //Print the Headers
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppId();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppIdAsString();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getClusterId();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getContentType();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getCorrelationId();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getDeliveryMode();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getEncoding();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getExpiration();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getMessageId();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPriority();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPropertyFlags();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getReplyTo();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getTimestamp();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getType();
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getUserId();
+//
+// //Print out all the property names
+// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders().getPropertyNames();
+//
+// msg.getMessageId();
+// msg.getSize();
+// msg.getArrivalTime();
+
+// msg.getDeliveredSubscription();
+// msg.getDeliveredToConsumer();
+// msg.getMessageHandle();
+// msg.getMessageId();
+// msg.getMessagePublishInfo();
+// msg.getPublisher();
+
+// msg.getStoreContext();
+// msg.isAllContentReceived();
+// msg.isPersistent();
+// msg.isRedelivered();
+// msg.isRejectedBy();
+// msg.isTaken();
+
+ //Header setup
+
+ List<List> data = new LinkedList<List>();
+
+ List<String> id = new LinkedList<String>();
+ data.add(id);
+ id.add(Columns.ID.name());
+ id.add(Console.ROW_DIVIDER);
+
+ List<String> exchange = new LinkedList<String>();
+ List<String> routingkey = new LinkedList<String>();
+ List<String> immediate = new LinkedList<String>();
+ List<String> mandatory = new LinkedList<String>();
+ if (showRouting)
+ {
+ data.add(exchange);
+ exchange.add(Columns.Exchange.name());
+ exchange.add(Console.ROW_DIVIDER);
+
+ data.add(routingkey);
+ routingkey.add(Columns.RoutingKey.name());
+ routingkey.add(Console.ROW_DIVIDER);
+
+ data.add(immediate);
+ immediate.add(Columns.isImmediate.name());
+ immediate.add(Console.ROW_DIVIDER);
+
+ data.add(mandatory);
+ mandatory.add(Columns.isMandatory.name());
+ mandatory.add(Console.ROW_DIVIDER);
+ }
+
+ List<String> size = new LinkedList<String>();
+ List<String> appid = new LinkedList<String>();
+ List<String> clusterid = new LinkedList<String>();
+ List<String> contenttype = new LinkedList<String>();
+ List<String> correlationid = new LinkedList<String>();
+ List<String> deliverymode = new LinkedList<String>();
+ List<String> encoding = new LinkedList<String>();
+ List<String> arrival = new LinkedList<String>();
+ List<String> expiration = new LinkedList<String>();
+ List<String> priority = new LinkedList<String>();
+ List<String> propertyflag = new LinkedList<String>();
+ List<String> replyto = new LinkedList<String>();
+ List<String> timestamp = new LinkedList<String>();
+ List<String> type = new LinkedList<String>();
+ List<String> userid = new LinkedList<String>();
+ List<String> ispersitent = new LinkedList<String>();
+ List<String> isredelivered = new LinkedList<String>();
+ List<String> isdelivered = new LinkedList<String>();
+
+ data.add(size);
+ size.add(Columns.Size.name());
+ size.add(Console.ROW_DIVIDER);
+
+ if (showHeaders)
+ {
+ data.add(ispersitent);
+ ispersitent.add(Columns.isPersistent.name());
+ ispersitent.add(Console.ROW_DIVIDER);
+
+ data.add(isredelivered);
+ isredelivered.add(Columns.isRedelivered.name());
+ isredelivered.add(Console.ROW_DIVIDER);
+
+ data.add(isdelivered);
+ isdelivered.add(Columns.isDelivered.name());
+ isdelivered.add(Console.ROW_DIVIDER);
+
+ data.add(appid);
+ appid.add(Columns.App_ID.name());
+ appid.add(Console.ROW_DIVIDER);
+
+ data.add(clusterid);
+ clusterid.add(Columns.Cluster_ID.name());
+ clusterid.add(Console.ROW_DIVIDER);
+
+ data.add(contenttype);
+ contenttype.add(Columns.Content_Type.name());
+ contenttype.add(Console.ROW_DIVIDER);
+
+ data.add(correlationid);
+ correlationid.add(Columns.Correlation_ID.name());
+ correlationid.add(Console.ROW_DIVIDER);
+
+ data.add(deliverymode);
+ deliverymode.add(Columns.Delivery_Mode.name());
+ deliverymode.add(Console.ROW_DIVIDER);
+
+ data.add(encoding);
+ encoding.add(Columns.Encoding.name());
+ encoding.add(Console.ROW_DIVIDER);
+
+ data.add(arrival);
+ expiration.add(Columns.Arrival.name());
+ expiration.add(Console.ROW_DIVIDER);
+
+ data.add(expiration);
+ expiration.add(Columns.Expiration.name());
+ expiration.add(Console.ROW_DIVIDER);
+
+ data.add(priority);
+ priority.add(Columns.Priority.name());
+ priority.add(Console.ROW_DIVIDER);
+
+ data.add(propertyflag);
+ propertyflag.add(Columns.Property_Flag.name());
+ propertyflag.add(Console.ROW_DIVIDER);
+
+ data.add(replyto);
+ replyto.add(Columns.ReplyTo.name());
+ replyto.add(Console.ROW_DIVIDER);
+
+ data.add(timestamp);
+ timestamp.add(Columns.Timestamp.name());
+ timestamp.add(Console.ROW_DIVIDER);
+
+ data.add(type);
+ type.add(Columns.Type.name());
+ type.add(Console.ROW_DIVIDER);
+
+ data.add(userid);
+ userid.add(Columns.UserID.name());
+ userid.add(Console.ROW_DIVIDER);
+ }
+
+ List<String> msgHeaders = new LinkedList<String>();
+ if (showMessageHeaders)
+ {
+ data.add(msgHeaders);
+ msgHeaders.add(Columns.MsgHeaders.name());
+ msgHeaders.add(Console.ROW_DIVIDER);
+ }
+
+ //Add create the table of data
+ for (AMQMessage msg : messages)
+ {
+ if (!includeMsg(msg, msgids))
+ {
+ continue;
+ }
+
+ id.add(msg.getMessageId().toString());
+
+ size.add("" + msg.getSize());
+
+ arrival.add("" + msg.getArrivalTime());
+
+ try
+ {
+ ispersitent.add(msg.isPersistent() ? "true" : "false");
+ }
+ catch (AMQException e)
+ {
+ ispersitent.add("n/a");
+ }
+
+ isredelivered.add(msg.isRedelivered() ? "true" : "false");
+
+ isdelivered.add(msg.getDeliveredToConsumer() ? "true" : "false");
+
+// msg.getMessageHandle();
+
+ BasicContentHeaderProperties headers = null;
+
+ try
+ {
+ headers = ((BasicContentHeaderProperties) msg.getContentHeaderBody().properties);
+ }
+ catch (AMQException e)
+ {
+ //ignore
+// commandError("Unable to read properties for message: " + e.getMessage(), null);
+ }
+
+ if (headers != null)
+ {
+ String appidS = headers.getAppIdAsString();
+ appid.add(appidS == null ? "null" : appidS);
+
+ String clusterS = headers.getClusterIdAsString();
+ clusterid.add(clusterS == null ? "null" : clusterS);
+
+ String contentS = headers.getContentTypeAsString();
+ contenttype.add(contentS == null ? "null" : contentS);
+
+ String correlationS = headers.getCorrelationIdAsString();
+ correlationid.add(correlationS == null ? "null" : correlationS);
+
+ deliverymode.add("" + headers.getDeliveryMode());
+
+ AMQShortString encodeSS = headers.getEncoding();
+ encoding.add(encodeSS == null ? "null" : encodeSS.toString());
+
+ expiration.add("" + headers.getExpiration());
+
+ FieldTable headerFT = headers.getHeaders();
+ msgHeaders.add(headerFT == null ? "none" : "" + headerFT.toString());
+
+ priority.add("" + headers.getPriority());
+ propertyflag.add("" + headers.getPropertyFlags());
+
+ AMQShortString replytoSS = headers.getReplyTo();
+ replyto.add(replytoSS == null ? "null" : replytoSS.toString());
+
+ timestamp.add("" + headers.getTimestamp());
+
+ AMQShortString typeSS = headers.getType();
+ type.add(typeSS == null ? "null" : typeSS.toString());
+
+ AMQShortString useridSS = headers.getUserId();
+ userid.add(useridSS == null ? "null" : useridSS.toString());
+
+ MessagePublishInfo info = null;
+ try
+ {
+ info = msg.getMessagePublishInfo();
+ }
+ catch (AMQException e)
+ {
+ //ignore
+ }
+
+ if (info != null)
+ {
+ AMQShortString exchangeSS = info.getExchange();
+ exchange.add(exchangeSS == null ? "null" : exchangeSS.toString());
+
+ AMQShortString routingkeySS = info.getRoutingKey();
+ routingkey.add(routingkeySS == null ? "null" : routingkeySS.toString());
+
+ immediate.add(info.isImmediate() ? "true" : "false");
+ mandatory.add(info.isMandatory() ? "true" : "false");
+ }
+
+// msg.getPublisher(); -- only used in clustering
+// msg.getStoreContext();
+// msg.isAllContentReceived();
+
+ }// if headers!=null
+
+// need to access internal map and do lookups.
+// msg.isTaken();
+// msg.getDeliveredSubscription();
+// msg.isRejectedBy();
+
+ }
+
+ // if id only had the header and the divider in it then we have no data to display
+ if (id.size() == 2)
+ {
+ return null;
+ }
+ return data;
+ }
+
+ protected boolean includeMsg(AMQMessage msg, List<Long> msgids)
+ {
+ if (msgids == null)
+ {
+ return true;
+ }
+
+ Long msgid = msg.getMessageId();
+
+ boolean found = false;
+
+ if (msgids != null)
+ {
+ //check msgid is in msgids
+ for (Long l : msgids)
+ {
+ if (l.equals(msgid))
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ return found;
+ }
+
+ public enum Columns
+ {
+ ID,
+ Size,
+ Exchange,
+ RoutingKey,
+ isImmediate,
+ isMandatory,
+ isPersistent,
+ isRedelivered,
+ isDelivered,
+ App_ID,
+ Cluster_ID,
+ Content_Type,
+ Correlation_ID,
+ Delivery_Mode,
+ Encoding,
+ Arrival,
+ Expiration,
+ Priority,
+ Property_Flag,
+ ReplyTo,
+ Timestamp,
+ Type,
+ UserID,
+ MsgHeaders
+ }
+}
+
+
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java
new file mode 100644
index 0000000000..c27c52eb8e
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.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.tools.security;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.DigestException;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class Passwd
+{
+ public static void main(String args[]) throws NoSuchAlgorithmException, DigestException, IOException
+ {
+ if (args.length != 2)
+ {
+ System.out.println("Passwd <username> <password>");
+ System.exit(0);
+ }
+
+ byte[] data = args[1].getBytes("utf-8");
+
+ MessageDigest md = MessageDigest.getInstance("MD5");
+
+ for (byte b : data)
+ {
+ md.update(b);
+ }
+
+ byte[] digest = md.digest();
+
+ Base64 b64 = new Base64();
+
+ byte[] encoded = b64.encode(digest);
+
+ output(args[0], encoded);
+ }
+
+ private static void output(String user, byte[] encoded) throws IOException
+ {
+
+// File passwdFile = new File("qpid.passwd");
+
+ PrintStream ps = new PrintStream(System.out);
+
+ user += ":";
+ ps.write(user.getBytes("utf-8"));
+
+ for (byte b : encoded)
+ {
+ ps.write(b);
+ }
+
+ ps.println();
+
+ ps.flush();
+ ps.close();
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java
new file mode 100644
index 0000000000..986fea32cc
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.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.tools.utils;
+
+public interface CommandParser
+{
+ /**
+ * If there is more than one command received on the last parse request.
+ *
+ * Subsequent calls to parse will utilise this input rather than reading new data from the input source
+ * @return boolean
+ */
+ boolean more();
+
+ /**
+ * True if the currently parsed command has been requested as a background operation
+ *
+ * @return boolean
+ */
+ boolean isBackground();
+
+ /**
+ * Parses user commands, and groups tokens in the
+ * String[] format that all Java main's love.
+ *
+ * If more than one command is provided in one input line then the more() method will return true.
+ * A subsequent call to parse() will continue to parse that input line before reading new input.
+ *
+ * @return <code>input</code> split in args[] format; null if eof.
+ * @throws java.io.IOException if there is a problem reading from the input stream
+ */
+ String[] parse() throws java.io.IOException;
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java
new file mode 100644
index 0000000000..cf457d1ea5
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.utils;
+
+import java.util.List;
+
+public interface Console
+{
+ public enum CellFormat
+ {
+ CENTRED, LEFT, RIGHT
+ }
+
+ public static String ROW_DIVIDER = "*divider";
+
+ public void print(String... message);
+
+ public void println(String... message);
+
+ public String readln();
+
+ /**
+ * Reads and parses the command line.
+ *
+ *
+ * @return The next command or null
+ */
+ public String[] readCommand();
+
+ public CommandParser getCommandParser();
+
+ public void setCommandParser(CommandParser parser);
+
+ /**
+ *
+ * Prints the list of String nicely.
+ *
+ * +-------------+
+ * | Heading |
+ * +-------------+
+ * | Item 1 |
+ * | Item 2 |
+ * | Item 3 |
+ * +-------------+
+ *
+ * @param hasTitle should list[0] be used as a heading
+ * @param list The list of Strings to display
+ */
+ public void displayList(boolean hasTitle, String... list);
+
+ /**
+ *
+ * Prints the list of String nicely.
+ *
+ * +----------------------------+
+ * | Heading |
+ * +----------------------------+
+ * | title | title | ..
+ * +----------------------------+
+ * | Item 2 | value 2 | ..
+ * +----------------------------+ (*divider)
+ * | Item 3 | value 2 | ..
+ * +----------------------------+
+ *
+ * @param title The title to display if any
+ * @param entries the entries to display in a map.
+ */
+ void printMap(String title, List<List> entries);
+
+
+ public void close();
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java
new file mode 100644
index 0000000000..09444ccdd7
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+public class SimpleCommandParser implements CommandParser
+{
+ private static final String COMMAND_SEPERATOR = ";";
+
+ /** Input source of commands */
+ protected BufferedReader _reader;
+
+ /** The next list of commands from the command line */
+ private StringBuilder _nextCommand = null;
+
+ public SimpleCommandParser(BufferedReader reader)
+ {
+ _reader = reader;
+ }
+
+ public boolean more()
+ {
+ return _nextCommand != null;
+ }
+
+ public boolean isBackground()
+ {
+ return false;
+ }
+
+ public String[] parse() throws IOException
+ {
+ String[] commands = null;
+
+ String input = null;
+
+ if (_nextCommand == null)
+ {
+ input = _reader.readLine();
+ }
+ else
+ {
+ input = _nextCommand.toString();
+ _nextCommand = null;
+ }
+
+ if (input == null)
+ {
+ return null;
+ }
+
+ StringTokenizer tok = new StringTokenizer(input, " ");
+
+ int tokenCount = tok.countTokens();
+ int index = 0;
+
+ if (tokenCount > 0)
+ {
+ commands = new String[tokenCount];
+ boolean commandComplete = false;
+
+ while (tok.hasMoreTokens())
+ {
+ String next = tok.nextToken();
+
+ if (next.equals(COMMAND_SEPERATOR))
+ {
+ commandComplete = true;
+ _nextCommand = new StringBuilder();
+ continue;
+ }
+
+ if (commandComplete)
+ {
+ _nextCommand.append(next);
+ _nextCommand.append(" ");
+ }
+ else
+ {
+ commands[index] = next;
+ index++;
+ }
+ }
+
+ }
+
+ //Reduce the String[] if not all the tokens were used in this command.
+ // i.e. there is more than one command on the line.
+ if (index != tokenCount)
+ {
+ String[] shortCommands = new String[index];
+ System.arraycopy(commands, 0, shortCommands, 0, index);
+ return shortCommands;
+ }
+ else
+ {
+ return commands;
+ }
+ }
+}
diff --git a/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java
new file mode 100644
index 0000000000..ec080a4611
--- /dev/null
+++ b/Final/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.tools.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+public class SimpleConsole implements Console
+{
+ /** SLF4J Logger. */
+ private static Logger _devlog = LoggerFactory.getLogger(SimpleConsole.class);
+
+ /** Console Writer. */
+ protected static BufferedWriter _consoleWriter;
+
+ /** Console Reader. */
+ protected static BufferedReader _consoleReader;
+
+ /** Parser for command-line input. */
+ protected CommandParser _parser;
+
+ public SimpleConsole(BufferedWriter writer, BufferedReader reader)
+ {
+ _consoleWriter = writer;
+ _consoleReader = reader;
+ _parser = new SimpleCommandParser(_consoleReader);
+ }
+
+ public void print(String... message)
+ {
+ try
+ {
+ for (String s : message)
+ {
+ _consoleWriter.write(s);
+ }
+ _consoleWriter.flush();
+ }
+ catch (IOException e)
+ {
+ _devlog.error(e.getMessage() + ": Occured whilst trying to write:" + message);
+ }
+
+ }
+
+ public void println(String... message)
+ {
+ print(message);
+ print(System.getProperty("line.separator"));
+ }
+
+
+ public String readln()
+ {
+ try
+ {
+ return _consoleReader.readLine();
+ }
+ catch (IOException e)
+ {
+ _devlog.debug("Unable to read input due to:" + e.getMessage());
+ return null;
+ }
+ }
+
+ public String[] readCommand()
+ {
+ try
+ {
+ return _parser.parse();
+ }
+ catch (IOException e)
+ {
+ _devlog.error("Error reading command:" + e.getMessage());
+ return new String[0];
+ }
+ }
+
+ public CommandParser getCommandParser()
+ {
+ return _parser;
+ }
+
+ public void setCommandParser(CommandParser parser)
+ {
+ _parser = parser;
+ }
+
+ public void displayList(boolean hasTitle, String... list)
+ {
+ java.util.List<java.util.List> data = new LinkedList<List>();
+
+ java.util.List<String> values = new LinkedList<String>();
+
+ data.add(values);
+
+ for (String value : list)
+ {
+ values.add(value);
+ }
+
+ if (hasTitle)
+ {
+ values.add(1, "*divider");
+ }
+
+ printMap(null, data);
+ }
+
+ /**
+ *
+ * Prints the list of String nicely.
+ *
+ * +----------------------------+
+ * | Heading |
+ * +----------------------------+
+ * | title | title | ..
+ * +----------------------------+
+ * | Item 2 | value 2 | ..
+ * | Item 3 | value 2 | ..
+ * +----------------------------+
+ *
+ * @param title The title to display if any
+ * @param entries the entries to display in a map.
+ */
+ public void printMap(String title, java.util.List<java.util.List> entries)
+ {
+ try
+ {
+ int columns = entries.size();
+
+ int[] columnWidth = new int[columns];
+
+ // calculate row count
+ int rowMax = 0;
+
+ //the longest item
+ int itemMax = 0;
+
+ for (int i = 0; i < columns; i++)
+ {
+ int columnIRowMax = entries.get(i).size();
+
+ if (columnIRowMax > rowMax)
+ {
+ rowMax = columnIRowMax;
+ }
+ for (Object values : entries.get(i))
+ {
+ if (values.toString().equals(Console.ROW_DIVIDER))
+ {
+ continue;
+ }
+
+ int itemLength = values.toString().length();
+
+ //note for single width
+ if (itemLength > itemMax)
+ {
+ itemMax = itemLength;
+ }
+
+ //note for mulit width
+ if (itemLength > columnWidth[i])
+ {
+ columnWidth[i] = itemLength;
+ }
+
+ }
+ }
+
+ int tableWidth = 0;
+
+
+ for (int i = 0; i < columns; i++)
+ {
+ // plus 2 for the space padding
+ columnWidth[i] += 2;
+ }
+ for (int size : columnWidth)
+ {
+ tableWidth += size;
+ }
+ tableWidth += (columns - 1);
+
+ if (title != null)
+ {
+ if (title.length() > tableWidth)
+ {
+ tableWidth = title.length();
+ }
+
+ printCellRow("+", "-", tableWidth);
+
+ printCell(CellFormat.CENTRED, "|", tableWidth, " " + title + " ", 0);
+ _consoleWriter.newLine();
+
+ }
+
+ //put top line | or bottom of title
+ printCellRow("+", "-", tableWidth);
+
+ //print the table data
+ int row = 0;
+
+ for (; row < rowMax; row++)
+ {
+ for (int i = 0; i < columns; i++)
+ {
+ java.util.List columnData = entries.get(i);
+
+ String value;
+ // does this column have a value for this row
+ if (columnData.size() > row)
+ {
+ value = " " + columnData.get(row).toString() + " ";
+ }
+ else
+ {
+ value = " ";
+ }
+
+ if (i == 0 && value.equals(" " + Console.ROW_DIVIDER + " "))
+ {
+ printCellRow("+", "-", tableWidth);
+ //move on to the next row
+ break;
+ }
+ else
+ {
+ printCell(CellFormat.LEFT, "|", columnWidth[i], value, i);
+ }
+
+ // if it is the last row then do a new line.
+ if (i == columns - 1)
+ {
+ _consoleWriter.newLine();
+ }
+ }
+ }
+
+ printCellRow("+", "-", tableWidth);
+
+ }
+ catch (IOException e)
+ {
+ _devlog.error(e.getMessage() + ": Occured whilst trying to write.");
+ }
+ }
+
+ public void close()
+ {
+
+ try
+ {
+ _consoleReader.close();
+ }
+ catch (IOException e)
+ {
+ _devlog.error(e.getMessage() + ": Occured whilst trying to close reader.");
+ }
+
+ try
+ {
+
+ _consoleWriter.close();
+ }
+ catch (IOException e)
+ {
+ _devlog.error(e.getMessage() + ": Occured whilst trying to close writer.");
+ }
+
+ }
+
+ private void printCell(CellFormat format, String edge, int cellWidth, String cell, int column) throws IOException
+ {
+ int pad = cellWidth - cell.length();
+
+ if (column == 0)
+ {
+ _consoleWriter.write(edge);
+ }
+
+ switch (format)
+ {
+ case CENTRED:
+ printPad(" ", pad / 2);
+ break;
+ case RIGHT:
+ printPad(" ", pad);
+ break;
+ }
+
+ _consoleWriter.write(cell);
+
+
+ switch (format)
+ {
+ case CENTRED:
+ // if pad isn't even put the extra one on the right
+ if (pad % 2 == 0)
+ {
+ printPad(" ", pad / 2);
+ }
+ else
+ {
+ printPad(" ", (pad / 2) + 1);
+ }
+ break;
+ case LEFT:
+ printPad(" ", pad);
+ break;
+ }
+
+
+ _consoleWriter.write(edge);
+
+ }
+
+ private void printCellRow(String edge, String mid, int cellWidth) throws IOException
+ {
+ _consoleWriter.write(edge);
+
+ printPad(mid, cellWidth);
+
+ _consoleWriter.write(edge);
+ _consoleWriter.newLine();
+ }
+
+ private void printPad(String padChar, int count) throws IOException
+ {
+ for (int i = 0; i < count; i++)
+ {
+ _consoleWriter.write(padChar);
+ }
+ }
+
+
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java b/Final/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java
new file mode 100644
index 0000000000..b8803206e0
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+public class RunBrokerWithCommand
+{
+ public static void main(String[] args)
+ {
+ //Start broker
+ try
+ {
+ String[] fudge = args.clone();
+
+ // Override the first value which is the command we are going to run later.
+ fudge[0] = "-v";
+ new Main(fudge).startup();
+ }
+ catch (Exception e)
+ {
+ System.out.println("Unable to start broker due to: " + e.getMessage());
+
+ e.printStackTrace();
+ exit(1);
+ }
+
+ Logger.getRootLogger().setLevel(Level.ERROR);
+
+ //run command
+ try
+ {
+ Process task = Runtime.getRuntime().exec(args[0]);
+ System.out.println("Started Proccess: " + args[0]);
+
+ InputStream inputStream = task.getInputStream();
+
+ InputStream errorStream = task.getErrorStream();
+
+ Thread out = new Thread(new Outputter("[OUT]", new BufferedReader(new InputStreamReader(inputStream))));
+ Thread err = new Thread(new Outputter("[ERR]", new BufferedReader(new InputStreamReader(errorStream))));
+
+ out.start();
+ err.start();
+
+ out.join();
+ err.join();
+
+ System.out.println("Waiting for process to exit: " + args[0]);
+ task.waitFor();
+ System.out.println("Done Proccess: " + args[0]);
+
+ }
+ catch (IOException e)
+ {
+ System.out.println("Proccess had problems: " + e.getMessage());
+ exit(1);
+ }
+ catch (InterruptedException e)
+ {
+ System.out.println("Proccess had problems: " + e.getMessage());
+
+ exit(1);
+ }
+
+
+ exit(0);
+ }
+
+ private static void exit(int i)
+ {
+ Logger.getRootLogger().setLevel(Level.INFO);
+ System.exit(i);
+ }
+
+ static class Outputter implements Runnable
+ {
+
+ BufferedReader reader;
+ String prefix;
+
+ Outputter(String s, BufferedReader r)
+ {
+ prefix = s;
+ reader = r;
+ }
+
+ public void run()
+ {
+ String line;
+ try
+ {
+ while ((line = reader.readLine()) != null)
+ {
+ System.out.println(prefix + line);
+ }
+ }
+ catch (IOException e)
+ {
+ System.out.println("Error occured reading; " + e.getMessage());
+ }
+ }
+
+ }
+
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java b/Final/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java
new file mode 100644
index 0000000000..3b83190e42
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.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.server.configuration;
+
+import org.apache.qpid.configuration.PropertyException;
+import org.apache.qpid.configuration.PropertyUtils;
+
+import junit.framework.TestCase;
+
+// TODO: This belongs in the "common" module.
+public class TestPropertyUtils extends TestCase
+{
+ public void testSimpleExpansion() throws PropertyException
+ {
+ System.setProperty("banana", "fruity");
+ String expandedProperty = PropertyUtils.replaceProperties("${banana}");
+ assertEquals(expandedProperty, "fruity");
+ }
+
+ public void testDualExpansion() throws PropertyException
+ {
+ System.setProperty("banana", "fruity");
+ System.setProperty("concrete", "horrible");
+ String expandedProperty = PropertyUtils.replaceProperties("${banana}xyz${concrete}");
+ assertEquals(expandedProperty, "fruityxyzhorrible");
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TestPropertyUtils.class);
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java b/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java
new file mode 100644
index 0000000000..0a3bc93763
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.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.exchange;
+
+import junit.framework.TestCase;
+import junit.framework.Assert;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.MessageHandleFactory;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.txn.NonTransactionalContext;
+import org.apache.qpid.server.txn.TransactionalContext;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.LinkedList;
+
+public class DestWildExchangeTest extends TestCase
+{
+
+ DestWildExchange _exchange;
+
+ VirtualHost _vhost;
+ MessageStore _store;
+ StoreContext _context;
+
+
+ public void setUp() throws AMQException
+ {
+ _exchange = new DestWildExchange();
+ _vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next();
+ _store = new MemoryMessageStore();
+ _context = new StoreContext();
+ }
+
+
+ public void testNoRoute() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a*#b"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null);
+
+
+ MessagePublishInfo info = new PublishInfo(new AMQShortString("a.b"));
+
+ AMQMessage message = new AMQMessage(0L, info, null);
+
+ try
+ {
+ _exchange.route(message);
+ fail("Message has no route and shouldn't be routed");
+ }
+ catch (NoRouteException nre)
+ {
+ //normal
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+ public void testDirectMatch() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("ab"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.b"), queue, null);
+
+
+ AMQMessage message = createMessage("a.b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has no route and should fail to be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+
+ public void testStarMatch() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a*"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.*"), queue, null);
+
+
+ AMQMessage message = createMessage("a.b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has no route and should fail to be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+ public void testHashMatch() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.#"), queue, null);
+
+
+ AMQMessage message = createMessage("a.b.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has no route and should fail to be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+ }
+
+
+ public void testMidHash() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null);
+
+
+ AMQMessage message = createMessage("a.c.d.b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has no route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.c.b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has no route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testMatchafterHash() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null);
+
+
+ AMQMessage message = createMessage("a.c.b.b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has route and should not be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.a.b.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has no route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.b.c.b");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has route and should not be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.b.c.b.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has no route and should be routed");
+
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+
+ public void testHashAfterHash() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null);
+
+
+ AMQMessage message = createMessage("a.c.b.b.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has route and should not be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+
+ message = createMessage("a.a.b.c.d");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has no route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testHashHash() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null);
+
+
+ AMQMessage message = createMessage("a.c.b.b.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has route and should not be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ message = createMessage("a.a.b.c.d");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ }
+ catch (AMQException nre)
+ {
+ fail("Message has no route and should be routed");
+ }
+
+ Assert.assertEquals(1, queue.getMessageCount());
+
+ Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0));
+
+ queue.deleteMessageFromTop(_context);
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testSubMatchFails() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null);
+
+
+ AMQMessage message = createMessage("a.b.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has route and should not be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testMoreRouting() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.b"), queue, null);
+
+
+ AMQMessage message = createMessage("a.b.c");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has route and should not be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ public void testMoreQueue() throws AMQException
+ {
+ AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ _exchange.registerQueue(new AMQShortString("a.b"), queue, null);
+
+
+ AMQMessage message = createMessage("a");
+
+ try
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, _context, new MessageHandleFactory());
+ fail("Message has route and should not be routed");
+ }
+ catch (AMQException nre)
+ {
+ }
+
+ Assert.assertEquals(0, queue.getMessageCount());
+
+ }
+
+ private AMQMessage createMessage(String s) throws AMQException
+ {
+ MessagePublishInfo info = new PublishInfo(new AMQShortString(s));
+
+ TransactionalContext trancontext = new NonTransactionalContext(_store, _context, null,
+ new LinkedList<RequiredDeliveryException>(),
+ new HashSet<Long>());
+
+ AMQMessage message = new AMQMessage(0L, info, trancontext);
+ message.setContentHeaderBody(new ContentHeaderBody());
+
+ return message;
+ }
+
+
+ class PublishInfo implements MessagePublishInfo
+ {
+ AMQShortString _routingkey;
+
+ PublishInfo(AMQShortString routingkey)
+ {
+ _routingkey = routingkey;
+ }
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return true;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return _routingkey;
+ }
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java
new file mode 100644
index 0000000000..18d8592817
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java
@@ -0,0 +1,138 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import junit.framework.TestCase;
+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.management.ManagedObject;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+import java.util.ArrayList;
+
+/**
+ * Unit test class for testing different Exchange MBean operations
+ */
+public class ExchangeMBeanTest extends TestCase
+{
+ private AMQQueue _queue;
+ private QueueRegistry _queueRegistry;
+ private VirtualHost _virtualHost;
+
+ /**
+ * Test for direct exchange mbean
+ * @throws Exception
+ */
+
+ public void testDirectExchangeMBean() throws Exception
+ {
+ DestNameExchange exchange = new DestNameExchange();
+ exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true);
+ ManagedObject managedObj = exchange.getManagedObject();
+ ManagedExchange mbean = (ManagedExchange)managedObj;
+
+ mbean.createNewBinding(_queue.getName().toString(), "binding1");
+ mbean.createNewBinding(_queue.getName().toString(), "binding2");
+
+ TabularData data = mbean.bindings();
+ ArrayList<Object> list = new ArrayList<Object>(data.values());
+ assertTrue(list.size() == 2);
+
+ // test general exchange properties
+ assertEquals(mbean.getName(), "amq.direct");
+ assertEquals(mbean.getExchangeType(), "direct");
+ assertTrue(mbean.getTicketNo() == 0);
+ assertTrue(!mbean.isDurable());
+ assertTrue(mbean.isAutoDelete());
+ }
+
+ /**
+ * Test for "topic" exchange mbean
+ * @throws Exception
+ */
+
+ public void testTopicExchangeMBean() throws Exception
+ {
+ DestWildExchange exchange = new DestWildExchange();
+ exchange.initialise(_virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true);
+ ManagedObject managedObj = exchange.getManagedObject();
+ ManagedExchange mbean = (ManagedExchange)managedObj;
+
+ mbean.createNewBinding(_queue.getName().toString(), "binding1");
+ mbean.createNewBinding(_queue.getName().toString(), "binding2");
+
+ TabularData data = mbean.bindings();
+ ArrayList<Object> list = new ArrayList<Object>(data.values());
+ assertTrue(list.size() == 2);
+
+ // test general exchange properties
+ assertEquals(mbean.getName(), "amq.topic");
+ assertEquals(mbean.getExchangeType(), "topic");
+ assertTrue(mbean.getTicketNo() == 0);
+ assertTrue(!mbean.isDurable());
+ assertTrue(mbean.isAutoDelete());
+ }
+
+ /**
+ * Test for "Headers" exchange mbean
+ * @throws Exception
+ */
+
+ public void testHeadersExchangeMBean() throws Exception
+ {
+ HeadersExchange exchange = new HeadersExchange();
+ exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true);
+ ManagedObject managedObj = exchange.getManagedObject();
+ ManagedExchange mbean = (ManagedExchange)managedObj;
+
+ mbean.createNewBinding(_queue.getName().toString(), "key1=binding1,key2=binding2");
+ mbean.createNewBinding(_queue.getName().toString(), "key3=binding3");
+
+ TabularData data = mbean.bindings();
+ ArrayList<Object> list = new ArrayList<Object>(data.values());
+ assertTrue(list.size() == 2);
+
+ // test general exchange properties
+ assertEquals(mbean.getName(), "amq.match");
+ assertEquals(mbean.getExchangeType(), "headers");
+ assertTrue(mbean.getTicketNo() == 0);
+ assertTrue(!mbean.isDurable());
+ assertTrue(mbean.isAutoDelete());
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
+ _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ _queueRegistry = _virtualHost.getQueueRegistry();
+ _queue = new AMQQueue(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost);
+ _queueRegistry.registerQueue(_queue);
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java b/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java
new file mode 100644
index 0000000000..86ba96bf5d
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.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.server.exchange;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import junit.framework.TestCase;
+import org.apache.qpid.framing.FieldTable;
+
+/**
+ */
+public class HeadersBindingTest extends TestCase
+{
+ private FieldTable bindHeaders = new FieldTable();
+ private FieldTable matchHeaders = new FieldTable();
+
+ public void testDefault_1()
+ {
+ bindHeaders.setString("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testDefault_2()
+ {
+ bindHeaders.setString("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testDefault_3()
+ {
+ bindHeaders.setString("A", "Value of A");
+
+ matchHeaders.setString("A", "Altered value of A");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAll_1()
+ {
+ bindHeaders.setString("X-match", "all");
+ bindHeaders.setString("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAll_2()
+ {
+ bindHeaders.setString("X-match", "all");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAll_3()
+ {
+ bindHeaders.setString("X-match", "all");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAll_4()
+ {
+ bindHeaders.setString("X-match", "all");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAll_5()
+ {
+ bindHeaders.setString("X-match", "all");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Altered value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAny_1()
+ {
+ bindHeaders.setString("X-match", "any");
+ bindHeaders.setString("A", "Value of A");
+
+ matchHeaders.setString("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAny_2()
+ {
+ bindHeaders.setString("X-match", "any");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAny_3()
+ {
+ bindHeaders.setString("X-match", "any");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAny_4()
+ {
+ bindHeaders.setString("X-match", "any");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAny_5()
+ {
+ bindHeaders.setString("X-match", "any");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Value of A");
+ matchHeaders.setString("B", "Altered value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public void testAny_6()
+ {
+ bindHeaders.setString("X-match", "any");
+ bindHeaders.setString("A", "Value of A");
+ bindHeaders.setString("B", "Value of B");
+
+ matchHeaders.setString("A", "Altered value of A");
+ matchHeaders.setString("B", "Altered value of B");
+ matchHeaders.setString("C", "Value of C");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(HeadersBindingTest.class);
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java b/Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java
new file mode 100644
index 0000000000..ff4d3ed9fb
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java
@@ -0,0 +1,295 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.mina.common.*;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented,
+ * so if this class is being used and some methods are to be used, then please update those.
+ */
+public class TestIoSession implements IoSession
+{
+ private final ConcurrentMap attributes = new ConcurrentHashMap();
+
+ public TestIoSession()
+ {
+ }
+
+ public IoService getService()
+ {
+ return null;
+ }
+
+ public IoServiceConfig getServiceConfig()
+ {
+ return new TestIoConfig();
+ }
+
+ public IoHandler getHandler()
+ {
+ return null;
+ }
+
+ public IoSessionConfig getConfig()
+ {
+ return null;
+ }
+
+ public IoFilterChain getFilterChain()
+ {
+ return null;
+ }
+
+ public WriteFuture write(Object message)
+ {
+ return null;
+ }
+
+ public CloseFuture close()
+ {
+ return null;
+ }
+
+ public Object getAttachment()
+ {
+ return getAttribute("");
+ }
+
+ public Object setAttachment(Object attachment)
+ {
+ return setAttribute("",attachment);
+ }
+
+ public Object getAttribute(String key)
+ {
+ return attributes.get(key);
+ }
+
+ public Object setAttribute(String key, Object value)
+ {
+ return attributes.put(key,value);
+ }
+
+ public Object setAttribute(String key)
+ {
+ return attributes.put(key, Boolean.TRUE);
+ }
+
+ public Object removeAttribute(String key)
+ {
+ return attributes.remove(key);
+ }
+
+ public boolean containsAttribute(String key)
+ {
+ return attributes.containsKey(key);
+ }
+
+ public Set getAttributeKeys()
+ {
+ return attributes.keySet();
+ }
+
+ public TransportType getTransportType()
+ {
+ return null;
+ }
+
+ public boolean isConnected()
+ {
+ return false;
+ }
+
+ public boolean isClosing()
+ {
+ return false;
+ }
+
+ public CloseFuture getCloseFuture()
+ {
+ return null;
+ }
+
+ public SocketAddress getRemoteAddress()
+ {
+ return new InetSocketAddress("127.0.0.1", 1234);
+ }
+
+ public SocketAddress getLocalAddress()
+ {
+ return null;
+ }
+
+ public SocketAddress getServiceAddress()
+ {
+ return null;
+ }
+
+ public int getIdleTime(IdleStatus status)
+ {
+ return 0;
+ }
+
+ public long getIdleTimeInMillis(IdleStatus status)
+ {
+ return 0;
+ }
+
+ public void setIdleTime(IdleStatus status, int idleTime)
+ {
+
+ }
+
+ public int getWriteTimeout()
+ {
+ return 0;
+ }
+
+ public long getWriteTimeoutInMillis()
+ {
+ return 0;
+ }
+
+ public void setWriteTimeout(int writeTimeout)
+ {
+
+ }
+
+ public TrafficMask getTrafficMask()
+ {
+ return null;
+ }
+
+ public void setTrafficMask(TrafficMask trafficMask)
+ {
+
+ }
+
+ public void suspendRead()
+ {
+
+ }
+
+ public void suspendWrite()
+ {
+
+ }
+
+ public void resumeRead()
+ {
+
+ }
+
+ public void resumeWrite()
+ {
+
+ }
+
+ public long getReadBytes()
+ {
+ return 0;
+ }
+
+ public long getWrittenBytes()
+ {
+ return 0;
+ }
+
+ public long getReadMessages()
+ {
+ return 0;
+ }
+
+ public long getWrittenMessages()
+ {
+ return 0;
+ }
+
+ public long getWrittenWriteRequests()
+ {
+ return 0;
+ }
+
+ public int getScheduledWriteRequests()
+ {
+ return 0;
+ }
+
+ public int getScheduledWriteBytes()
+ {
+ return 0;
+ }
+
+ public long getCreationTime()
+ {
+ return 0;
+ }
+
+ public long getLastIoTime()
+ {
+ return 0;
+ }
+
+ public long getLastReadTime()
+ {
+ return 0;
+ }
+
+ public long getLastWriteTime()
+ {
+ return 0;
+ }
+
+ public boolean isIdle(IdleStatus status)
+ {
+ return false;
+ }
+
+ public int getIdleCount(IdleStatus status)
+ {
+ return 0;
+ }
+
+ public long getLastIdleTime(IdleStatus status)
+ {
+ return 0;
+ }
+
+ /**
+ * Test implementation of IoServiceConfig
+ */
+ private class TestIoConfig extends SocketAcceptorConfig
+ {
+ public ThreadModel getThreadModel()
+ {
+ return ReadWriteThreadModel.getInstance();
+ }
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java b/Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java
new file mode 100644
index 0000000000..0c0d8f471e
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.server.output.ProtocolOutputConverterRegistry;
+
+public class TestMinaProtocolSession extends AMQMinaProtocolSession
+{
+ public TestMinaProtocolSession() throws AMQException
+ {
+ super(new TestIoSession(),
+ ApplicationRegistry.getInstance().getVirtualHostRegistry(),
+ new AMQCodecFactory(true));
+ }
+
+ public ProtocolOutputConverter getProtocolOutputConverter()
+ {
+ return ProtocolOutputConverterRegistry.getConverter(this);
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return (byte)8;
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return (byte)0;
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
new file mode 100644
index 0000000000..b718b9c861
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
@@ -0,0 +1,301 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.store.StoreContext;
+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.txn.TransactionalContext;
+import org.apache.qpid.server.txn.NonTransactionalContext;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.TestMinaProtocolSession;
+import org.apache.qpid.server.protocol.AMQMinaProtocolSession;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+
+import javax.management.Notification;
+import java.util.LinkedList;
+import java.util.HashSet;
+
+/** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */
+public class AMQQueueAlertTest extends TestCase
+{
+ private final static long MAX_MESSAGE_COUNT = 50;
+ private final static long MAX_MESSAGE_AGE = 250; // 0.25 sec
+ private final static long MAX_MESSAGE_SIZE = 2000; // 2 KB
+ private final static long MAX_QUEUE_DEPTH = 10000; // 10 KB
+ private AMQQueue _queue;
+ private AMQQueueMBean _queueMBean;
+ private VirtualHost _virtualHost;
+ private AMQMinaProtocolSession protocolSession = null;
+ private MessageStore _messageStore = new MemoryMessageStore();
+ private StoreContext _storeContext = new StoreContext();
+ private TransactionalContext _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext,
+ null,
+ new LinkedList<RequiredDeliveryException>(),
+ new HashSet<Long>());
+
+ /**
+ * Tests if the alert gets thrown when message count increases the threshold limit
+ *
+ * @throws Exception
+ */
+ public void testMessageCountAlert() throws Exception
+ {
+ _queue = new AMQQueue(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"),
+ false, _virtualHost);
+ _queueMBean = (AMQQueueMBean) _queue.getManagedObject();
+
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+
+ sendMessages(MAX_MESSAGE_COUNT, 256l);
+ assertTrue(_queueMBean.getMessageCount() == MAX_MESSAGE_COUNT);
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_COUNT_ALERT.name()));
+ }
+
+ /**
+ * Tests if the Message Size alert gets thrown when message of higher than threshold limit is sent
+ *
+ * @throws Exception
+ */
+ public void testMessageSizeAlert() throws Exception
+ {
+ _queue = new AMQQueue(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"),
+ false, _virtualHost);
+ _queueMBean = (AMQQueueMBean) _queue.getManagedObject();
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+ _queueMBean.setMaximumMessageSize(MAX_MESSAGE_SIZE);
+
+ sendMessages(1, MAX_MESSAGE_SIZE * 2);
+ assertTrue(_queueMBean.getMessageCount() == 1);
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_SIZE_ALERT.name()));
+ }
+
+ /**
+ * Tests if Queue Depth alert is thrown when queue depth reaches the threshold value
+ *
+ * Based on FT-402 subbmitted by client
+ *
+ * @throws Exception
+ */
+ public void testQueueDepthAlertNoSubscriber() throws Exception
+ {
+ _queue = new AMQQueue(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"),
+ false, _virtualHost);
+ _queueMBean = (AMQQueueMBean) _queue.getManagedObject();
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+ _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH);
+
+ while (_queue.getQueueDepth() < MAX_QUEUE_DEPTH)
+ {
+ sendMessages(1, MAX_MESSAGE_SIZE);
+ }
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.QUEUE_DEPTH_ALERT.name()));
+ }
+
+ /**
+ * Tests if MESSAGE AGE alert is thrown, when a message is in the queue for time higher than threshold value of
+ * message age
+ *
+ * Alternative test to FT-401 provided by client
+ *
+ * @throws Exception
+ */
+ public void testMessageAgeAlert() throws Exception
+ {
+ _queue = new AMQQueue(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"),
+ false, _virtualHost);
+ _queueMBean = (AMQQueueMBean) _queue.getManagedObject();
+ _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
+ _queueMBean.setMaximumMessageAge(MAX_MESSAGE_AGE);
+
+ sendMessages(1, MAX_MESSAGE_SIZE);
+
+ // Ensure message sits on queue long enough to age.
+ Thread.sleep(MAX_MESSAGE_AGE * 2);
+
+ sendMessages(1, MAX_MESSAGE_SIZE);
+ assertTrue(_queueMBean.getMessageCount() == 2);
+
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_AGE_ALERT.name()));
+ }
+
+ /*
+ This test sends some messages to the queue with subscribers needing message to be acknowledged.
+ The messages will not be acknowledged and will be required twice. Why we are checking this is because
+ the bug reported said that the queueDepth keeps increasing when messages are requeued.
+ The QueueDepth should decrease when messages are delivered from the queue (QPID-408)
+ */
+ public void testQueueDepthAlertWithSubscribers() throws Exception
+ {
+ protocolSession = new TestMinaProtocolSession();
+ AMQChannel channel = new AMQChannel(protocolSession, 2, _messageStore, null);
+ protocolSession.addChannel(channel);
+
+ // Create queue
+ _queue = getNewQueue();
+ _queue.registerProtocolSession(protocolSession, channel.getChannelId(),
+ new AMQShortString("consumer_tag"), true, null, false, false);
+
+ _queueMBean = (AMQQueueMBean) _queue.getManagedObject();
+ _queueMBean.setMaximumMessageCount(9999l); // Set a high value, because this is not being tested
+ _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH);
+
+ // Send messages(no of message to be little more than what can cause a Queue_Depth alert)
+ int messageCount = Math.round(MAX_QUEUE_DEPTH / MAX_MESSAGE_SIZE) + 10;
+ long totalSize = (messageCount * MAX_MESSAGE_SIZE) >> 10;
+ sendMessages(messageCount, MAX_MESSAGE_SIZE);
+
+ // Check queueDepth. There should be no messages on the queue and as the subscriber is listening
+ // so there should be no Queue_Deoth alert raised
+ assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
+ Notification lastNotification = _queueMBean.getLastNotification();
+ assertNull(lastNotification);
+
+ // Kill the subscriber and check for the queue depth values.
+ // Messages are unacknowledged, so those should get requeued. All messages should be on the Queue
+ _queue.unregisterProtocolSession(protocolSession, channel.getChannelId(), new AMQShortString("consumer_tag"));
+ channel.requeue();
+
+ assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
+
+ lastNotification = _queueMBean.getLastNotification();
+ assertNotNull(lastNotification);
+ String notificationMsg = lastNotification.getMessage();
+ assertTrue(notificationMsg.startsWith(NotificationCheck.QUEUE_DEPTH_ALERT.name()));
+
+ // Connect a consumer again and check QueueDepth values. The queue should get emptied.
+ // Messages will get delivered but still are unacknowledged.
+ _queue.registerProtocolSession(protocolSession, channel.getChannelId(),
+ new AMQShortString("consumer_tag"), true, null, false, false);
+ _queue.deliverAsync();
+ while (_queue.getMessageCount() != 0)
+ {
+ Thread.sleep(100);
+ }
+ assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
+
+ // Kill the subscriber again. Now those messages should get requeued again. Check if the queue depth
+ // value is correct.
+ _queue.unregisterProtocolSession(protocolSession, channel.getChannelId(), new AMQShortString("consumer_tag"));
+ channel.requeue();
+
+ assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
+ protocolSession.closeSession();
+
+ // Check the clear queue
+ _queueMBean.clearQueue();
+ assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
+ }
+
+ protected AMQMessage message(final boolean immediate, long size) throws AMQException
+ {
+ MessagePublishInfo publish = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean isImmediate()
+ {
+ return immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ ContentHeaderBody contentHeaderBody = new ContentHeaderBody();
+ contentHeaderBody.bodySize = size; // in bytes
+ AMQMessage message = new AMQMessage(_messageStore.getNewMessageId(), publish, _transactionalContext);
+ message.setContentHeaderBody(contentHeaderBody);
+ message.setPublisher(protocolSession);
+ return message;
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
+ _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ }
+
+ private void sendMessages(long messageCount, long size) throws AMQException
+ {
+ AMQMessage[] messages = new AMQMessage[(int) messageCount];
+ for (int i = 0; i < messages.length; i++)
+ {
+ messages[i] = message(false, size);
+ messages[i].enqueue(_queue);
+ messages[i].routingComplete(_messageStore, _storeContext, new MessageHandleFactory());
+ }
+
+ for (int i = 0; i < messageCount; i++)
+ {
+ _queue.process(_storeContext, messages[i], false);
+ }
+ }
+
+ private AMQQueue getNewQueue() throws AMQException
+ {
+ return new AMQQueue(new AMQShortString("testQueue" + Math.random()),
+ false,
+ new AMQShortString("AMQueueAlertTest"),
+ false,
+ _virtualHost);
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
new file mode 100644
index 0000000000..3caf6ad73d
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
@@ -0,0 +1,300 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.protocol.TestMinaProtocolSession;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+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.txn.TransactionalContext;
+import org.apache.qpid.server.txn.NonTransactionalContext;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.mina.common.ByteBuffer;
+
+import javax.management.JMException;
+import java.util.LinkedList;
+import java.util.HashSet;
+
+/**
+ * Test class to test AMQQueueMBean attribtues and operations
+ */
+public class AMQQueueMBeanTest extends TestCase
+{
+ private static long MESSAGE_SIZE = 1000;
+ private AMQQueue _queue;
+ private AMQQueueMBean _queueMBean;
+ private MessageStore _messageStore;
+ private StoreContext _storeContext = new StoreContext();
+ private TransactionalContext _transactionalContext;
+ private VirtualHost _virtualHost;
+ private AMQProtocolSession _protocolSession;
+
+ public void testMessageCountTransient() throws Exception
+ {
+ int messageCount = 10;
+ sendMessages(messageCount, false);
+ assertTrue(_queueMBean.getMessageCount() == messageCount);
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+ long queueDepth = (messageCount * MESSAGE_SIZE) >> 10;
+ assertTrue(_queueMBean.getQueueDepth() == queueDepth);
+
+ _queueMBean.deleteMessageFromTop();
+ assertTrue(_queueMBean.getMessageCount() == (messageCount - 1));
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ _queueMBean.clearQueue();
+ assertTrue(_queueMBean.getMessageCount() == 0);
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ //Ensure that the data has been removed from the Store
+ verifyBrokerState();
+ }
+
+ public void testMessageCountPersistent() throws Exception
+ {
+ int messageCount = 10;
+ sendMessages(messageCount, true);
+ assertEquals("", messageCount, _queueMBean.getMessageCount().intValue());
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+ long queueDepth = (messageCount * MESSAGE_SIZE) >> 10;
+ assertTrue(_queueMBean.getQueueDepth() == queueDepth);
+
+ _queueMBean.deleteMessageFromTop();
+ assertTrue(_queueMBean.getMessageCount() == (messageCount - 1));
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ _queueMBean.clearQueue();
+ assertTrue(_queueMBean.getMessageCount() == 0);
+ assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
+
+ //Ensure that the data has been removed from the Store
+ verifyBrokerState();
+ }
+
+ // todo: collect to a general testing class -duplicated from Systest/MessageReturntest
+ private void verifyBrokerState()
+ {
+
+ TestableMemoryMessageStore store = new TestableMemoryMessageStore((MemoryMessageStore) _virtualHost.getMessageStore());
+
+ // Unlike MessageReturnTest there is no need for a delay as there this thread does the clean up.
+ assertNotNull("ContentBodyMap should not be null", store.getContentBodyMap());
+ assertEquals("Expected the store to have no content:" + store.getContentBodyMap(), 0, store.getContentBodyMap().size());
+ assertNotNull("MessageMetaDataMap should not be null", store.getMessageMetaDataMap());
+ assertEquals("Expected the store to have no metadata:" + store.getMessageMetaDataMap(), 0, store.getMessageMetaDataMap().size());
+ }
+
+ public void testConsumerCount() throws AMQException
+ {
+ SubscriptionManager mgr = _queue.getSubscribers();
+ assertFalse(mgr.hasActiveSubscribers());
+ assertTrue(_queueMBean.getActiveConsumerCount() == 0);
+
+
+ TestMinaProtocolSession protocolSession = new TestMinaProtocolSession();
+ AMQChannel channel = new AMQChannel(protocolSession, 1, _messageStore, null);
+ protocolSession.addChannel(channel);
+
+ _queue.registerProtocolSession(protocolSession, 1, new AMQShortString("test"), false, null, false, false);
+ assertTrue(_queueMBean.getActiveConsumerCount() == 1);
+
+ SubscriptionSet _subscribers = (SubscriptionSet) mgr;
+ SubscriptionFactory subscriptionFactory = new SubscriptionImpl.Factory();
+ Subscription s1 = subscriptionFactory.createSubscription(channel.getChannelId(),
+ protocolSession,
+ new AMQShortString("S1"),
+ false,
+ null,
+ true,
+ _queue);
+
+ Subscription s2 = subscriptionFactory.createSubscription(channel.getChannelId(),
+ protocolSession,
+ new AMQShortString("S2"),
+ false,
+ null,
+ true,
+ _queue);
+ _subscribers.addSubscriber(s1);
+ _subscribers.addSubscriber(s2);
+ assertTrue(_queueMBean.getActiveConsumerCount() == 3);
+ assertTrue(_queueMBean.getConsumerCount() == 3);
+
+ s1.close();
+ assertTrue(_queueMBean.getActiveConsumerCount() == 2);
+ assertTrue(_queueMBean.getConsumerCount() == 3);
+ }
+
+ public void testGeneralProperties()
+ {
+ long maxQueueDepth = 1000; // in bytes
+ _queueMBean.setMaximumMessageCount(50000l);
+ _queueMBean.setMaximumMessageSize(2000l);
+ _queueMBean.setMaximumQueueDepth(maxQueueDepth);
+
+ assertTrue(_queueMBean.getMaximumMessageCount() == 50000);
+ assertTrue(_queueMBean.getMaximumMessageSize() == 2000);
+ assertTrue(_queueMBean.getMaximumQueueDepth() == (maxQueueDepth >> 10));
+
+ assertTrue(_queueMBean.getName().equals("testQueue"));
+ assertTrue(_queueMBean.getOwner().equals("AMQueueMBeanTest"));
+ assertFalse(_queueMBean.isAutoDelete());
+ assertFalse(_queueMBean.isDurable());
+ }
+
+ public void testExceptions() throws Exception
+ {
+ try
+ {
+ _queueMBean.viewMessages(0, 3);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+
+ try
+ {
+ _queueMBean.viewMessages(2, 1);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+
+ try
+ {
+ _queueMBean.viewMessages(-1, 1);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+
+ AMQMessage msg = message(false, false);
+ long id = msg.getMessageId();
+ _queue.clearQueue(_storeContext);
+
+ msg.enqueue(_queue);
+ msg.routingComplete(_messageStore, _storeContext, new MessageHandleFactory());
+ _queue.process(_storeContext, msg, false);
+ _queueMBean.viewMessageContent(id);
+ try
+ {
+ _queueMBean.viewMessageContent(id + 1);
+ fail();
+ }
+ catch (JMException ex)
+ {
+
+ }
+ }
+
+ private AMQMessage message(final boolean immediate, boolean persistent) throws AMQException
+ {
+ MessagePublishInfo publish = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean isImmediate()
+ {
+ return immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ ContentHeaderBody contentHeaderBody = new ContentHeaderBody();
+ contentHeaderBody.bodySize = MESSAGE_SIZE; // in bytes
+ contentHeaderBody.properties = new BasicContentHeaderProperties();
+ ((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) (persistent ? 2 : 1));
+ return new AMQMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, contentHeaderBody);
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
+ _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ _messageStore = _virtualHost.getMessageStore();
+
+ _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext,
+ null,
+ new LinkedList<RequiredDeliveryException>(),
+ new HashSet<Long>());
+
+ _queue = new AMQQueue(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost);
+ _queueMBean = new AMQQueueMBean(_queue);
+
+ _protocolSession = new TestMinaProtocolSession();
+ }
+
+ private void sendMessages(int messageCount, boolean persistent) throws AMQException
+ {
+ for (int i = 0; i < messageCount; i++)
+ {
+ AMQMessage currentMessage = message(false, persistent);
+ currentMessage.enqueue(_queue);
+
+ // route header
+ currentMessage.routingComplete(_messageStore, _storeContext, new MessageHandleFactory());
+
+ // Add the body so we have somthing to test later
+ currentMessage.addContentBodyFrame(_storeContext,
+ _protocolSession.getRegistry()
+ .getProtocolVersionMethodConverter()
+ .convertToContentChunk(
+ new ContentBody(ByteBuffer.allocate((int) MESSAGE_SIZE),
+ MESSAGE_SIZE)));
+
+
+ }
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/Final/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java
new file mode 100644
index 0000000000..48d808142c
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.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.server.store;
+
+import org.apache.qpid.server.queue.MessageMetaData;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.List;
+
+/**
+ * Adds some extra methods to the memory message store for testing purposes.
+ */
+public class TestableMemoryMessageStore extends MemoryMessageStore
+{
+
+ MemoryMessageStore _mms = null;
+
+ public TestableMemoryMessageStore(MemoryMessageStore mms)
+ {
+ _mms = mms;
+ }
+
+ public TestableMemoryMessageStore()
+ {
+ _metaDataMap = new ConcurrentHashMap<Long, MessageMetaData>();
+ _contentBodyMap = new ConcurrentHashMap<Long, List<ContentChunk>>();
+ }
+
+ public ConcurrentMap<Long, MessageMetaData> getMessageMetaDataMap()
+ {
+ if (_mms != null)
+ {
+ return _mms._metaDataMap;
+ }
+ else
+ {
+ return _metaDataMap;
+ }
+ }
+
+ public ConcurrentMap<Long, List<ContentChunk>> getContentBodyMap()
+ {
+ if (_mms != null)
+ {
+ return _mms._contentBodyMap;
+ }
+ else
+ {
+ return _contentBodyMap;
+ }
+ }
+}
diff --git a/Final/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java b/Final/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java
new file mode 100644
index 0000000000..c7db51016e
--- /dev/null
+++ b/Final/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java
@@ -0,0 +1,88 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class LoggingProxyTest extends TestCase
+{
+ static interface IFoo {
+ void foo();
+ void foo(int i, Collection c);
+ String bar();
+ String bar(String s, List l);
+ }
+
+ static class Foo implements IFoo {
+ public void foo()
+ {
+ }
+
+ public void foo(int i, Collection c)
+ {
+ }
+
+ public String bar()
+ {
+ return null;
+ }
+
+ public String bar(String s, List l)
+ {
+ return "ha";
+ }
+ }
+
+ public void testSimple() {
+ LoggingProxy proxy = new LoggingProxy(new Foo(), 20);
+ IFoo foo = (IFoo)proxy.getProxy(IFoo.class);
+ foo.foo();
+ assertEquals(2, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(0).toString().matches(".*: foo\\(\\) entered$"));
+ assertTrue(proxy.getBuffer().get(1).toString().matches(".*: foo\\(\\) returned$"));
+
+ foo.foo(3, Arrays.asList(0, 1, 2));
+ assertEquals(4, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(2).toString().matches(".*: foo\\(\\[3, \\[0, 1, 2\\]\\]\\) entered$"));
+ assertTrue(proxy.getBuffer().get(3).toString().matches(".*: foo\\(\\) returned$"));
+
+ foo.bar();
+ assertEquals(6, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(4).toString().matches(".*: bar\\(\\) entered$"));
+ assertTrue(proxy.getBuffer().get(5).toString().matches(".*: bar\\(\\) returned null$"));
+
+ foo.bar("hello", Arrays.asList(1, 2, 3));
+ assertEquals(8, proxy.getBufferSize());
+ assertTrue(proxy.getBuffer().get(6).toString().matches(".*: bar\\(\\[hello, \\[1, 2, 3\\]\\]\\) entered$"));
+ assertTrue(proxy.getBuffer().get(7).toString().matches(".*: bar\\(\\) returned ha$"));
+
+ proxy.dump();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(LoggingProxyTest.class);
+ }
+}
diff --git a/Final/java/client-java14/README.txt b/Final/java/client-java14/README.txt
new file mode 100644
index 0000000000..66621c7eb2
--- /dev/null
+++ b/Final/java/client-java14/README.txt
@@ -0,0 +1,33 @@
+An implementation of SASL is provided here, for the PLAIN and CRAM-MD5 mechanisms as well as maven assembly
+instructions for producing a backported Java client for Java 1.4. In order to use the custom SASL implementation
+on JRE 1.4 the following steps must be taken:
+
+ * Install the SASL JSR-28 API jar.
+ * Install the qpid-client-java14 jar or set it up as a dynamically registered SASL provider.
+ * Set up java.security to add the SASL provider to the list of security providers if hte SASL implemenation jar was installed as an extension.
+
+Installing the SASL JSR-28 API jar.
+
+ Download, http://www.worldspot.com/jsr28/v1.1/download/sasl.jar, and copy it to the JAVA_HOME\lib\ext directory.
+
+Install or set up the qpid-client-java14 jar.
+
+ Copy the output jar for this project, qpid-client-java14-1.0-incubating-M2-SNAPSHOT.jar, to JAVA_HOME\lib\ext.
+
+ OR
+
+ Create a properties file and register the SASL implementations in the jar so that Qpids dynamic SASL registry can find them. In a properties file
+ add the lines:
+
+ PLAIN=org.apache.qpid.sasl.ClientFactoryImpl
+ CRAM-MD5=org.apache.qpid.sasl.ClientFactoryImpl
+
+ Place this somewhere on the classpath and put the qpid-client-java14-1.0-incubating-M2-SNAPSHOT.jar on the classpath too. When starting your application
+ pass in the system property amq.dynamicsaslregistrar.properties and set it to point to the location of the properties file.
+
+Set up the SASL provider.
+
+ You only need to do this if the custom SASL jar, qpid-client-java14-1.0-incubating-M2-SNAPSHOT.jar, was copied to JAVA_HOME\lib\ext.
+ Add the following line to JAVA_HOME\lib\security\java.security file (where n is the providers preference order):
+
+ security.provider.n=org.apache.qpid.sasl.Provider \ No newline at end of file
diff --git a/Final/java/client-java14/etc/sasl.properties b/Final/java/client-java14/etc/sasl.properties
new file mode 100644
index 0000000000..04519e2a30
--- /dev/null
+++ b/Final/java/client-java14/etc/sasl.properties
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+PLAIN=org.apache.qpid.sasl.ClientFactoryImpl
+CRAM-MD5=org.apache.qpid.sasl.ClientFactoryImpl
diff --git a/Final/java/client-java14/pom.xml b/Final/java/client-java14/pom.xml
new file mode 100644
index 0000000000..a2da7800e6
--- /dev/null
+++ b/Final/java/client-java14/pom.xml
@@ -0,0 +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</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</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/Final/java/client-java14/src/main/assembly/client-java14-bin.xml b/Final/java/client-java14/src/main/assembly/client-java14-bin.xml
new file mode 100644
index 0000000000..91e1f23975
--- /dev/null
+++ b/Final/java/client-java14/src/main/assembly/client-java14-bin.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<!-- Assembly instructions for a client that runs on Java 1.4 -->
+<assembly>
+ <id>java-client-java14-bin</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <!-- Apache license files -->
+ <directory>../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+
+ <excludes>
+ <!-- Exclude the Java 5 built client and common. The java 1.4 retrotranslated versions are used instead. -->
+ <exclude>org.apache.qpid:qpid-client:jar</exclude>
+ <exclude>org.apache.qpid:qpid-common:jar</exclude>
+
+ <!-- Exclude the retrotranslated integration tests from the distriubtion. -->
+ <exclude>org.apache.qpid:qpid-integrationtests:jar:java14</exclude>
+
+ <!-- Mina SSL support only available in Java 5. No SSL on 1.4. -->
+ <exclude>org.apache.mina:mina-java5</exclude>
+ <exclude>org.apache.mina:mina-filter-ssl</exclude>
+
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
+
+
diff --git a/Final/java/client-java14/src/main/assembly/jar-with-dependencies.xml b/Final/java/client-java14/src/main/assembly/jar-with-dependencies.xml
new file mode 100644
index 0000000000..091fd46427
--- /dev/null
+++ b/Final/java/client-java14/src/main/assembly/jar-with-dependencies.xml
@@ -0,0 +1,104 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!-- This is an assembly descriptor that produces a jar file that contains all the
+ test dependencies, fully expanded into a single jar, required to run the tests
+ of a maven project.
+-->
+<!--
+<assembly>
+ <id>all-test-deps</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <formats>
+ <format>jar</format>
+ </formats>
+
+ <dependencySets>
+ <!## Include all test dependencies. ##>
+ <dependencySet>
+ <outputDirectory></outputDirectory>
+ <outputFileNameMapping></outputFileNameMapping>
+ <unpack>true</unpack>
+ <!##<scope>runtime</scope>##>
+
+ <!##
+ <includes>
+ <include>org.apache.qpid:qpid-client:jar:java14</include>
+ <include>org.apache.qpid:qpid-common:jar:java14</include>
+ </includes>
+ ##>
+
+ <excludes>
+ <!## Exclude the Java 5 built client and common. The java 1.4 retrotranslated versions are used instead. ##>
+ <exclude>org.apache.qpid:qpid-client:jar</exclude>
+ <exclude>org.apache.qpid:qpid-common:jar</exclude>
+
+ <!## Mina SSL support only available in Java 5. No SSL on 1.4. ##>
+ <exclude>org.apache.mina:mina-java5</exclude>
+ <exclude>org.apache.mina:mina-filter-ssl</exclude>
+ </excludes>
+
+ </dependencySet>
+
+ </dependencySets>
+
+ <fileSets>
+ <!## Include all project classes. ##>
+ <fileSet>
+ <directory>target/classes</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+
+ <!## Include all project test classes. ##>
+ <fileSet>
+ <directory>target/test-classes</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
+-->
+
+<assembly>
+ <id>all-test-deps</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>jar</format>
+ </formats>
+
+ <fileSets>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory></outputDirectory>
+ <unpack>true</unpack>
+
+ <excludes>
+ <!-- Exclude the Java 5 built client and common. The java 1.4 retrotranslated versions are used instead. -->
+ <exclude>org.apache.qpid:qpid-client:jar</exclude>
+ <exclude>org.apache.qpid:qpid-common:jar</exclude>
+
+ <!-- Mina SSL support only available in Java 5. No SSL on 1.4. -->
+ <exclude>org.apache.mina:mina-java5</exclude>
+ <exclude>org.apache.mina:mina-filter-ssl</exclude>
+
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly> \ No newline at end of file
diff --git a/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java
new file mode 100644
index 0000000000..691020307a
--- /dev/null
+++ b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java
@@ -0,0 +1,343 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.sasl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.security.auth.callback.*;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.util.PrettyPrintingUtils;
+
+/**
+ * Implements a factory for generating Sasl client implementations.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a list of supported encryption mechansims that meet a defined set of Sasl properties.
+ * <tr><td> Provide the best matching supported Sasl mechanism to a preference ordered list of mechanisms and Sasl
+ * properties.
+ * <tr><td> Perform username and password request call backs. <td> CallBackHandler
+ * </table>
+ */
+public class ClientFactoryImpl implements SaslClientFactory
+{
+ //private static final Logger log = Logger.getLogger(ClientFactoryImpl.class);
+
+ /** Holds the names of the supported encryption mechanisms. */
+ private static final String[] SUPPORTED_MECHANISMS = { "CRAM-MD5", "PLAIN" };
+
+ /** Defines index of the CRAM-MD5 mechanism within the supported mechanisms. */
+ private static final int CRAM_MD5 = 0;
+
+ /** Defines index of the PLAIN mechanism within the supported mechanisms. */
+ private static final int PLAIN = 1;
+
+ /** Bit mapping of the no plain text policy. */
+ private static final int NOPLAINTEXT = 0x0001;
+
+ /** Bit mapping of the no susceptible active attacks policy. */
+ private static final int NOACTIVE = 0x0002;
+
+ /** Bit mapping of the no susceptible to dictionary attacks policy. */
+ private static final int NODICTIONARY = 0x0004;
+
+ /** Bit mapping of the must use forward secrecy between sessions policy. */
+ private static final int FORWARD_SECRECY = 0x0008;
+
+ /** Bit mapping of the no anonymous logins policy. */
+ private static final int NOANONYMOUS = 0x0010;
+
+ /** Bit mapping of the must pass credentials policy. */
+ private static final int PASS_CREDENTIALS = 0x0020;
+
+ /** Defines a mapping from supported mechanisms to supported policy flags. */
+ private static final int[] SUPPPORTED_MECHANISMS_POLICIES =
+ {
+ NOPLAINTEXT | NOANONYMOUS, // CRAM-MD5
+ NOANONYMOUS // PLAIN
+ };
+
+ /**
+ * Creates a SaslClient using the parameters supplied.
+ *
+ * @param mechanisms The non-null list of mechanism names to try. Each is the IANA-registered name of a SASL
+ * mechanism. (e.g. "GSSAPI", "CRAM-MD5").
+ * @param authorizationId The possibly null protocol-dependent identification to be used for authorization.
+ * If null or empty, the server derives an authorization ID from the client's authentication
+ * credentials. When the SASL authentication completes successfully, the specified entity is
+ * granted access.
+ * @param protocol The non-null string name of the protocol for which the authentication is being performed
+ * (e.g., "ldap").
+ * @param serverName The non-null fully qualified host name of the server to authenticate to.
+ * @param props The possibly null set of properties used to select the SASL mechanism and to configure the
+ * authentication exchange of the selected mechanism. See the <tt>Sasl</tt> class for a list
+ * of standard properties. Other, possibly mechanism-specific, properties can be included.
+ * Properties not relevant to the selected mechanism are ignored.
+ * @param cbh The possibly null callback handler to used by the SASL mechanisms to get further
+ * information from the application/library to complete the authentication. For example, a
+ * SASL mechanism might require the authentication ID, password and realm from the caller.
+ * The authentication ID is requested by using a <tt>NameCallback</tt>.
+ * The password is requested by using a <tt>PasswordCallback</tt>.
+ * The realm is requested by using a <tt>RealmChoiceCallback</tt> if there is a list
+ * of realms to choose from, and by using a <tt>RealmCallback</tt> if
+ * the realm must be entered.
+ *
+ * @return A possibly null <tt>SaslClient</tt> created using the parameters supplied. If null, this factory cannot
+ * produce a <tt>SaslClient</tt> using the parameters supplied.
+ *
+ * @throws javax.security.sasl.SaslException If cannot create a <tt>SaslClient</tt> because of an error.
+ */
+ public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName,
+ Map props, CallbackHandler cbh) throws SaslException
+ {
+ /*log.debug("public SaslClient createSaslClient(String[] mechanisms = " + PrettyPrintingUtils.printArray(mechanisms)
+ + ", String authorizationId = " + authorizationId + ", String protocol = " + protocol
+ + ", String serverName = " + serverName + ", Map props = " + props + ", CallbackHandler cbh): called");*/
+
+ // Get a list of all supported mechanisms that matched the required properties.
+ String[] matchingMechanisms = getMechanismNames(props);
+ //log.debug("matchingMechanisms = " + PrettyPrintingUtils.printArray(matchingMechanisms));
+
+ // Scan down the list of mechanisms until the first one that matches one of the matching supported mechanisms
+ // is found.
+ String chosenMechanism = null;
+
+ for (int i = 0; i < mechanisms.length; i++)
+ {
+ String mechanism = mechanisms[i];
+
+ for (int j = 0; j < matchingMechanisms.length; j++)
+ {
+ String matchingMechanism = matchingMechanisms[j];
+
+ if (mechanism.equals(matchingMechanism))
+ {
+ chosenMechanism = mechanism;
+
+ break;
+ }
+ }
+
+ // Stop scanning if a match has been found.
+ if (chosenMechanism != null)
+ {
+ break;
+ }
+ }
+
+ // Check that a matching mechanism was found or return null otherwise.
+ if (chosenMechanism == null)
+ {
+ //log.debug("No matching mechanism could be found.");
+
+ return null;
+ }
+
+ // Instantiate an appropriate client type for the chosen mechanism.
+ if (chosenMechanism.equals(SUPPORTED_MECHANISMS[CRAM_MD5]))
+ {
+ Object[] uinfo = getUserInfo("CRAM-MD5", authorizationId, cbh);
+
+ //log.debug("Using CRAM-MD5 mechanism.");
+
+ return new CramMD5Client((String) uinfo[0], (byte[]) uinfo[1]);
+ }
+ else
+ {
+ Object[] uinfo = getUserInfo("PLAIN", authorizationId, cbh);
+
+ //log.debug("Using PLAIN mechanism.");
+
+ return new PlainClient(authorizationId, (String) uinfo[0], (byte[]) uinfo[1]);
+ }
+ }
+
+ /**
+ * Returns an array of names of mechanisms that match the specified
+ * mechanism selection policies.
+ *
+ * @param props The possibly null set of properties used to specify the
+ * security policy of the SASL mechanisms. For example, if <tt>props</tt>
+ * contains the <tt>Sasl.POLICY_NOPLAINTEXT</tt> property with the value
+ * <tt>"true"</tt>, then the factory must not return any SASL mechanisms
+ * that are susceptible to simple plain passive attacks.
+ * See the <tt>Sasl</tt> class for a complete list of policy properties.
+ * Non-policy related properties, if present in <tt>props</tt>, are ignored.
+ *
+ * @return A non-null array containing a IANA-registered SASL mechanism names.
+ */
+ public String[] getMechanismNames(Map props)
+ {
+ //log.debug("public String[] getMechanismNames(Map props = " + props + "): called");
+
+ // Used to build up the valid mechanisms in.
+ List validMechanisms = new ArrayList();
+
+ // Transform the Sasl properties into a set of bit mapped flags indicating the required properties of the
+ // encryption mechanism employed.
+ int requiredFlags = bitMapSaslProperties(props);
+ //log.debug("requiredFlags = " + requiredFlags);
+
+ // Scan down the list of supported mechanisms filtering in only those that satisfy all of the desired
+ // encryption properties.
+ for (int i = 0; i < SUPPORTED_MECHANISMS.length; i++)
+ {
+ int mechanismFlags = SUPPPORTED_MECHANISMS_POLICIES[i];
+ //log.debug("mechanismFlags = " + mechanismFlags);
+
+ // Check if the current mechanism contains all of the required flags.
+ if ((requiredFlags & ~mechanismFlags) == 0)
+ {
+ //log.debug("Mechanism " + SUPPORTED_MECHANISMS[i] + " meets the required properties.");
+ validMechanisms.add(SUPPORTED_MECHANISMS[i]);
+ }
+ }
+
+ String[] result = (String[]) validMechanisms.toArray(new String[validMechanisms.size()]);
+
+ //log.debug("result = " + PrettyPrintingUtils.printArray(result));
+
+ return result;
+ }
+
+ /**
+ * Transforms a set of Sasl properties, defined using the property names in javax.security.sasl.Sasl, into
+ * a bit mapped set of property flags encoded using the bit mapping constants defined in this class.
+ *
+ * @param properties The Sasl properties to bit map.
+ *
+ * @return A set of bit mapped properties encoded in an integer.
+ */
+ private int bitMapSaslProperties(Map properties)
+ {
+ //log.debug("private int bitMapSaslProperties(Map properties = " + properties + "): called");
+
+ int result = 0;
+
+ // No flags set if no properties are set.
+ if (properties == null)
+ {
+ return result;
+ }
+
+ if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOPLAINTEXT)))
+ {
+ result |= NOPLAINTEXT;
+ }
+
+ if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOACTIVE)))
+ {
+ result |= NOACTIVE;
+ }
+
+ if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NODICTIONARY)))
+ {
+ result |= NODICTIONARY;
+ }
+
+ if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOANONYMOUS)))
+ {
+ result |= NOANONYMOUS;
+ }
+
+ if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_FORWARD_SECRECY)))
+ {
+ result |= FORWARD_SECRECY;
+ }
+
+ if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_PASS_CREDENTIALS)))
+ {
+ result |= PASS_CREDENTIALS;
+ }
+
+ return result;
+ }
+
+ /**
+ * Uses the specified call back handler to query for the users log in name and password.
+ *
+ * @param prefix A prefix to prepend onto the username and password queries.
+ * @param authorizationId The default autorhization name.
+ * @param cbh The call back handler.
+ *
+ * @return The username and password from the callback.
+ *
+ * @throws SaslException If the callback fails for any reason.
+ */
+ private Object[] getUserInfo(String prefix, String authorizationId, CallbackHandler cbh) throws SaslException
+ {
+ // Check that the callback handler is defined.
+ if (cbh == null)
+ {
+ throw new SaslException("Callback handler to get username/password required.");
+ }
+
+ try
+ {
+ String userPrompt = prefix + " authentication id: ";
+ String passwdPrompt = prefix + " password: ";
+
+ NameCallback ncb =
+ (authorizationId == null) ? new NameCallback(userPrompt) : new NameCallback(userPrompt, authorizationId);
+ PasswordCallback pcb = new PasswordCallback(passwdPrompt, false);
+
+ // Ask the call back handler to get the users name and password.
+ cbh.handle(new Callback[] { ncb, pcb });
+
+ char[] pw = pcb.getPassword();
+
+ byte[] bytepw;
+ String authId;
+
+ if (pw != null)
+ {
+ bytepw = new String(pw).getBytes("UTF8");
+ pcb.clearPassword();
+ }
+ else
+ {
+ bytepw = null;
+ }
+
+ authId = ncb.getName();
+
+ return new Object[] { authId, bytepw };
+ }
+ catch (IOException e)
+ {
+ throw new SaslException("Cannot get password.", e);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException("Cannot get userid/password.", e);
+ }
+ }
+}
diff --git a/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java
new file mode 100644
index 0000000000..7d15f03329
--- /dev/null
+++ b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java
@@ -0,0 +1,347 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.sasl;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+/**
+ * Implements the CRAM-MD5 SASL mechanism.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Concatenate the user id and password hashed by a challenge string as the challenge response.
+ * <tr><td> Ensure password is wiped once a challenge has been processed.
+ * </table>
+ */
+public class CramMD5Client implements SaslClient
+{
+ /** Defines the HMAC block size. */
+ private static final int MD5_BLOCKSIZE = 64;
+
+ /** Flag used to indicate that the authentication has completed. */
+ private boolean completed = false;
+
+ private String authenticationId;
+ private byte[] password;
+
+ /**
+ * Creates a PLAIN SASL client for an authorization id, authentication id and password.
+ *
+ * @param authenticationId The authentication id.
+ * @param password The password.
+ *
+ * @throws SaslException If the authentication id or password is null.
+ */
+ CramMD5Client(String authenticationId, byte[] password) throws SaslException
+ {
+ // Check that a username and password are specified.
+ if ((authenticationId == null) || (password == null))
+ {
+ throw new SaslException("CRAM: authentication id and password must be specified");
+ }
+
+ // Keep the log in credentials.
+ this.authenticationId = authenticationId;
+ this.password = password;
+ }
+
+ /**
+ * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI").
+ *
+ * @return A non-null string representing the IANA-registered mechanism name.
+ */
+ public String getMechanismName()
+ {
+ return "CRAM-MD5";
+ }
+
+ /**
+ * Determines whether this mechanism has an optional initial response. If true, caller should call
+ * <tt>evaluateChallenge()</tt> with an empty array to get the initial response.
+ *
+ * <p/>CRAM-MD5 has no intial response.
+ *
+ * @return true if this mechanism has an initial response.
+ */
+ public boolean hasInitialResponse()
+ {
+ return false;
+ }
+
+ /**
+ * Evaluates the challenge data and generates a response. If a challenge is received from the server during the
+ * authentication process, this method is called to prepare an appropriate next response to submit to the server.
+ *
+ * <p/>The initial response for the SASL command, for the CRAM-MD5 mechanism is the concatenation of authentication
+ * ID and password HMAC-MD5 hashed by the server challenge.
+ *
+ * @param challenge The non-null challenge sent from the server. The challenge array may have zero length.
+ *
+ * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS"
+ * status and the challenge only contains data for the client to update its state and no response
+ * needs to be sent to the server. The response is a zero-length byte array if the client is to send a
+ * response with no data.
+ *
+ * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a
+ * response.
+ */
+ public byte[] evaluateChallenge(byte[] challenge) throws SaslException
+ {
+ // Check that the authentication has not already been performed.
+ if (completed)
+ {
+ throw new IllegalStateException("CRAM-MD5 authentication already completed.");
+ }
+
+ // Check if the password is null, this will be the case if a previous attempt to authenticated failed.
+ if (password == null)
+ {
+ throw new IllegalStateException("CRAM-MD5 authentication previously aborted due to error.");
+ }
+
+ // Generate a keyed-MD5 digest from the user's password keyed by the challenge bytes.
+ try
+ {
+ String digest = hmac_md5(password, challenge);
+ String result = authenticationId + " " + digest;
+
+ completed = true;
+
+ return result.getBytes("UTF8");
+ }
+ catch (java.security.NoSuchAlgorithmException e)
+ {
+ throw new SaslException("MD5 algorithm not available on platform", e);
+ }
+ catch (java.io.UnsupportedEncodingException e)
+ {
+ throw new SaslException("UTF8 not available on platform", e);
+ }
+ finally
+ {
+ clearPassword();
+ }
+ }
+
+ /**
+ * Determines whether the authentication exchange has completed. This method may be called at any time, but
+ * typically, it will not be called until the caller has received indication from the server (in a protocol-specific
+ * manner) that the exchange has completed.
+ *
+ * @return true if the authentication exchange has completed; false otherwise.
+ */
+ public boolean isComplete()
+ {
+ return completed;
+ }
+
+ /**
+ * Unwraps a byte array received from the server. This method can be called only after the authentication exchange
+ * has completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has
+ * negotiated integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is
+ * thrown.
+ *
+ * <p/><tt>incoming</tt> is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet
+ * field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of <tt>incoming</tt>
+ * to use.
+ *
+ * @param incoming A non-null byte array containing the encoded bytes from the server.
+ * @param offset The starting position at <tt>incoming</tt> of the bytes to use.
+ * @param len The number of bytes from <tt>incoming</tt> to use.
+ *
+ * @return A non-null byte array containing the decoded bytes.
+ *
+ * @throws javax.security.sasl.SaslException If <tt>incoming</tt> cannot be successfully unwrapped.
+ * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
+ * protection has neither integrity nor privacy.
+ */
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("CRAM-MD5 does not support quality of protection.");
+ }
+
+ /**
+ * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has
+ * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated
+ * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown.
+ *
+ * <p/>The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the
+ * leading four octet field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of
+ * <tt>outgoing</tt> to use.
+ *
+ * @param outgoing A non-null byte array containing the bytes to encode.
+ * @param offset The starting position at <tt>outgoing</tt> of the bytes to use.
+ * @param len The number of bytes from <tt>outgoing</tt> to use.
+ *
+ * @return A non-null byte array containing the encoded bytes.
+ *
+ * @throws javax.security.sasl.SaslException If <tt>outgoing</tt> cannot be successfully wrapped.
+ * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
+ * protection has neither integrity nor privacy.
+ */
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("CRAM-MD5 does not support quality of protection.");
+ }
+
+ /**
+ * Retrieves the negotiated property. This method can be called only after the authentication exchange has
+ * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, an <tt>IllegalStateException</tt> is thrown.
+ *
+ * @param propName The non-null property name.
+ *
+ * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to
+ * this mechanism.
+ *
+ * @throws IllegalStateException If this authentication exchange has not completed.
+ */
+ public Object getNegotiatedProperty(String propName)
+ {
+ if (completed)
+ {
+ if (propName.equals(Sasl.QOP))
+ {
+ return "auth";
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("CRAM-MD5 authentication not completed");
+ }
+ }
+
+ /**
+ * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this
+ * method invalidates the SaslClient instance. This method is idempotent.
+ *
+ * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources.
+ */
+ public void dispose() throws SaslException
+ { }
+
+ /*
+ * Hashes its input arguments according to HMAC-MD5 (RFC 2104) and returns the resulting digest in its ASCII
+ * representation.
+ *
+ * <p/> The HMAC-MD5 function is described as follows:
+ * <p/><pre>
+ * MD5(key XOR opad, MD5(key XOR ipad, text))
+ * </pre>
+ *
+ * <p/>Where key is an n byte key, ipad is the byte 0x36 repeated 64 times, opad is the byte 0x5c repeated 64 times
+ * and text is the data to be protected.
+ *
+ * @param key The key to hash by.
+ * @param text The plain text to hash.
+ *
+ * @return The hashed text.
+ *
+ * @throws NoSuchAlgorithmException If the Java platform does not supply an MD5 implementation.
+ */
+ private static final String hmac_md5(byte[] key, byte[] text) throws NoSuchAlgorithmException
+ {
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+
+ /* digest the key if longer than 64 bytes */
+ if (key.length > 64)
+ {
+ key = md5.digest(key);
+ }
+
+ byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */
+ byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */
+ byte[] digest;
+ int i;
+
+ /* store key in pads */
+ for (i = 0; i < MD5_BLOCKSIZE; i++)
+ {
+ for (; i < key.length; i++)
+ {
+ ipad[i] = key[i];
+ opad[i] = key[i];
+ }
+
+ ipad[i] = 0x00;
+ opad[i] = 0x00;
+ }
+
+ /* XOR key with pads */
+ for (i = 0; i < MD5_BLOCKSIZE; i++)
+ {
+ ipad[i] ^= 0x36;
+ opad[i] ^= 0x5c;
+ }
+
+ /* inner MD5 */
+ md5.update(ipad);
+ md5.update(text);
+ digest = md5.digest();
+
+ /* outer MD5 */
+ md5.update(opad);
+ md5.update(digest);
+ digest = md5.digest();
+
+ // Get character representation of digest
+ StringBuffer digestString = new StringBuffer();
+
+ for (i = 0; i < digest.length; i++)
+ {
+ if ((digest[i] & 0x000000ff) < 0x10)
+ {
+ digestString.append("0" + Integer.toHexString(digest[i] & 0x000000ff));
+ }
+ else
+ {
+ digestString.append(Integer.toHexString(digest[i] & 0x000000ff));
+ }
+ }
+
+ return (digestString.toString());
+ }
+
+ /**
+ * Overwrites the password with zeros.
+ */
+ private void clearPassword()
+ {
+ if (password != null)
+ {
+ // Zero out password.
+ for (int i = 0; i < password.length; i++)
+ {
+ password[i] = (byte) 0;
+ }
+
+ password = null;
+ }
+ }
+}
diff --git a/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java
new file mode 100644
index 0000000000..97e607c57e
--- /dev/null
+++ b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java
@@ -0,0 +1,275 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.sasl;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+/**
+ * Implements the PLAIN SASL mechanism.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Concatenate the user id and password in plain text as the challenge respose.
+ * <tr><td> Ensure password is wiped once a challenge has been processed.
+ * </table>
+ */
+public class PlainClient implements SaslClient
+{
+ /** Flag used to indicate that the authentication has completed. */
+ private boolean completed = false;
+
+ private String authorizationId;
+ private String authenticationId;
+ private byte[] password;
+
+ private static byte SEPERATOR = 0; // US-ASCII <NUL>
+
+ /**
+ * Creates a PLAIN SASL client for an authorization id, authentication id and password.
+ *
+ * @param authorizationId The authorization id. May be null.
+ * @param authenticationId The authentication id.
+ * @param password The password.
+ *
+ * @throws SaslException If the authentication id or password is null.
+ */
+ PlainClient(String authorizationId, String authenticationId, byte[] password) throws SaslException
+ {
+ // Check that a username and password are specified.
+ if ((authenticationId == null) || (password == null))
+ {
+ throw new SaslException("PLAIN: authentication ID and password must be specified");
+ }
+
+ // Keep the log in credentials.
+ this.authorizationId = authorizationId;
+ this.authenticationId = authenticationId;
+ this.password = password;
+ }
+
+ /**
+ * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI").
+ *
+ * @return A non-null string representing the IANA-registered mechanism name.
+ */
+ public String getMechanismName()
+ {
+ return "PLAIN";
+ }
+
+ /**
+ * Determines whether this mechanism has an optional initial response. If true, caller should call
+ * <tt>evaluateChallenge()</tt> with an empty array to get the initial response.
+ *
+ * @return true if this mechanism has an initial response.
+ */
+ public boolean hasInitialResponse()
+ {
+ return true;
+ }
+
+ /**
+ * Evaluates the challenge data and generates a response. If a challenge is received from the server during the
+ * authentication process, this method is called to prepare an appropriate next response to submit to the server.
+ *
+ * <p/>The initial response for the SASL command, for the PLAIN mechanism is the concatenation of authorization ID,
+ * authentication ID and password, with each component separated by the US-ASCII <NUL> byte.
+ *
+ * @param challenge The non-null challenge sent from the server. The challenge array may have zero length.
+ *
+ * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS"
+ * status and the challenge only contains data for the client to update its state and no response
+ * needs to be sent to the server. The response is a zero-length byte array if the client is to send a
+ * response with no data.
+ *
+ * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a
+ * response.
+ */
+ public byte[] evaluateChallenge(byte[] challenge) throws SaslException
+ {
+ // Check that the authentication has not already been performed.
+ if (completed)
+ {
+ throw new IllegalStateException("PLAIN authentication already completed");
+ }
+
+ try
+ {
+ // Get the authorization and authentication ids in bytes.
+ byte[] authorizationBytes = (authorizationId != null) ? authorizationId.getBytes("UTF8") : null;
+ byte[] authenticationBytes = authenticationId.getBytes("UTF8");
+
+ // Create an array big enough to hold the results.
+ byte[] result =
+ new byte[password.length + authenticationBytes.length + 2
+ + ((authorizationBytes == null) ? 0 : authorizationBytes.length)];
+
+ // Copy the authorization id, authentication id and password into the results.
+ int pos = 0;
+ if (authorizationBytes != null)
+ {
+ System.arraycopy(authorizationBytes, 0, result, 0, authorizationBytes.length);
+ pos = authorizationBytes.length;
+ }
+
+ result[pos++] = SEPERATOR;
+ System.arraycopy(authenticationBytes, 0, result, pos, authenticationBytes.length);
+
+ pos += authenticationBytes.length;
+ result[pos++] = SEPERATOR;
+
+ System.arraycopy(password, 0, result, pos, password.length);
+
+ completed = true;
+
+ return result;
+ }
+ catch (java.io.UnsupportedEncodingException e)
+ {
+ throw new SaslException("Cannot get UTF-8 encoding of ids", e);
+ }
+ finally
+ {
+ clearPassword();
+ }
+ }
+
+ /**
+ * Determines whether the authentication exchange has completed. This method may be called at any time, but
+ * typically, it will not be called until the caller has received indication from the server (in a protocol-specific
+ * manner) that the exchange has completed.
+ *
+ * @return true if the authentication exchange has completed; false otherwise.
+ */
+ public boolean isComplete()
+ {
+ return completed;
+ }
+
+ /**
+ * Unwraps a byte array received from the server. This method can be called only after the authentication exchange has
+ * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated
+ * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown.
+ *
+ * <p/><tt>incoming</tt> is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet
+ * field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of <tt>incoming</tt>
+ * to use.
+ *
+ * @param incoming A non-null byte array containing the encoded bytes
+ * from the server.
+ * @param offset The starting position at <tt>incoming</tt> of the bytes to use.
+ * @param len The number of bytes from <tt>incoming</tt> to use.
+ *
+ * @return A non-null byte array containing the decoded bytes.
+ *
+ * @throws javax.security.sasl.SaslException If <tt>incoming</tt> cannot be successfully unwrapped.
+ * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
+ * protection has neither integrity nor privacy.
+ */
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("PLAIN does not support quality of protection.");
+ }
+
+ /**
+ * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has
+ * completed (i.e., when <tt>isComplete()</tt> returns true) and only if the authentication exchange has negotiated
+ * integrity and/or privacy as the quality of protection; otherwise, an <tt>IllegalStateException</tt> is thrown.
+ *
+ * <p/>The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the
+ * leading four octet field that represents the length. <tt>offset</tt> and <tt>len</tt> specify the portion of
+ * <tt>outgoing</tt> to use.
+ *
+ * @param outgoing A non-null byte array containing the bytes to encode.
+ * @param offset The starting position at <tt>outgoing</tt> of the bytes to use.
+ * @param len The number of bytes from <tt>outgoing</tt> to use.
+ *
+ * @return A non-null byte array containing the encoded bytes.
+ *
+ * @throws javax.security.sasl.SaslException If <tt>outgoing</tt> cannot be successfully wrapped.
+ * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of
+ * protection has neither integrity nor privacy.
+ */
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("PLAIN does not support quality of protection.");
+ }
+
+ /**
+ * Retrieves the negotiated property. This method can be called only after the authentication exchange has
+ * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, an <tt>IllegalStateException</tt> is thrown.
+ *
+ * @param propName The non-null property name.
+ *
+ * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to
+ * this mechanism.
+ *
+ * @throws IllegalStateException If this authentication exchange has not completed.
+ */
+ public Object getNegotiatedProperty(String propName)
+ {
+ if (completed)
+ {
+ if (propName.equals(Sasl.QOP))
+ {
+ return "auth";
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("PLAIN authentication not completed");
+ }
+ }
+
+ /**
+ * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this
+ * method invalidates the SaslClient instance. This method is idempotent.
+ *
+ * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources.
+ */
+ public void dispose() throws SaslException
+ {
+ clearPassword();
+ }
+
+ /**
+ * Overwrites the password with zeros.
+ */
+ private void clearPassword()
+ {
+ if (password != null)
+ {
+ // Zero out password.
+ for (int i = 0; i < password.length; i++)
+ {
+ password[i] = (byte) 0;
+ }
+
+ password = null;
+ }
+ }
+}
diff --git a/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java
new file mode 100644
index 0000000000..f9a5c42c3d
--- /dev/null
+++ b/Final/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.sasl;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A SASL provider for Java 1.4. Declares the capabilities of this implementation, which supports PLAIN and CRAM-MD5
+ * client implementations only.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Declare PLAIN SASL support.
+ * <tr><td> Declare CRAM-MD5 SASL support.
+ * </table>
+ */
+public class Provider extends java.security.Provider
+{
+ //Logger log = Logger.getLogger(Provider.class);
+
+ private static final String info = "Qpid SASL provider" + "(implements client mechanisms for: PLAIN, CRAM-MD5)";
+
+ public Provider()
+ {
+ super("QpidSASL", 1.4, info);
+
+ //log.debug("public Provider(): called");
+
+ AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ put("SaslClientFactory.PLAIN", "org.apache.qpid.sasl.ClientFactoryImpl");
+ put("SaslClientFactory.CRAM-MD5", "org.apache.qpid.sasl.ClientFactoryImpl");
+
+ return null;
+ }
+ });
+ }
+}
diff --git a/Final/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java b/Final/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java
new file mode 100644
index 0000000000..468beda5b5
--- /dev/null
+++ b/Final/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.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.test.integration.client;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import org.apache.qpid.client.AMQConnection;
+
+/**
+ * Implements a trivial broker connection test, to a broker on the default port on localhost, to check that SASL
+ * authentication works.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that a connection to a broker can be established.
+ * </table>
+ */
+public class ConnectionTest extends TestCase
+{
+ private String BROKER_URL = "tcp://localhost:5672";
+
+ public ConnectionTest(String name)
+ {
+ super(name);
+ }
+
+ /** Check that a connection to a broker can be established. */
+ public void testConnection() throws Exception
+ {
+ // Open a connection to the broker and close it again.
+ AMQConnection conn = new AMQConnection(BROKER_URL, "guest", "guest", "clientid", "test");
+ conn.close();
+ }
+
+ protected void setUp()
+ {
+ NDC.push(getName());
+ }
+
+ protected void tearDown()
+ {
+ NDC.pop();
+ }
+}
diff --git a/Final/java/client/distribution/pom.xml b/Final/java/client/distribution/pom.xml
new file mode 100644
index 0000000000..bd97463d15
--- /dev/null
+++ b/Final/java/client/distribution/pom.xml
@@ -0,0 +1,156 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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-distribution</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Client Distributions</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <java.source.version>1.5</java.source.version>
+ <qpid.version>${pom.version}</qpid.version>
+ <qpid.targetDir>${project.build.directory}</qpid.targetDir>
+ <qpid.root>${basedir}/..</qpid.root>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client</artifactId>
+ <type>jar</type>
+ <version>${pom.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <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>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>${assembly.version}</version>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/client-bin.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ <outputDirectory>${qpid.targetDir}</outputDirectory>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <finalName>qpid-incubating</finalName>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/client-bin.xml</descriptor>
+ <descriptor>src/main/assembly/client-src.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ </build>
+
+<profiles>
+ <profile>
+ <id>tests</id>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client</artifactId>
+ <type>test-jar</type>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/client-bin-tests.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+</profiles>
+
+</project>
diff --git a/Final/java/client/distribution/src/main/assembly/client-bin-tests.xml b/Final/java/client/distribution/src/main/assembly/client-bin-tests.xml
new file mode 100644
index 0000000000..ec4df1c9a7
--- /dev/null
+++ b/Final/java/client/distribution/src/main/assembly/client-bin-tests.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <id>java-client-bin-with-tests</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <!-- Apache license files -->
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include easy access to test source-->
+ <fileSet>
+ <directory>../src/test</directory>
+ <outputDirectory>qpid-${qpid.version}/src</outputDirectory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </fileSet>
+
+ <!-- fileSet> Client contains a readme.txt as does qpid root.
+ Which will cause problems on windows as the zip will
+ contain: readme.txt and README.txt
+ < Client local documentation>
+ <directory>..</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet-->
+
+ <!-- Configuration -->
+ <fileSet>
+ <directory>../test/etc</directory>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </fileSet>
+
+ <!-- Execution Scripts -->
+ <fileSet>
+ <directory>../test/bin</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ <fileMode>777</fileMode> <!-- RWX -->
+ </fileSet>
+
+ <!-- Metadata Jar -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-incubating.jar</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-client-distribution</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/client/distribution/src/main/assembly/client-bin.xml b/Final/java/client/distribution/src/main/assembly/client-bin.xml
new file mode 100644
index 0000000000..70874b09a4
--- /dev/null
+++ b/Final/java/client/distribution/src/main/assembly/client-bin.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <id>java-client-bin</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <!-- Apache license files -->
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <!--fileSet>
+ < Client local documentation>
+ <directory>..</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet-->
+
+ <fileSet>
+ <directory>../../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-incubating.jar</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-client:jar:java14</exclude>
+ <exclude>org.apache.qpid:qpid-common:jar:java14</exclude>
+ <exclude>org.apache.qpid:qpid-client-distribution</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/client/distribution/src/main/assembly/client-java1.4-bin.xml b/Final/java/client/distribution/src/main/assembly/client-java1.4-bin.xml
new file mode 100644
index 0000000000..6a2dd17c5c
--- /dev/null
+++ b/Final/java/client/distribution/src/main/assembly/client-java1.4-bin.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<!-- Assembly instructions for a client that runs on Java 1.4 -->
+<assembly>
+ <id>java-client-java1.4-bin</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <!-- Apache license files -->
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-incubating.jar</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-client:jar</exclude>
+ <exclude>org.apache.qpid:qpid-common:jar</exclude>
+ <exclude>org.apache.qpid:qpid-client-distribution</exclude>
+ <exclude>org.apache.mina:mina-java5</exclude>
+ <exclude>org.apache.mina:mina-filter-ssl</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
+
+
diff --git a/Final/java/client/distribution/src/main/assembly/client-src.xml b/Final/java/client/distribution/src/main/assembly/client-src.xml
new file mode 100644
index 0000000000..b5055f05d7
--- /dev/null
+++ b/Final/java/client/distribution/src/main/assembly/client-src.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>java-client-src</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+
+ <fileSet>
+ <!-- Apache license files -->
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}-src</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>qpid-${qpid.version}-src</outputDirectory>
+ <includes>
+ <include>src/main/**</include>
+ <include>src/test/**</include>
+ <include>test/main/**</include>
+ <include>test/test/**</include>
+ <include>pom.xml</include>
+ <include>distribution/**</include>
+ </includes>
+ <excludes>
+ <exclude>**/target</exclude>
+ <exclude>**/target/**/*</exclude>
+ <exclude>**/build</exclude>
+ <exclude>**/build/**/*</exclude>
+ </excludes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/Final/java/client/example/bin/set_classpath.bat b/Final/java/client/example/bin/set_classpath.bat
new file mode 100644
index 0000000000..d528967024
--- /dev/null
+++ b/Final/java/client/example/bin/set_classpath.bat
@@ -0,0 +1,50 @@
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+
+@REM Helper script to set classpath for running Qpid example classes
+@REM NB: You must add the Qpid client and common jars to your CLASSPATH
+@REM before running this script
+
+@echo off
+
+if "%QPID_HOME%" == "" GOTO ERROR_QPID_HOME
+
+set QPIDLIB=%QPID_HOME%\lib
+
+if "%CLASSPATH%" == "" GOTO ERROR_CLASSPATH
+
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\backport-util-concurrent-2.2.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\geronimo-jms_1.1_spec-1.0.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-collections-3.1.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-configuration-1.2.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-cli-1.0.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-lang-2.1.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-logging-api-1.0.4.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-logging-1.0.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\log4j-1.2.12.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\mina-core-1.0.0.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\mina-filter-ssl-1.0.0.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\mina-java5-1.0.0.jar
+set CLASSPATH=%CLASSPATH%;%QPIDLIB%\slf4j-simple-1.0.jar
+
+GOTO END
+
+:ERROR_CLASSPATH
+Echo Please set set your CLASSPATH variable to include the Qpid client and common jars. Exiting ....
+:ERROR_QPID_HOME
+Echo Please set QPID_HOME variable. Exiting ....
+:END
diff --git a/Final/java/client/example/bin/set_classpath.sh b/Final/java/client/example/bin/set_classpath.sh
new file mode 100755
index 0000000000..89e9bc8242
--- /dev/null
+++ b/Final/java/client/example/bin/set_classpath.sh
@@ -0,0 +1,83 @@
+#!/bin/sh -xv
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Helper script to set classpath for running Qpid example classes
+# NB: You must add the Qpid client and common jars to your CLASSPATH
+# before running this script
+
+
+cygwin=false
+if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then
+ cygwin=true
+fi
+
+#Should have set the QPID_HOME var after install to the working dir e.g. home/qpid/qpid-1.0-incubating-M2-SNAPSHOT
+if [ "$QPID_HOME" = "" ] ; then
+ echo "ERROR: Please set QPID_HOME variable. Exiting ...."
+ exit 1
+else
+ QPIDLIB=$QPID_HOME/lib
+fi
+
+if $cygwin; then
+ QPIDLIB=$(cygpath -w $QPIDLIB)
+fi
+
+if [ "$CLASSPATH" = "" ] ; then
+ echo "ERROR: Please set set your CLASSPATH variable to include the Qpid client and common jars. Exiting ...."
+ exit 2
+fi
+
+#Converts paths for cygwin if req
+#Some nasty concatenation to get round cygpath line limits
+if $cygwin; then
+ SEP=";"
+ CLASSPATH=`cygpath -w $CLASSPATH`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/backport-util-concurrent-2.2.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/geronimo-jms_1.1_spec-1.0.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-collections-3.1.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-configuration-1.2.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-cli-1.0.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-lang-2.1.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-logging-api-1.0.4.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-logging-1.0.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/log4j-1.2.12.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/mina-core-1.0.0.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/mina-filter-ssl-1.0.0.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/mina-java5-1.0.0.jar`
+ CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/slf4j-simple-1.0.jar`
+ export CLASSPATH
+else
+ CLASSPATH=$CLASSPATH:$QPIDLIB/backport-util-concurrent-2.2.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/geronimo-jms_1.1_spec-1.0.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/commons-collections-3.1.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/commons-configuration-1.2.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/commons-cli-1.0.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/commons-lang-2.1.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/commons-logging-api-1.0.4.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/commons-logging-1.0.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/log4j-1.2.12.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/mina-core-1.0.0.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/mina-filter-ssl-1.0.0.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/mina-java5-1.0.0.jar
+ CLASSPATH=$CLASSPATH:$QPIDLIB/slf4j-simple-1.0.jar
+ export CLASSPATH
+fi
+
diff --git a/Final/java/client/example/pom.xml b/Final/java/client/example/pom.xml
new file mode 100644
index 0000000000..3d4cf21a2d
--- /dev/null
+++ b/Final/java/client/example/pom.xml
@@ -0,0 +1,152 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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-example</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Example</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ <amqj.logging.level>warn</amqj.logging.level>
+ </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>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jms_1.1_spec</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>jmscts</groupId>
+ <artifactId>jmscts</artifactId>
+ <version>0.5-b2</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>jms</groupId>
+ <artifactId>jms</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemProperties>
+ <property>
+ <name>amqj.noAutoCreateVMBroker</name>
+ <value>true</value>
+ </property>
+ <property>
+ <name>amqj.logging.level</name>
+ <value>${amqj.logging.level}</value>
+ </property>
+ <property>
+ <name>log4j.configuration</name>
+ <value>file:///${basedir}/src/main/java/log4j.properties</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+
+ <!-- Build a zip file with the source in it, this had to be done with the assembly plugin as the source plugin did not provide a way
+ to exclude the .svn directories. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2-SNAPSHOT</version>
+ <configuration>
+ <descriptors>
+ <descriptor>source-jar.xml</descriptor>
+ </descriptors>
+ <outputDirectory>target</outputDirectory>
+ <workDirectory>target/assembly/work</workDirectory>
+ </configuration>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attached</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- Publish the source as a build artifact. -->
+ <!--
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>target/${project.build.finalName}-source.jar</file>
+ <type>jar</type>
+ <classifier>source</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ -->
+
+ </plugins>
+ </build>
+</project>
diff --git a/Final/java/client/example/source-jar.xml b/Final/java/client/example/source-jar.xml
new file mode 100644
index 0000000000..60451448b8
--- /dev/null
+++ b/Final/java/client/example/source-jar.xml
@@ -0,0 +1,35 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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 jar file that contains all the
+ dependencies, fully expanded into a single jar, required to run the tests of
+ a maven project.
+ -->
+<assembly>
+ <id>source</id>
+ <formats>
+ <format>jar</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <fileSets>
+ <fileSet>
+ <directory>src/main/java</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml b/Final/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml
new file mode 100644
index 0000000000..de64423a51
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml
@@ -0,0 +1,45 @@
+<?xml version="1.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.
+ -
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="FileAppender" class="org.apache.log4j.FileAppender">
+ <param name="File" value="ams_messaging.log"/>
+ <param name="Append" value="false"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%t %-5p %c{2} - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <root>
+ <priority value="debug"/>
+ <appender-ref ref="STDOUT"/>
+ <appender-ref ref="FileAppender"/>
+ </root>
+</log4j:configuration> \ No newline at end of file
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java
new file mode 100644
index 0000000000..6a7626c51d
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java
@@ -0,0 +1,162 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.publisher;
+
+import java.io.File;
+
+import javax.jms.JMSException;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.example.shared.FileUtils;
+import org.apache.qpid.example.shared.Statics;
+
+/**
+ * Class that sends message files to the Publisher to distribute
+ * using files as input
+ * Must set properties for host in properties file or uses in vm broker
+ */
+public class FileMessageDispatcher
+{
+
+ protected static final Logger _logger = Logger.getLogger(FileMessageDispatcher.class);
+
+ protected static Publisher _publisher = null;
+
+ /**
+ * To use this main method you need to specify a path or file to use for input
+ * This class then uses file contents from the dir/file specified to generate
+ * messages to publish
+ * Intended to be a very simple way to get going with publishing using the broker
+ * @param args - must specify one value, the path to file(s) for publisher
+ */
+ public static void main(String[] args)
+ {
+
+ // Check command line args ok - must provide a path or file for us to dispatch
+ if (args.length == 0)
+ {
+ System.out.println("Usage: FileMessageDispatcher <filesToDispatch>" + "");
+ }
+ else
+ {
+ try
+ {
+ // publish message(s) from file(s) to configured queue
+ publish(args[0]);
+
+ // Move payload file(s) to archive location as no error
+ FileUtils.moveFileToNewDir(args[0], System.getProperties().getProperty(Statics.ARCHIVE_PATH));
+ }
+ catch (Exception e)
+ {
+ // log error and exit
+ _logger.error("Error trying to dispatch message: " + e);
+ System.exit(1);
+ }
+ finally
+ {
+ // clean up before exiting
+ if (getPublisher() != null)
+ {
+ getPublisher().cleanup();
+ }
+ }
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Finished dispatching message");
+ }
+
+ System.exit(0);
+ }
+
+ /**
+ * Publish the content of a file or files from a directory as messages
+ * @param path - from main args
+ * @throws JMSException
+ * @throws MessageFactoryException - if cannot create message from file content
+ */
+ public static void publish(String path) throws JMSException, MessageFactoryException
+ {
+ File tempFile = new File(path);
+ if (tempFile.isDirectory())
+ {
+ // while more files in dir publish them
+ File[] files = tempFile.listFiles();
+
+ if ((files == null) || (files.length == 0))
+ {
+ _logger.info("FileMessageDispatcher - No files to publish in input directory: " + tempFile);
+ }
+ else
+ {
+ for (File file : files)
+ {
+ // Create message factory passing in payload path
+ FileMessageFactory factory = new FileMessageFactory(getPublisher().getSession(), file.toString());
+
+ // Send the message generated from the payload using the _publisher
+ getPublisher().sendMessage(factory.createEventMessage());
+
+ }
+ }
+ }
+ else
+ {
+ // handle a single file
+ // Create message factory passing in payload path
+ FileMessageFactory factory = new FileMessageFactory(getPublisher().getSession(), tempFile.toString());
+
+ // Send the message generated from the payload using the _publisher
+ getPublisher().sendMessage(factory.createEventMessage());
+ }
+ }
+
+ /**
+ * Cleanup before exit
+ */
+ public static void cleanup()
+ {
+ if (getPublisher() != null)
+ {
+ getPublisher().cleanup();
+ }
+ }
+
+ /**
+ * @return A Publisher instance
+ */
+ private static Publisher getPublisher()
+ {
+ if (_publisher != null)
+ {
+ return _publisher;
+ }
+
+ // Create a _publisher
+ _publisher = new Publisher();
+
+ return _publisher;
+ }
+
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java
new file mode 100644
index 0000000000..f3b21e3c64
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.qpid.example.publisher;
+
+import org.apache.qpid.example.shared.FileUtils;
+import org.apache.qpid.example.shared.Statics;
+
+import java.io.*;
+import javax.jms.*;
+
+public class FileMessageFactory
+{
+ protected final Session _session;
+ protected final String _payload;
+ protected final String _filename;
+
+ /**
+ * Contructs and instance using a filename from which content will be used to create message
+ * @param session
+ * @param filename
+ * @throws MessageFactoryException
+ */
+ public FileMessageFactory(Session session, String filename) throws MessageFactoryException
+ {
+ try
+ {
+ _filename = filename;
+ _payload = FileUtils.getFileContent(filename);
+ _session = session;
+ }
+ catch (IOException e)
+ {
+ MessageFactoryException mfe = new MessageFactoryException(e.toString());
+ mfe.initCause(e);
+ throw mfe;
+ }
+ }
+
+ /**
+ * Creates a text message and sets filename property on it
+ * The filename property is purely intended to provide visibility
+ * of file content passing trhough the broker using example classes
+ * @return Message - a TextMessage with content from file
+ * @throws JMSException
+ */
+ public Message createEventMessage() throws JMSException
+ {
+ TextMessage msg = _session.createTextMessage();
+ msg.setText(_payload);
+ msg.setStringProperty(Statics.FILENAME_PROPERTY,new File(_filename).getName());
+ return msg;
+ }
+
+ /**
+ * Creates message from a string for use by the monitor
+ * @param session
+ * @param textMsg - message content
+ * @return Message - TextMessage with content from String
+ * @throws JMSException
+ */
+ public static Message createSimpleEventMessage(Session session, String textMsg) throws JMSException
+ {
+ TextMessage msg = session.createTextMessage();
+ msg.setText(textMsg);
+ return msg;
+ }
+
+ public Message createShutdownMessage() throws JMSException
+ {
+ return _session.createTextMessage("SHUTDOWN");
+ }
+
+ public Message createReportRequestMessage() throws JMSException
+ {
+ return _session.createTextMessage("REPORT");
+ }
+
+ public Message createReportResponseMessage(String msg) throws JMSException
+ {
+ return _session.createTextMessage(msg);
+ }
+
+ public boolean isShutdown(Message m)
+ {
+ return checkText(m, "SHUTDOWN");
+ }
+
+ public boolean isReport(Message m)
+ {
+ return checkText(m, "REPORT");
+ }
+
+ public Object getReport(Message m)
+ {
+ try
+ {
+ return ((TextMessage) m).getText();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return e.toString();
+ }
+ }
+
+ private static boolean checkText(Message m, String s)
+ {
+ try
+ {
+ return m instanceof TextMessage && ((TextMessage) m).getText().equals(s);
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return false;
+ }
+ }
+}
+
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java
new file mode 100644
index 0000000000..0a4231c977
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.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.example.publisher;
+
+public class MessageFactoryException extends Exception
+{
+
+ private int _errorCode;
+
+ public MessageFactoryException(String message)
+ {
+ super(message);
+ }
+
+ public MessageFactoryException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+
+ public MessageFactoryException(int errorCode, String msg, Throwable t)
+ {
+ super(msg + " [error code " + errorCode + ']', t);
+ _errorCode = errorCode;
+ }
+
+ public MessageFactoryException(int errorCode, String msg)
+ {
+ super(msg + " [error code " + errorCode + ']');
+ _errorCode = errorCode;
+ }
+
+ public int getErrorCode()
+ {
+ return _errorCode;
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java
new file mode 100644
index 0000000000..b6544db995
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.publisher;
+
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+
+/**
+ * Class that sends heartbeat messages to allow monitoring of message consumption Sends regular (currently 20 seconds
+ * apart) heartbeat message
+ */
+public class MonitorMessageDispatcher
+{
+
+ private static final Logger _logger = Logger.getLogger(MonitorMessageDispatcher.class);
+
+ protected static MonitorPublisher _monitorPublisher = null;
+
+ protected static final String DEFAULT_MONITOR_PUB_NAME = "MonitorPublisher";
+
+ /**
+ * Easy entry point for running a message dispatcher for monitoring consumption
+ *
+ * @param args
+ */
+ public static void main(String[] args)
+ {
+ //Switch on logging appropriately for your app
+ BasicConfigurator.configure();
+
+ try
+ {
+ int i =0;
+ while (i < 1000)
+ {
+ try
+ {
+ //endlessly publish messages to monitor queue
+ publish();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Dispatched monitor message");
+ }
+
+ //sleep for twenty seconds and then publish again - change if appropriate
+ //Thread.sleep(1000);
+ i++ ;
+ }
+ catch (UndeliveredMessageException a)
+ {
+ //trigger application specific failure handling here
+ _logger.error("Problem delivering monitor message");
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error trying to dispatch AMS monitor message: " + e);
+ System.exit(1);
+ }
+ finally
+ {
+ if (getMonitorPublisher() != null)
+ {
+ getMonitorPublisher().cleanup();
+ }
+ }
+
+ System.exit(1);
+ }
+
+ /**
+ * Publish heartbeat message
+ *
+ * @throws JMSException
+ * @throws UndeliveredMessageException
+ */
+ public static void publish() throws JMSException, UndeliveredMessageException
+ {
+ //Send the message generated from the payload using the _publisher
+// getMonitorPublisher().sendImmediateMessage
+// (FileMessageFactory.createSimpleEventMessage(getMonitorPublisher().getSession(),"monitor:" +System.currentTimeMillis()));
+
+ getMonitorPublisher().sendMessage
+ (getMonitorPublisher()._session,
+ FileMessageFactory.createSimpleEventMessage(getMonitorPublisher().getSession(), "monitor:" + System.currentTimeMillis()),
+ DeliveryMode.PERSISTENT, false, true);
+
+ }
+
+ /** Cleanup publishers */
+ public static void cleanup()
+ {
+ if (getMonitorPublisher() != null)
+ {
+ getMonitorPublisher().cleanup();
+ }
+
+ if (getMonitorPublisher() != null)
+ {
+ getMonitorPublisher().cleanup();
+ }
+ }
+
+ //Returns a _publisher for the monitor queue
+ private static MonitorPublisher getMonitorPublisher()
+ {
+ if (_monitorPublisher != null)
+ {
+ return _monitorPublisher;
+ }
+
+ //Create a _publisher using failover details and constant for monitor queue
+ _monitorPublisher = new MonitorPublisher();
+
+ _monitorPublisher.setName(MonitorMessageDispatcher.DEFAULT_MONITOR_PUB_NAME);
+ return _monitorPublisher;
+ }
+
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java
new file mode 100644
index 0000000000..a67b602e58
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.qpid.example.publisher;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.BasicMessageProducer;
+
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Session;
+
+/**
+ * Subclass of Publisher which uses QPID functionality to send a heartbeat message Note immediate flag not available via
+ * JMS MessageProducer
+ */
+public class MonitorPublisher extends Publisher
+{
+
+ private static final Logger _log = Logger.getLogger(Publisher.class);
+
+ BasicMessageProducer _producer;
+
+ public MonitorPublisher()
+ {
+ super();
+ }
+
+ /*
+ * Publishes a message using given details
+ */
+ public boolean sendMessage(Session session, Message message, int deliveryMode,
+ boolean immediate, boolean commit) throws UndeliveredMessageException
+ {
+ try
+ {
+ _producer = (BasicMessageProducer) session.createProducer(_destination);
+
+ _producer.send(message, deliveryMode, immediate);
+
+ if (commit)
+ {
+ //commit the message send and close the transaction
+ _session.commit();
+ }
+
+ }
+ catch (JMSException e)
+ {
+ //Have to assume our commit failed but do not rollback here as channel closed
+ _log.error(e);
+ e.printStackTrace();
+ throw new UndeliveredMessageException("Cannot deliver immediate message", e);
+ }
+
+ _log.info(_name + " finished sending message: " + message);
+ return true;
+ }
+
+ /*
+ * Publishes a non-persistent message using transacted session
+ */
+ public boolean sendImmediateMessage(Message message) throws UndeliveredMessageException
+ {
+ try
+ {
+ _producer = (BasicMessageProducer) _session.createProducer(_destination);
+
+ //Send message via our producer which is not persistent and is immediate
+ //NB: not available via jms interface MessageProducer
+ _producer.send(message, DeliveryMode.NON_PERSISTENT, true);
+
+ //commit the message send and close the transaction
+ _session.commit();
+
+ }
+ catch (JMSException e)
+ {
+ //Have to assume our commit failed but do not rollback here as channel closed
+ _log.error(e);
+ e.printStackTrace();
+ throw new UndeliveredMessageException("Cannot deliver immediate message", e);
+ }
+
+ _log.info(_name + " finished sending message: " + message);
+ return true;
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java
new file mode 100644
index 0000000000..2bde4ec35c
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java
@@ -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.
+ */
+package org.apache.qpid.example.publisher;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.DeliveryMode;
+import javax.jms.Queue;
+import javax.jms.MessageProducer;
+import javax.jms.Connection;
+import javax.jms.Session;
+
+import javax.naming.InitialContext;
+
+import org.apache.qpid.example.shared.InitialContextHelper;
+
+public class Publisher
+{
+ private static final Logger _log = Logger.getLogger(Publisher.class);
+
+ protected InitialContextHelper _contextHelper;
+
+ protected Connection _connection;
+
+ protected Session _session;
+
+ protected MessageProducer _producer;
+
+ protected String _destinationDir;
+
+ protected String _name = "Publisher";
+
+ protected Queue _destination;
+
+ protected static final String _defaultDestinationDir = "/tmp";
+
+ /**
+ * Creates a Publisher instance using properties from example.properties
+ * See InitialContextHelper for details of how context etc created
+ */
+ public Publisher()
+ {
+ try
+ {
+ //get an initial context from default properties
+ _contextHelper = new InitialContextHelper(null);
+ InitialContext ctx = _contextHelper.getInitialContext();
+
+ //then create a connection using the AMQConnectionFactory
+ AMQConnectionFactory cf = (AMQConnectionFactory) ctx.lookup("local");
+ _connection = cf.createConnection();
+
+ //create a transactional session
+ _session = _connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
+
+ //lookup the example queue and use it
+ //Queue is non-exclusive and not deleted when last consumer detaches
+ _destination = (Queue) ctx.lookup("MyQueue");
+
+ //create a message producer
+ _producer = _session.createProducer(_destination);
+
+ //set destination dir for files that have been processed
+ _destinationDir = _defaultDestinationDir;
+
+ _connection.start();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ _log.error(e);
+ }
+ }
+
+ /**
+ * Publishes a non-persistent message using transacted session
+ * Note that persistent is the default mode for send - so need to specify for transient
+ */
+ public boolean sendMessage(Message message)
+ {
+ try
+ {
+ //Send message via our producer which is not persistent
+ _producer.send(message, DeliveryMode.NON_PERSISTENT, _producer.getPriority(), _producer.getTimeToLive());
+
+ //commit the message send and close the transaction
+ _session.commit();
+
+ }
+ catch (JMSException e)
+ {
+ //Have to assume our commit failed and rollback here
+ try
+ {
+ _session.rollback();
+ _log.error(e);
+ e.printStackTrace();
+ return false;
+ }
+ catch (JMSException j)
+ {
+ _log.error("Unable to rollback publish transaction ",e);
+ return false;
+ }
+ }
+
+ _log.info(_name + " finished sending message: " + message);
+ return true;
+ }
+
+ /**
+ * Cleanup resources before exit
+ */
+ public void cleanup()
+ {
+ try
+ {
+ if (_connection != null)
+ {
+ _connection.stop();
+ _connection.close();
+ }
+ _connection = null;
+ _producer = null;
+ }
+ catch(Exception e)
+ {
+ _log.error("Error trying to cleanup publisher " + e);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Exposes session
+ * @return Session
+ */
+ public Session getSession()
+ {
+ return _session;
+ }
+
+ public String getDestinationDir()
+ {
+ return _destinationDir;
+ }
+
+ public void setDestinationDir(String destinationDir)
+ {
+ _destinationDir = destinationDir;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public void setName(String _name) {
+ this._name = _name;
+ }
+}
+
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java
new file mode 100644
index 0000000000..399cbc9427
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.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.example.publisher;
+
+/**
+ * Exception thrown by monitor when cannot send a message marked for immediate delivery
+ */
+public class UndeliveredMessageException extends Exception
+{
+
+ private int _errorCode;
+
+ public UndeliveredMessageException(String message)
+ {
+ super(message);
+ }
+
+ public UndeliveredMessageException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+
+ public UndeliveredMessageException(int errorCode, String msg, Throwable t)
+ {
+ super(msg + " [error code " + errorCode + ']', t);
+ _errorCode = errorCode;
+ }
+
+ public UndeliveredMessageException(int errorCode, String msg)
+ {
+ super(msg + " [error code " + errorCode + ']');
+ _errorCode = errorCode;
+ }
+
+ public int getErrorCode()
+ {
+ return _errorCode;
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java
new file mode 100644
index 0000000000..e32ee0ba73
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.pubsub;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.naming.NamingException;
+
+/**
+ * An abstract base class that wraps up the creation of a JMS client utilising JNDI
+ */
+public abstract class Client
+{
+ protected ConnectionSetup _setup;
+
+ protected Connection _connection;
+ protected Destination _destination;
+ protected Session _session;
+
+ public Client(String destination)
+ {
+ if (destination == null)
+ {
+ destination = ConnectionSetup.TOPIC_JNDI_NAME;
+ }
+
+ try
+ {
+ _setup = new ConnectionSetup();
+ }
+ catch (NamingException e)
+ {
+ //ignore
+ }
+
+ if (_setup != null)
+ {
+ try
+ {
+ _connection = _setup.getConnectionFactory().createConnection();
+ _destination = _setup.getDestination(destination);
+ }
+ catch (JMSException e)
+ {
+ System.err.println(e.getMessage());
+ }
+ }
+ }
+
+ public abstract void start();
+
+} \ No newline at end of file
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java
new file mode 100644
index 0000000000..c4edd9034f
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.pubsub;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import java.util.Properties;
+
+/**
+ * This ConnectionSetup is a wrapper around JNDI it creates a number of entries.
+ *
+ * It is equivalent to a PropertyFile of value:
+ *
+ * connectionfactory.local=amqp://guest:guest@clientid/test?brokerlist='localhost'
+ * connectionfactory.vm=amqp://guest:guest@clientid/test?brokerlist='vm://:1'
+ *
+ * queue.queue=example.MyQueue
+ * topic.topic=example.hierarical.topic
+ *
+ */
+public class ConnectionSetup
+{
+ final static String INITIAL_CONTEXT_FACTORY = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory";
+
+ final static String CONNECTION_JNDI_NAME = "local";
+ final static String CONNECTION_NAME = "amqp://guest:guest@clientid/test?brokerlist='localhost'";
+
+ public static final String QUEUE_JNDI_NAME = "queue";
+ final static String QUEUE_NAME = "example.MyQueue";
+
+ public static final String TOPIC_JNDI_NAME = "topic";
+ final static String TOPIC_NAME = "example.hierarical.topic";
+
+ private Context _ctx;
+
+ public ConnectionSetup() throws NamingException
+ {
+
+ // Set the properties ...
+ Properties properties = new Properties();
+ properties.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
+ properties.put("connectionfactory." + CONNECTION_JNDI_NAME, CONNECTION_NAME);
+ properties.put("connectionfactory." + "vm", "amqp://guest:guest@clientid/test?brokerlist='vm://:1'");
+
+ properties.put("queue." + QUEUE_JNDI_NAME, QUEUE_NAME);
+ properties.put("topic." + TOPIC_JNDI_NAME, TOPIC_NAME);
+ // Create the initial context
+ _ctx = new InitialContext(properties);
+
+ }
+
+ public ConnectionSetup(Properties properties) throws NamingException
+ {
+ _ctx = new InitialContext(properties);
+ }
+
+ public ConnectionFactory getConnectionFactory()
+ {
+
+ // Perform the lookups
+ try
+ {
+ return (ConnectionFactory) _ctx.lookup(CONNECTION_JNDI_NAME);
+ }
+ catch (NamingException e)
+ {
+ //ignore
+ }
+ return null;
+ }
+
+ public Destination getDestination(String jndiName)
+ {
+ // Perform the lookups
+ try
+ {
+ return (Destination) _ctx.lookup(jndiName);
+ }
+ catch (ClassCastException cce)
+ {
+ //ignore
+ }
+ catch (NamingException ne)
+ {
+ //ignore
+ }
+ return null;
+ }
+
+
+ public void close()
+ {
+ try
+ {
+ _ctx.close();
+ }
+ catch (NamingException e)
+ {
+ //ignore
+ }
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java
new file mode 100644
index 0000000000..dd936e429f
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.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.example.pubsub;
+
+import javax.jms.JMSException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+/**
+ * A simple Publisher example.
+ *
+ * The class can take two arguments.
+ * java Publisher <destination> <msgCount>
+ * Where:
+ * destination is either 'topic' or 'queue' (Default: topic)
+ * msgCount is the number of messages to send (Default : 100)
+ *
+ */
+public class Publisher extends Client
+{
+ int _msgCount;
+
+ public Publisher(String destination, int msgCount)
+ {
+ super(destination);
+ _msgCount = msgCount;
+ }
+
+ public void start()
+ {
+ try
+ {
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ MessageProducer _producer = _session.createProducer(_destination);
+
+ for (int msgCount = 0; msgCount < _msgCount; msgCount++)
+ {
+ _producer.send(_session.createTextMessage("msg:" + msgCount));
+ System.out.println("Sent:" + msgCount);
+ }
+
+ System.out.println("Done.");
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+
+
+ public static void main(String[] args)
+ {
+
+ String destination = args.length > 2 ? args[1] : null;
+
+ int msgCount = args.length > 2 ? Integer.parseInt(args[2]) : 100;
+
+ new Publisher(destination, msgCount).start();
+ }
+
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java
new file mode 100644
index 0000000000..f2d736701f
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.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.example.pubsub;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import java.util.concurrent.CountDownLatch;
+
+
+/**
+ * Simple client that listens for the specified number of msgs on the given Destinaton
+ *
+ * The class can take two arguments.
+ * java Subscriber <destination> <msgCount>
+ * Where:
+ * destination is either 'topic' or 'queue' (Default: topic)
+ * msgCount is the number of messages to send (Default : 100)
+ */
+public class Subscriber extends Client implements MessageListener
+{
+
+ CountDownLatch _count;
+
+ public Subscriber(String destination, int msgCount)
+ {
+ super(destination);
+ _count = new CountDownLatch(msgCount);
+ }
+
+
+ public void start()
+ {
+ try
+ {
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _session.createDurableSubscriber((Topic) _setup.getDestination(ConnectionSetup.TOPIC_JNDI_NAME),
+ "exampleClient").setMessageListener(this);
+ _connection.start();
+ _count.await();
+
+ System.out.println("Done");
+
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ String destination = args.length > 2 ? args[1] : null;
+ int msgCount = args.length > 2 ? Integer.parseInt(args[2]) : 100;
+
+ new Subscriber(destination, msgCount).start();
+ }
+
+ public void onMessage(Message message)
+ {
+ try
+ {
+ _count.countDown();
+ System.out.println("Received msg:" + ((TextMessage) message).getText());
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java
new file mode 100644
index 0000000000..6eb847ea9d
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.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.example.shared;
+
+public class ConnectionException extends Exception
+{
+
+ private int _errorCode;
+
+ public ConnectionException(String message)
+ {
+ super(message);
+ }
+
+ public ConnectionException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+
+ public ConnectionException(int errorCode, String msg, Throwable t)
+ {
+ super(msg + " [error code " + errorCode + ']', t);
+ _errorCode = errorCode;
+ }
+
+ public ConnectionException(int errorCode, String msg)
+ {
+ super(msg + " [error code " + errorCode + ']');
+ _errorCode = errorCode;
+ }
+
+ public int getErrorCode()
+ {
+ return _errorCode;
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java
new file mode 100644
index 0000000000..bf805ab817
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.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.example.shared;
+
+public class ContextException extends Exception
+{
+
+ private int _errorCode;
+
+ public ContextException(String message)
+ {
+ super(message);
+ }
+
+ public ContextException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+
+ public ContextException(int errorCode, String msg, Throwable t)
+ {
+ super(msg + " [error code " + errorCode + ']', t);
+ _errorCode = errorCode;
+ }
+
+ public ContextException(int errorCode, String msg)
+ {
+ super(msg + " [error code " + errorCode + ']');
+ _errorCode = errorCode;
+ }
+
+ public int getErrorCode()
+ {
+ return _errorCode;
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java
new file mode 100644
index 0000000000..54446cb6a7
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.qpid.example.shared;
+
+import java.io.*;
+
+/**
+ * Class that provides file related utility methods for utility use
+ */
+public class FileUtils {
+
+
+ //Reads file content into String
+ public static String getFileContent(String filePath) throws IOException
+ {
+
+ BufferedReader reader = null;
+ String tempData = "";
+ String eol = "\n\r";
+
+ try
+ {
+ String line;
+ reader = new BufferedReader(new FileReader(filePath));
+ while ((line = reader.readLine()) != null)
+ {
+ if (!tempData.equals(""))
+ {
+ tempData = tempData + eol + line;
+ }
+ else
+ {
+ tempData = line;
+ }
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ return tempData;
+ }
+
+ /*
+ * Reads xml from a file and returns it as an array of chars
+ */
+ public static char[] getFileAsCharArray(String filePath) throws IOException
+ {
+ BufferedReader reader = null;
+ char[] tempChars = null;
+ String tempData = "";
+
+ try
+ {
+ String line;
+ reader = new BufferedReader(new FileReader(filePath));
+ while ((line = reader.readLine()) != null)
+ {
+ tempData = tempData + line;
+ }
+ tempChars = tempData.toCharArray();
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ return tempChars;
+ }
+
+ /*
+ * Write String content to filename provided
+ */
+ public static void writeStringToFile(String content, String path) throws IOException
+ {
+
+ BufferedWriter writer = new BufferedWriter(new FileWriter(new File(path)));
+ writer.write(content);
+ writer.flush();
+ writer.close();
+ }
+
+ /*
+ * Allows moving of files to a new dir and preserves the last bit of the name only
+ */
+ public static void moveFileToNewDir(String path, String newDir) throws IOException
+ {
+ //get file name from current path
+ //while more files in dir publish them
+ File pathFile = new File(path);
+ if (pathFile.isDirectory())
+ {
+ File[] files = pathFile.listFiles();
+ for (File file : files)
+ {
+ moveFileToNewDir(file,newDir);
+ }
+ }
+ }
+
+ /*
+ * Allows moving of a file to a new dir and preserves the last bit of the name only
+ */
+ public static void moveFileToNewDir(File fileToMove, String newDir) throws IOException
+ {
+ moveFile(fileToMove,getArchiveFileName(fileToMove,newDir));
+ }
+
+ /*
+ * Moves file from a given path to a new path with String params
+ */
+ public static void moveFile(String fromPath, String dest) throws IOException
+ {
+ moveFile(new File(fromPath),new File(dest));
+ }
+
+ /*
+ * Moves file from a given path to a new path with mixed params
+ */
+ public static void moveFile(File fileToMove, String dest) throws IOException
+ {
+ moveFile(fileToMove,new File(dest));
+ }
+
+ /*
+ * Moves file from a given path to a new path with File params
+ */
+ public static void moveFile(File fileToMove, File dest) throws IOException
+ {
+ fileToMove.renameTo(dest);
+ }
+
+ /*
+ * Deletes a given file
+ */
+ public static void deleteFile(String filePath) throws IOException
+ {
+ new File(filePath).delete();
+ }
+
+ private static String getArchiveFileName(File fileToMove, String archiveDir)
+ {
+ //get file name from current path
+ String fileName = fileToMove.getName();
+ return archiveDir + File.separator + fileName;
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java
new file mode 100644
index 0000000000..98a2c0d497
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.shared;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Class that provides helper methods for JNDI
+ */
+public class InitialContextHelper
+{
+
+ public static final String _defaultPropertiesName = "example.properties";
+ protected static Properties _fileProperties;
+ protected static InitialContext _initialContext;
+ protected static final Logger _log = Logger.getLogger(InitialContextHelper.class);
+
+ public InitialContextHelper(String propertiesName) throws ContextException
+ {
+ try
+ {
+ if ((propertiesName == null) || (propertiesName.length() == 0))
+ {
+ propertiesName = _defaultPropertiesName;
+ }
+
+ _fileProperties = new Properties();
+ ClassLoader cl = this.getClass().getClassLoader();
+
+ // NB: Need to change path to reflect package if moving classes around !
+ InputStream is = cl.getResourceAsStream("org/apache/qpid/example/shared/" + propertiesName);
+ _fileProperties.load(is);
+ _initialContext = new InitialContext(_fileProperties);
+ }
+ catch (IOException e)
+ {
+ throw new ContextException(e.toString(), e);
+ }
+ catch (NamingException n)
+ {
+ throw new ContextException(n.toString(), n);
+ }
+ }
+
+ public Properties getFileProperties()
+ {
+ return _fileProperties;
+ }
+
+ public InitialContext getInitialContext()
+ {
+ return _initialContext;
+ }
+
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java
new file mode 100644
index 0000000000..c056f8a7da
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.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.example.shared;
+
+/**
+ * Constants used by AMS Publisher/Subscriber classes
+ */
+public class Statics {
+
+ public static final String TOPIC_NAME = "EXAMPLE_TOPIC";
+
+ public static final String QUEUE_NAME = "EXAMPLE_QUEUE";
+
+ public static final String MONITOR_QUEUE_SUFFIX = "_MONITOR";
+
+ public static final String HOST_PROPERTY = "host";
+
+ public static final String PORT_PROPERTY = "port";
+
+ public static final String USER_PROPERTY = "user";
+
+ public static final String PWD_PROPERTY = "pwd";
+
+ public static final String TOPIC_PROPERTY = "topic";
+
+ public static final String QUEUE_PROPERTY = "queue";
+
+ public static final String VIRTUAL_PATH_PROPERTY = "virtualpath";
+
+ public static final String ARCHIVE_PATH = "archivepath";
+
+ public static final String CLIENT_PROPERTY = "client";
+
+ public static final String FILENAME_PROPERTY = "filename";
+
+ public static final String DEFAULT_USER = "guest";
+
+ public static final String DEFAULT_PWD = "guest";
+
+
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties
new file mode 100644
index 0000000000..a60e3964ad
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory
+
+# use the following property to configure the default connector
+#java.naming.provider.url - ignored.
+
+# register some connection factories
+# connectionfactory.[jndiname] = [ConnectionURL]
+connectionfactory.local = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'
+
+# register some queues in JNDI using the form
+# queue.[jndiName] = [physicalName]
+queue.MyQueue = example.MyQueue
+
+# register some topics in JNDI using the form
+# topic.[jndiName] = [physicalName]
+topic.ibmStocks = stocks.nyse.ibm
+
+# Register an AMQP destination in JNDI
+# NOTE: Qpid currently only supports direct,topics and headers
+# destination.[jniName] = [BindingURL]
+destination.direct = direct://amq.direct//directQueue
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java
new file mode 100644
index 0000000000..1d2e5e0e66
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.subscriber;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.example.shared.Statics;
+
+import javax.jms.*;
+
+/**
+ * Subclass of Subscriber which consumes a heartbeat message
+ */
+
+public class MonitoredSubscriber extends Subscriber
+{
+ protected String _monitorDestinationName;
+
+ private static final Logger _logger = Logger.getLogger(MonitoredSubscriber.class);
+
+ private static MessageConsumer _monitorConsumer;
+
+ public MonitoredSubscriber()
+ {
+ super();
+ //lookup queue name and append suffix
+ _monitorDestinationName = _destination.toString() + Statics.MONITOR_QUEUE_SUFFIX;
+ }
+
+ /**
+ * MessageListener implementation for this subscriber
+ */
+ public static class MonitorMessageListener implements MessageListener
+ {
+ private String _name;
+
+ public MonitorMessageListener(String name)
+ {
+ _name = name;
+
+ }
+
+ /**
+ * Listens for heartbeat messages and acknowledges them
+ * @param message
+ */
+ public void onMessage(javax.jms.Message message)
+ {
+ _logger.info(_name + " monitor got message '" + message + "'");
+
+ try
+ {
+ _logger.debug("Monitor acknowledging recieved message");
+
+ //Now acknowledge the message to clear it from our queue
+ message.acknowledge();
+ }
+ catch(JMSException j)
+ {
+ _logger.error("Monitor caught JMSException trying to acknowledge message receipt");
+ j.printStackTrace();
+ }
+ catch(Exception e)
+ {
+ _logger.error("Monitor caught unexpected exception trying to handle message");
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Subscribes to Queue and attaches additional monitor listener
+ */
+ public void subscribeAndMonitor()
+ {
+ try
+ {
+ _connection = _connectionFactory.createConnection();
+
+ //create a transactional session
+ Session session = _connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
+
+ //Queue is non-exclusive and not deleted when last consumer detaches
+ Destination destination = session.createQueue(_monitorDestinationName);
+
+ //Create a consumer with a destination of our queue which will use defaults for prefetch etc
+ _monitorConsumer = session.createConsumer(destination);
+
+ //give the monitor message listener a name of it's own
+ _monitorConsumer.setMessageListener(new MonitoredSubscriber.MonitorMessageListener
+ ("MonitorListener " + System.currentTimeMillis()));
+
+ MonitoredSubscriber._logger.info("Starting monitored subscription ...");
+
+ MonitoredSubscriber._connection.start();
+
+ //and now start ordinary consumption too
+ subscribe();
+ }
+ catch (Throwable t)
+ {
+ _logger.error("Fatal error: " + t);
+ t.printStackTrace();
+ }
+ }
+
+ /**
+ * Stop consuming
+ */
+ public void stopMonitor()
+ {
+ try
+ {
+ _monitorConsumer.close();
+ _monitorConsumer = null;
+ stop();
+ }
+ catch(JMSException j)
+ {
+ _logger.error("JMSException trying to Subscriber.stop: " + j.getStackTrace());
+ }
+ }
+
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java
new file mode 100644
index 0000000000..d2f27da052
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.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.example.subscriber;
+
+import org.apache.log4j.BasicConfigurator;
+
+/**
+ * Allows you to simply start a monitored subscriber
+ */
+public class MonitoredSubscriptionWrapper {
+
+ private static MonitoredSubscriber _subscriber;
+
+ /**
+ * Create a monitored subscriber and start it
+ * @param args - no params required
+ */
+ public static void main(String args[])
+ {
+ //switch on logging
+ BasicConfigurator.configure();
+
+ _subscriber = new MonitoredSubscriber();
+
+ _subscriber.subscribe();
+ }
+
+ /**
+ * Stop subscribing now ...
+ */
+ public static void stop()
+ {
+ _subscriber.stop();
+ }
+}
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java
new file mode 100644
index 0000000000..d443dca828
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java
@@ -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.
+ */
+package org.apache.qpid.example.subscriber;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnectionFactory;
+
+import javax.jms.*;
+import javax.jms.Connection;
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+import javax.naming.InitialContext;
+
+import org.apache.qpid.example.shared.InitialContextHelper;
+
+/**
+ * Subscriber which consumes messages from a queue
+ */
+
+public class Subscriber
+{
+ private static final Logger _log = Logger.getLogger(Subscriber.class);
+
+ protected static Connection _connection;
+
+ protected static MessageConsumer _consumer;
+
+ protected static InitialContextHelper _contextHelper;
+
+ protected static AMQConnectionFactory _connectionFactory;
+
+ protected Destination _destination;
+
+ public Subscriber()
+ {
+ try
+ {
+ //get an initial context from default properties
+ _contextHelper = new InitialContextHelper(null);
+ InitialContext ctx = _contextHelper.getInitialContext();
+
+ //then create a connection using the AMQConnectionFactory
+ _connectionFactory = (AMQConnectionFactory) ctx.lookup("local");
+
+ //lookup queue from context
+ _destination = (Destination) ctx.lookup("MyQueue");
+
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ _log.error(e);
+ }
+ }
+
+ /**
+ * Listener class that handles messages
+ */
+ public static class ExampleMessageListener implements MessageListener
+ {
+ private String _name;
+
+ public ExampleMessageListener(String name)
+ {
+ _name = name;
+ }
+
+ /**
+ * Listens for message callbacks, handles and then acknowledges them
+ * @param message - the message received
+ */
+ public void onMessage(javax.jms.Message message)
+ {
+ _log.info(_name + " got message '" + message + "'");
+
+ try
+ {
+ //NB: Handle your message appropriately for your application here
+ //do some stuff
+
+ _log.debug("Acknowledging recieved message");
+
+ //Now acknowledge the message to clear it from our queue
+ message.acknowledge();
+ }
+ catch(JMSException j)
+ {
+ _log.error("JMSException trying to acknowledge message receipt");
+ j.printStackTrace();
+ }
+ catch(Exception e)
+ {
+ _log.error("Unexpected exception trying to handle message");
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Subscribes to example Queue and attaches listener
+ */
+ public void subscribe()
+ {
+ _log.info("Starting subscription ...");
+
+ try
+ {
+ _connection = _connectionFactory.createConnection();
+
+ //Non transactional session using client acknowledgement
+ Session session = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ //Create a consumer with a destination of our queue which will use defaults for prefetch etc
+ _consumer = session.createConsumer(_destination);
+
+ //give the message listener a name of it's own
+ _consumer.setMessageListener(new ExampleMessageListener("MessageListener " + System.currentTimeMillis()));
+
+ _connection.start();
+ }
+ catch (Throwable t)
+ {
+ _log.error("Fatal error: " + t);
+ t.printStackTrace();
+ }
+
+ _log.info("Waiting for messages ...");
+
+ //wait for messages and sleep to survive failover
+ try
+ {
+ while(true)
+ {
+ Thread.sleep(Long.MAX_VALUE);
+ }
+ }
+ catch (Exception e)
+ {
+ _log.warn("Exception while Subscriber sleeping",e);
+ }
+ }
+
+ /**
+ * Stop consuming and close connection
+ */
+ public void stop()
+ {
+ try
+ {
+ _consumer.close();
+ _consumer = null;
+ _connection.stop();
+ _connection.close();
+ }
+ catch(JMSException j)
+ {
+ _log.error("JMSException trying to Subscriber.stop: " + j.getStackTrace());
+ }
+ }
+
+}
+
+
+
+
diff --git a/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java
new file mode 100644
index 0000000000..32a0ef685c
--- /dev/null
+++ b/Final/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.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.example.subscriber;
+
+import org.apache.log4j.BasicConfigurator;
+
+/**
+ * Allows you to simply start a subscriber
+ */
+public class SubscriptionWrapper {
+
+ private static Subscriber _subscriber;
+
+ /**
+ * Create a subscriber and start it
+ * @param args
+ */
+ public static void main(String args[])
+ {
+ //switch on logging
+ BasicConfigurator.configure();
+
+ _subscriber = new Subscriber();
+
+ _subscriber.subscribe();
+ }
+
+ /**
+ * Stop subscribing now ...
+ */
+ public static void stop()
+ {
+ _subscriber.stop();
+ }
+}
diff --git a/Final/java/client/pom.xml b/Final/java/client/pom.xml
new file mode 100644
index 0000000000..0f0757e9a3
--- /dev/null
+++ b/Final/java/client/pom.xml
@@ -0,0 +1,249 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Client</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <java.source.version>1.5</java.source.version>
+ <qpid.version>${pom.version}</qpid.version>
+ <qpid.targetDir>${project.build.directory}</qpid.targetDir>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.4.0</version>
+ </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>
+
+
+ <!-- Test Dependencies -->
+
+ <dependency> <!-- for inVm Broker -->
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>uk.co.thebadgerset</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <scope>test</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>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>minijar-maven-plugin</artifactId>
+ <groupId>org.codehaus.mojo</groupId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>ueberjar</goal>
+ </goals>
+ <configuration>
+ <stripUnusedClasses>false</stripUnusedClasses>
+ <name>[artifactId]-[version]-single.jar</name>
+ <classifier>single</classifier>
+ <attach>true</attach>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>target/${artifactId}-${version}-single.jar</file>
+ <type>jar</type>
+ <classifier>single</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <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>amqj.noAutoCreateVMBroker</name>
+ <value>true</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </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-client</id>
+ <goals>
+ <goal>translate-project</goal>
+ </goals>
+ <configuration>
+ <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>
+
+ </plugins>
+
+ <testResources>
+ <testResource>
+ <targetPath>META-INF/</targetPath>
+ <filtering>false</filtering>
+ <directory>../resources/META-INF</directory>
+ <includes>
+ <include>**</include>
+ </includes>
+ </testResource>
+
+ <!--
+ <testResource>
+ <targetPath>src/</targetPath>
+ <filtering>false</filtering>
+ <directory>src/test/java</directory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </testResource>
+ -->
+
+ <testResource>
+ <targetPath></targetPath>
+ <filtering>false</filtering>
+ <directory>src/main/java</directory>
+ <includes>
+ <include>client.log4j</include>
+ </includes>
+ </testResource>
+ </testResources>
+
+ </build>
+
+</project>
diff --git a/Final/java/client/src/main/java/client.log4j b/Final/java/client/src/main/java/client.log4j
new file mode 100644
index 0000000000..525433e9a9
--- /dev/null
+++ b/Final/java/client/src/main/java/client.log4j
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootLogger=${root.logging.level}
+
+
+log4j.logger.org.apache.qpid=${amqj.logging.level}, console
+log4j.additivity.org.apache.qpid=false
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+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/Final/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java
new file mode 100644
index 0000000000..b6fbb6c6bf
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQAuthenticationException represents all failures to authenticate access to a broker.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to authenticate the client.
+ * </table>
+ *
+ * @todo Will this alwyas have the same status code, NOT_ALLOWED 530? Might set this up to always use that code.
+ */
+public class AMQAuthenticationException extends AMQException
+{
+ public AMQAuthenticationException(AMQConstant error, String msg)
+ {
+ super(error, msg);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
new file mode 100644
index 0000000000..c04380ba8c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java
@@ -0,0 +1,353 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.url.URLHelper;
+import org.apache.qpid.url.URLSyntaxException;
+
+public class AMQBrokerDetails implements BrokerDetails
+{
+ private String _host;
+ private int _port;
+ private String _transport;
+
+ private HashMap<String, String> _options;
+
+ private SSLConfiguration _sslConfiguration;
+
+ public AMQBrokerDetails()
+ {
+ _options = new HashMap<String, String>();
+ }
+
+ public AMQBrokerDetails(String url) throws URLSyntaxException
+ {
+ this();
+ // URL should be of format tcp://host:port?option='value',option='value'
+ try
+ {
+ URI connection = new URI(url);
+
+ String transport = connection.getScheme();
+
+ // Handles some defaults to minimise changes to existing broker URLS e.g. localhost
+ if (transport != null)
+ {
+ //todo this list of valid transports should be enumerated somewhere
+ if ((!(transport.equalsIgnoreCase("vm") ||
+ transport.equalsIgnoreCase("tcp"))))
+ {
+ if (transport.equalsIgnoreCase("localhost"))
+ {
+ connection = new URI(DEFAULT_TRANSPORT + "://" + url);
+ transport = connection.getScheme();
+ }
+ else
+ {
+ if (url.charAt(transport.length()) == ':' && url.charAt(transport.length() + 1) != '/')
+ {
+ //Then most likely we have a host:port value
+ connection = new URI(DEFAULT_TRANSPORT + "://" + url);
+ transport = connection.getScheme();
+ }
+ else
+ {
+ throw URLHelper.parseError(0, transport.length(), "Unknown transport", url);
+ }
+ }
+ }
+ }
+ else
+ {
+ //Default the transport
+ connection = new URI(DEFAULT_TRANSPORT + "://" + url);
+ transport = connection.getScheme();
+ }
+
+ if (transport == null)
+ {
+ throw URLHelper.parseError(-1, "Unknown transport:'" + transport + "'" +
+ " In broker URL:'" + url + "' Format: " + URL_FORMAT_EXAMPLE, "");
+ }
+
+ setTransport(transport);
+
+ String host = connection.getHost();
+
+ // Fix for Java 1.5
+ if (host == null)
+ {
+ host = "";
+ }
+
+ setHost(host);
+
+ int port = connection.getPort();
+
+ if (port == -1)
+ {
+ // Fix for when there is port data but it is not automatically parseable by getPort().
+ String auth = connection.getAuthority();
+
+ if (auth != null && auth.contains(":"))
+ {
+ int start = auth.indexOf(":") + 1;
+ int end = start;
+ boolean looking = true;
+ boolean found = false;
+ //Walk the authority looking for a port value.
+ while (looking)
+ {
+ try
+ {
+ end++;
+ Integer.parseInt(auth.substring(start, end));
+
+ if (end >= auth.length())
+ {
+ looking = false;
+ found = true;
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ looking = false;
+ }
+
+ }
+ if (found)
+ {
+ setPort(Integer.parseInt(auth.substring(start, end)));
+ }
+ else
+ {
+ throw URLHelper.parseError(connection.toString().indexOf(connection.getAuthority()) + end - 1,
+ "Illegal character in port number", connection.toString());
+ }
+
+ }
+ else
+ {
+ setPort(DEFAULT_PORT);
+ }
+ }
+ else
+ {
+ setPort(port);
+ }
+
+ String queryString = connection.getQuery();
+
+ URLHelper.parseOptions(_options, queryString);
+
+ //Fragment is #string (not used)
+ }
+ catch (URISyntaxException uris)
+ {
+ if (uris instanceof URLSyntaxException)
+ {
+ throw(URLSyntaxException) uris;
+ }
+
+ throw URLHelper.parseError(uris.getIndex(), uris.getReason(), uris.getInput());
+ }
+ }
+
+ public AMQBrokerDetails(String host, int port, SSLConfiguration sslConfiguration)
+ {
+ _host = host;
+ _port = port;
+ _sslConfiguration = sslConfiguration;
+ }
+
+ public String getHost()
+ {
+ return _host;
+ }
+
+ public void setHost(String _host)
+ {
+ this._host = _host;
+ }
+
+ public int getPort()
+ {
+ return _port;
+ }
+
+ public void setPort(int _port)
+ {
+ this._port = _port;
+ }
+
+ public String getTransport()
+ {
+ return _transport;
+ }
+
+ public void setTransport(String _transport)
+ {
+ this._transport = _transport;
+ }
+
+
+ public String getOption(String key)
+ {
+ return _options.get(key);
+ }
+
+ public void setOption(String key, String value)
+ {
+ _options.put(key, value);
+ }
+
+ public long getTimeout()
+ {
+ if (_options.containsKey(OPTIONS_CONNECT_TIMEOUT))
+ {
+ try
+ {
+ return Long.parseLong(_options.get(OPTIONS_CONNECT_TIMEOUT));
+ }
+ catch (NumberFormatException nfe)
+ {
+ //Do nothing as we will use the default below.
+ }
+ }
+
+ return BrokerDetails.DEFAULT_CONNECT_TIMEOUT;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ setOption(OPTIONS_CONNECT_TIMEOUT, Long.toString(timeout));
+ }
+
+ public SSLConfiguration getSSLConfiguration()
+ {
+ return _sslConfiguration;
+ }
+
+ public void setSSLConfiguration(SSLConfiguration sslConfig)
+ {
+ _sslConfiguration = sslConfig;
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(_transport);
+ sb.append("://");
+
+ if (!(_transport.equalsIgnoreCase("vm")))
+ {
+ sb.append(_host);
+ }
+
+ sb.append(':');
+ sb.append(_port);
+
+ sb.append(printOptionsURL());
+
+ return sb.toString();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof BrokerDetails))
+ {
+ return false;
+ }
+
+ BrokerDetails bd = (BrokerDetails) o;
+
+ return _host.equalsIgnoreCase(bd.getHost()) &&
+ (_port == bd.getPort()) &&
+ _transport.equalsIgnoreCase(bd.getTransport()) &&
+ compareSSLConfigurations(bd.getSSLConfiguration());
+ //todo do we need to compare all the options as well?
+ }
+
+ private String printOptionsURL()
+ {
+ StringBuffer optionsURL = new StringBuffer();
+
+ optionsURL.append('?');
+
+ if (!(_options.isEmpty()))
+ {
+
+ for (String key : _options.keySet())
+ {
+ optionsURL.append(key);
+
+ optionsURL.append("='");
+
+ optionsURL.append(_options.get(key));
+
+ optionsURL.append("'");
+
+ optionsURL.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ }
+ }
+
+ //removeKey the extra DEFAULT_OPTION_SEPERATOR or the '?' if there are no options
+ optionsURL.deleteCharAt(optionsURL.length() - 1);
+
+ return optionsURL.toString();
+ }
+
+ // Do we need to do a more in-depth comparison?
+ private boolean compareSSLConfigurations(SSLConfiguration other)
+ {
+ boolean retval = false;
+ if (_sslConfiguration == null &&
+ other == null)
+ {
+ retval = true;
+ }
+ else if (_sslConfiguration != null &&
+ other != null)
+ {
+ retval = true;
+ }
+
+ return retval;
+ }
+
+ public static String checkTransport(String broker)
+ {
+ if ((!broker.contains("://")))
+ {
+ return "tcp://" + broker;
+ }
+ else
+ {
+ return broker;
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
new file mode 100644
index 0000000000..9abc94b3df
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
@@ -0,0 +1,1289 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.AMQConnectionFailureException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQUndeliveredException;
+import org.apache.qpid.AMQUnresolvedAddressException;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.client.failover.FailoverProtectedOperation;
+import org.apache.qpid.client.failover.FailoverRetrySupport;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicQosBody;
+import org.apache.qpid.framing.BasicQosOkBody;
+import org.apache.qpid.framing.ChannelOpenBody;
+import org.apache.qpid.framing.ChannelOpenOkBody;
+import org.apache.qpid.framing.TxSelectBody;
+import org.apache.qpid.framing.TxSelectOkBody;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.jms.ChannelLimitReachedException;
+import org.apache.qpid.jms.Connection;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.jms.FailoverPolicy;
+import org.apache.qpid.url.URLSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.ConnectionConsumer;
+import javax.jms.ConnectionMetaData;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueSession;
+import javax.jms.ServerSessionPool;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicSession;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.StringRefAddr;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.nio.channels.UnresolvedAddressException;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AMQConnection extends Closeable implements Connection, QueueConnection, TopicConnection, Referenceable
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AMQConnection.class);
+
+ private AtomicInteger _idFactory = new AtomicInteger(0);
+
+ /**
+ * This is the "root" mutex that must be held when doing anything that could be impacted by failover. This must be
+ * held by any child objects of this connection such as the session, producers and consumers.
+ */
+ private final Object _failoverMutex = new Object();
+
+ private final Object _sessionCreationLock = new Object();
+
+ /**
+ * A channel is roughly analogous to a session. The server can negotiate the maximum number of channels per session
+ * and we must prevent the client from opening too many. Zero means unlimited.
+ */
+ private long _maximumChannelCount;
+
+ /** The maximum size of frame supported by the server */
+ private long _maximumFrameSize;
+
+ /**
+ * The protocol handler dispatches protocol events for this connection. For example, when the connection is dropped
+ * the handler deals with this. It also deals with the initial dispatch of any protocol frames to their appropriate
+ * handler.
+ */
+ private AMQProtocolHandler _protocolHandler;
+
+ /** Maps from session id (Integer) to AMQSession instance */
+ private final Map<Integer, AMQSession> _sessions = new LinkedHashMap<Integer, AMQSession>();
+
+ private String _clientName;
+
+ /** The user name to use for authentication */
+ private String _username;
+
+ /** The password to use for authentication */
+ private String _password;
+
+ /** The virtual path to connect to on the AMQ server */
+ private String _virtualHost;
+
+ private ExceptionListener _exceptionListener;
+
+ private ConnectionListener _connectionListener;
+
+ private ConnectionURL _connectionURL;
+
+ /**
+ * Whether this connection is started, i.e. whether messages are flowing to consumers. It has no meaning for message
+ * publication.
+ */
+ private boolean _started;
+
+ /** Policy dictating how to failover */
+ private FailoverPolicy _failoverPolicy;
+
+ /*
+ * _Connected should be refactored with a suitable wait object.
+ */
+ private boolean _connected;
+
+ /*
+ * The last error code that occured on the connection. Used to return the correct exception to the client
+ */
+ private AMQException _lastAMQException = null;
+
+ /*
+ * The connection meta data
+ */
+ private QpidConnectionMetaData _connectionMetaData;
+
+ /** Configuration info for SSL */
+ private SSLConfiguration _sslConfiguration;
+
+ private AMQShortString _defaultTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
+ private AMQShortString _defaultQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
+ private AMQShortString _temporaryTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME;
+ private AMQShortString _temporaryQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME;
+
+ /** Thread Pool for executing connection level processes. Such as returning bounced messages. */
+ private final ExecutorService _taskPool = Executors.newCachedThreadPool();
+ private static final long DEFAULT_TIMEOUT = 1000 * 30;
+
+ /**
+ * @param broker brokerdetails
+ * @param username username
+ * @param password password
+ * @param clientName clientid
+ * @param virtualHost virtualhost
+ *
+ * @throws AMQException
+ * @throws URLSyntaxException
+ */
+ public AMQConnection(String broker, String username, String password, String clientName, String virtualHost)
+ throws AMQException, URLSyntaxException
+ {
+ this(new AMQConnectionURL(
+ ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@"
+ + ((clientName == null) ? "" : clientName) + "/" + virtualHost + "?brokerlist='"
+ + AMQBrokerDetails.checkTransport(broker) + "'"), null);
+ }
+
+ /**
+ * @param broker brokerdetails
+ * @param username username
+ * @param password password
+ * @param clientName clientid
+ * @param virtualHost virtualhost
+ *
+ * @throws AMQException
+ * @throws URLSyntaxException
+ */
+ public AMQConnection(String broker, String username, String password, String clientName, String virtualHost,
+ SSLConfiguration sslConfig) throws AMQException, URLSyntaxException
+ {
+ this(new AMQConnectionURL(
+ ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@"
+ + ((clientName == null) ? "" : clientName) + "/" + virtualHost + "?brokerlist='"
+ + AMQBrokerDetails.checkTransport(broker) + "'"), sslConfig);
+ }
+
+ public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost)
+ throws AMQException, URLSyntaxException
+ {
+ this(host, port, false, username, password, clientName, virtualHost, null);
+ }
+
+ public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost,
+ SSLConfiguration sslConfig) throws AMQException, URLSyntaxException
+ {
+ this(host, port, false, username, password, clientName, virtualHost, sslConfig);
+ }
+
+ public AMQConnection(String host, int port, boolean useSSL, String username, String password, String clientName,
+ String virtualHost, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException
+ {
+ this(new AMQConnectionURL(
+ useSSL
+ ? (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@"
+ + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port
+ + "'" + "," + ConnectionURL.OPTIONS_SSL + "='true'")
+ : (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@"
+ + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port
+ + "'" + "," + ConnectionURL.OPTIONS_SSL + "='false'")), sslConfig);
+ }
+
+ public AMQConnection(String connection) throws AMQException, URLSyntaxException
+ {
+ this(new AMQConnectionURL(connection), null);
+ }
+
+ public AMQConnection(String connection, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException
+ {
+ this(new AMQConnectionURL(connection), sslConfig);
+ }
+
+ public AMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Connection:" + connectionURL);
+ }
+
+ _sslConfiguration = sslConfig;
+ if (connectionURL == null)
+ {
+ throw new IllegalArgumentException("Connection must be specified");
+ }
+
+ _connectionURL = connectionURL;
+
+ _clientName = connectionURL.getClientName();
+ _username = connectionURL.getUsername();
+ _password = connectionURL.getPassword();
+ setVirtualHost(connectionURL.getVirtualHost());
+
+ if (connectionURL.getDefaultQueueExchangeName() != null)
+ {
+ _defaultQueueExchangeName = connectionURL.getDefaultQueueExchangeName();
+ }
+
+ if (connectionURL.getDefaultTopicExchangeName() != null)
+ {
+ _defaultTopicExchangeName = connectionURL.getDefaultTopicExchangeName();
+ }
+
+ if (connectionURL.getTemporaryQueueExchangeName() != null)
+ {
+ _temporaryQueueExchangeName = connectionURL.getTemporaryQueueExchangeName();
+ }
+
+ if (connectionURL.getTemporaryTopicExchangeName() != null)
+ {
+ _temporaryTopicExchangeName = connectionURL.getTemporaryTopicExchangeName();
+ }
+
+ _failoverPolicy = new FailoverPolicy(connectionURL);
+
+ _protocolHandler = new AMQProtocolHandler(this);
+
+ // We are not currently connected
+ _connected = false;
+
+ Exception lastException = new Exception();
+ lastException.initCause(new ConnectException());
+
+ while (!_connected && _failoverPolicy.failoverAllowed())
+ {
+ try
+ {
+ makeBrokerConnection(_failoverPolicy.getNextBrokerDetails());
+ lastException = null;
+ _connected = true;
+ }
+ catch (Exception e)
+ {
+ lastException = e;
+
+ //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 can be seen when a exception occurs during connection. i.e. log4j NoSuchMethod. (using < 1.2.12)
+ _protocolHandler.setStateManager(new AMQStateManager());
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Unable to connect to broker at " + _failoverPolicy.getCurrentBrokerDetails(),
+ e.getCause());
+ }
+ }
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Are we connected:" + _connected);
+ }
+
+ if (!_connected)
+ {
+ String message = null;
+
+ if (lastException != null)
+ {
+ if (lastException.getCause() != null)
+ {
+ message = lastException.getCause().getMessage();
+ }
+ else
+ {
+ message = lastException.getMessage();
+ }
+ }
+
+ if ((message == null) || message.equals(""))
+ {
+ if (message == null)
+ {
+ message = "Unable to Connect";
+ }
+ else // can only be "" if getMessage() returned it therfore lastException != null
+ {
+ message = "Unable to Connect:" + lastException.getClass();
+ }
+ }
+
+ AMQException e = new AMQConnectionFailureException(message);
+
+ if (lastException != null)
+ {
+ if (lastException instanceof UnresolvedAddressException)
+ {
+ e = new AMQUnresolvedAddressException(message, _failoverPolicy.getCurrentBrokerDetails().toString());
+ }
+
+ e.initCause(lastException);
+ }
+
+ throw e;
+ }
+
+ _connectionMetaData = new QpidConnectionMetaData(this);
+ }
+
+ protected boolean checkException(Throwable thrown)
+ {
+ Throwable cause = thrown.getCause();
+
+ if (cause == null)
+ {
+ cause = thrown;
+ }
+
+ return ((cause instanceof ConnectException) || (cause instanceof UnresolvedAddressException));
+ }
+
+ protected AMQConnection(String username, String password, String clientName, String virtualHost)
+ {
+ _clientName = clientName;
+ _username = username;
+ _password = password;
+ setVirtualHost(virtualHost);
+ }
+
+ private void setVirtualHost(String virtualHost)
+ {
+ if (virtualHost.startsWith("/"))
+ {
+ virtualHost = virtualHost.substring(1);
+ }
+
+ _virtualHost = virtualHost;
+ }
+
+ private void makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException
+ {
+ 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);
+ _failoverPolicy.attainedConnection();
+
+ // Again this should be changed to a suitable notify
+ _connected = true;
+ }
+ catch (AMQException e)
+ {
+ _lastAMQException = e;
+ throw e;
+ }
+ }
+
+ public boolean attemptReconnection(String host, int port)
+ {
+ BrokerDetails bd = new AMQBrokerDetails(host, port, _sslConfiguration);
+
+ _failoverPolicy.setBroker(bd);
+
+ try
+ {
+ makeBrokerConnection(bd);
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Unable to connect to broker at " + bd);
+ }
+
+ attemptReconnection();
+ }
+
+ return false;
+ }
+
+ public boolean attemptReconnection()
+ {
+ while (_failoverPolicy.failoverAllowed())
+ {
+ try
+ {
+ makeBrokerConnection(_failoverPolicy.getNextBrokerDetails());
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ if (!(e instanceof AMQException))
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Unable to connect to broker at " + _failoverPolicy.getCurrentBrokerDetails(), e);
+ }
+ }
+ else
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info(e.getMessage() + ":Unable to connect to broker at "
+ + _failoverPolicy.getCurrentBrokerDetails());
+ }
+ }
+ }
+ }
+
+ // connection unsuccessful
+ return false;
+ }
+
+ /**
+ * Get the details of the currently active broker
+ *
+ * @return null if no broker is active (i.e. no successful connection has been made, or the BrokerDetail instance
+ * otherwise
+ */
+ public BrokerDetails getActiveBrokerDetails()
+ {
+ return _failoverPolicy.getCurrentBrokerDetails();
+ }
+
+ public boolean failoverAllowed()
+ {
+ if (!_connected)
+ {
+ return false;
+ }
+ else
+ {
+ return _failoverPolicy.failoverAllowed();
+ }
+ }
+
+ public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode) throws JMSException
+ {
+ return createSession(transacted, acknowledgeMode, AMQSession.DEFAULT_PREFETCH_HIGH_MARK);
+ }
+
+ public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetch)
+ throws JMSException
+ {
+ return createSession(transacted, acknowledgeMode, prefetch, prefetch);
+ }
+
+ public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode,
+ final int prefetchHigh, final int prefetchLow) throws JMSException
+ {
+ synchronized(_sessionCreationLock)
+ {
+ checkNotClosed();
+
+ 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
+ {
+ int channelId = _idFactory.incrementAndGet();
+
+ if (_logger.isDebugEnabled())
+ {
+ _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);
+
+ 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)
+ {
+ deregisterSession(channelId);
+ }
+ }
+
+ if (_started)
+ {
+ try
+ {
+ session.start();
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException(e);
+ }
+ }
+
+ return session;
+ }
+ }, this).execute();
+ }
+ }
+
+ private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted)
+ throws AMQException, FailoverException
+ {
+
+ // TODO: Be aware of possible changes to parameter order as versions change.
+
+ _protocolHandler.syncWrite(ChannelOpenBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(),
+ _protocolHandler.getProtocolMinorVersion(), null), // outOfBand
+ ChannelOpenOkBody.class);
+
+ // todo send low water mark when protocol allows.
+ // todo Be aware of possible changes to parameter order as versions change.
+ _protocolHandler.syncWrite(BasicQosBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(),
+ _protocolHandler.getProtocolMinorVersion(), false, // global
+ prefetchHigh, // prefetchCount
+ 0), // prefetchSize
+ BasicQosOkBody.class);
+
+ if (transacted)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Issuing TxSelect for " + channelId);
+ }
+
+ // TODO: Be aware of possible changes to parameter order as versions change.
+ _protocolHandler.syncWrite(TxSelectBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(),
+ _protocolHandler.getProtocolMinorVersion()), TxSelectOkBody.class);
+ }
+ }
+
+ private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted)
+ throws AMQException, FailoverException
+ {
+ try
+ {
+ createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted);
+ }
+ catch (AMQException e)
+ {
+ deregisterSession(channelId);
+ throw new AMQException("Error reopening channel " + channelId + " after failover: " + e, e);
+ }
+ }
+
+ public void setFailoverPolicy(FailoverPolicy policy)
+ {
+ _failoverPolicy = policy;
+ }
+
+ public FailoverPolicy getFailoverPolicy()
+ {
+ return _failoverPolicy;
+ }
+
+ /**
+ * Returns an AMQQueueSessionAdaptor which wraps an AMQSession and throws IllegalStateExceptions where specified in
+ * the JMS spec
+ *
+ * @param transacted
+ * @param acknowledgeMode
+ *
+ * @return QueueSession
+ *
+ * @throws JMSException
+ */
+ public QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException
+ {
+ return new AMQQueueSessionAdaptor(createSession(transacted, acknowledgeMode));
+ }
+
+ /**
+ * Returns an AMQTopicSessionAdapter which wraps an AMQSession and throws IllegalStateExceptions where specified in
+ * the JMS spec
+ *
+ * @param transacted
+ * @param acknowledgeMode
+ *
+ * @return TopicSession
+ *
+ * @throws JMSException
+ */
+ public TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException
+ {
+ return new AMQTopicSessionAdaptor(createSession(transacted, acknowledgeMode));
+ }
+
+ private boolean channelLimitReached()
+ {
+ return (_maximumChannelCount != 0) && (_sessions.size() == _maximumChannelCount);
+ }
+
+ public String getClientID() throws JMSException
+ {
+ checkNotClosed();
+
+ return _clientName;
+ }
+
+ public void setClientID(String clientID) throws JMSException
+ {
+ checkNotClosed();
+ // in AMQP it is not possible to change the client ID. If one is not specified
+ // upon connection construction, an id is generated automatically. Therefore
+ // we can always throw an exception.
+ throw new IllegalStateException("Client name cannot be changed after being set");
+ }
+
+ public ConnectionMetaData getMetaData() throws JMSException
+ {
+ checkNotClosed();
+
+ return _connectionMetaData;
+
+ }
+
+ public ExceptionListener getExceptionListener() throws JMSException
+ {
+ checkNotClosed();
+
+ return _exceptionListener;
+ }
+
+ public void setExceptionListener(ExceptionListener listener) throws JMSException
+ {
+ checkNotClosed();
+ _exceptionListener = listener;
+ }
+
+ /**
+ * Start the connection, i.e. start flowing messages. Note that this method must be called only from a single thread
+ * and is not thread safe (which is legal according to the JMS specification).
+ *
+ * @throws JMSException
+ */
+ public void start() throws JMSException
+ {
+ checkNotClosed();
+ if (!_started)
+ {
+ final Iterator it = _sessions.entrySet().iterator();
+ while (it.hasNext())
+ {
+ final AMQSession s = (AMQSession) ((Map.Entry) it.next()).getValue();
+ try
+ {
+ s.start();
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException(e);
+ }
+ }
+
+ _started = true;
+ }
+ }
+
+ public void stop() throws JMSException
+ {
+ checkNotClosed();
+ if (_started)
+ {
+ for (Iterator i = _sessions.values().iterator(); i.hasNext();)
+ {
+ try
+ {
+ ((AMQSession) i.next()).stop();
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException(e);
+ }
+ }
+
+ _started = false;
+ }
+ }
+
+ public void close() throws JMSException
+ {
+ close(DEFAULT_TIMEOUT);
+ }
+
+ public void close(long timeout) throws JMSException
+ {
+ close(new ArrayList<AMQSession>(_sessions.values()),timeout);
+ }
+
+ public void close(List<AMQSession> sessions, long timeout) throws JMSException
+ {
+ synchronized(_sessionCreationLock)
+ {
+ if(!sessions.isEmpty())
+ {
+ AMQSession session = sessions.remove(0);
+ synchronized(session.getMessageDeliveryLock())
+ {
+ close(sessions, timeout);
+ }
+ }
+ else
+ {
+ synchronized (getFailoverMutex())
+ {
+ if (!_closed.getAndSet(true))
+ {
+ try
+ {
+ long startCloseTime = System.currentTimeMillis();
+
+ _taskPool.shutdown();
+ closeAllSessions(null, timeout, startCloseTime);
+
+ if (!_taskPool.isTerminated())
+ {
+ try
+ {
+ // adjust timeout
+ long taskPoolTimeout = adjustTimeout(timeout, startCloseTime);
+
+ _taskPool.awaitTermination(taskPoolTimeout, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ _logger.info("Interrupted while shutting down connection thread pool.");
+ }
+ }
+
+ // adjust timeout
+ timeout = adjustTimeout(timeout, startCloseTime);
+
+ _protocolHandler.closeConnection(timeout);
+
+ //If the taskpool hasn't shutdown by now then give it shutdownNow.
+ // This will interupt any running tasks.
+ if (!_taskPool.isTerminated())
+ {
+ List<Runnable> tasks = _taskPool.shutdownNow();
+ for (Runnable r : tasks)
+ {
+ _logger.warn("Connection close forced taskpool to prevent execution:" + r);
+ }
+ }
+ }
+ catch (AMQException e)
+ {
+ JMSException jmse = new JMSException("Error closing connection: " + e);
+ jmse.setLinkedException(e);
+ throw jmse;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private long adjustTimeout(long timeout, long startTime)
+ {
+ long now = System.currentTimeMillis();
+ timeout -= now - startTime;
+ if (timeout < 0)
+ {
+ timeout = 0;
+ }
+
+ return timeout;
+ }
+
+ /**
+ * Marks all sessions and their children as closed without sending any protocol messages. Useful when you need to
+ * mark objects "visible" in userland as closed after failover or other significant event that impacts the
+ * connection. <p/> The caller must hold the failover mutex before calling this method.
+ */
+ private void markAllSessionsClosed()
+ {
+ final LinkedList sessionCopy = new LinkedList(_sessions.values());
+ final Iterator it = sessionCopy.iterator();
+ while (it.hasNext())
+ {
+ final AMQSession session = (AMQSession) it.next();
+
+ session.markClosed();
+ }
+
+ _sessions.clear();
+ }
+
+ /**
+ * Close all the sessions, either due to normal connection closure or due to an error occurring.
+ *
+ * @param cause if not null, the error that is causing this shutdown <p/> The caller must hold the failover mutex
+ * before calling this method.
+ */
+ private void closeAllSessions(Throwable cause, long timeout, long starttime) throws JMSException
+ {
+ final LinkedList sessionCopy = new LinkedList(_sessions.values());
+ final Iterator it = sessionCopy.iterator();
+ JMSException sessionException = null;
+ while (it.hasNext())
+ {
+ final AMQSession session = (AMQSession) it.next();
+ if (cause != null)
+ {
+ session.closed(cause);
+ }
+ else
+ {
+ try
+ {
+ if (starttime != -1)
+ {
+ timeout = adjustTimeout(timeout, starttime);
+ }
+
+ session.close(timeout);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error closing session: " + e);
+ sessionException = e;
+ }
+ }
+ }
+
+ _sessions.clear();
+ if (sessionException != null)
+ {
+ throw sessionException;
+ }
+ }
+
+ public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector,
+ ServerSessionPool sessionPool, int maxMessages) throws JMSException
+ {
+ checkNotClosed();
+
+ return null;
+ }
+
+ public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector, ServerSessionPool sessionPool,
+ int maxMessages) throws JMSException
+ {
+ checkNotClosed();
+
+ return null;
+ }
+
+ public ConnectionConsumer createConnectionConsumer(Topic topic, String messageSelector, ServerSessionPool sessionPool,
+ int maxMessages) throws JMSException
+ {
+ checkNotClosed();
+
+ return null;
+ }
+
+ public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector,
+ ServerSessionPool sessionPool, int maxMessages) throws JMSException
+ {
+ // TODO Auto-generated method stub
+ checkNotClosed();
+
+ return null;
+ }
+
+ public long getMaximumChannelCount() throws JMSException
+ {
+ checkNotClosed();
+
+ return _maximumChannelCount;
+ }
+
+ public void setConnectionListener(ConnectionListener listener)
+ {
+ _connectionListener = listener;
+ }
+
+ public ConnectionListener getConnectionListener()
+ {
+ return _connectionListener;
+ }
+
+ public void setMaximumChannelCount(long maximumChannelCount)
+ {
+ _maximumChannelCount = maximumChannelCount;
+ }
+
+ public void setMaximumFrameSize(long frameMax)
+ {
+ _maximumFrameSize = frameMax;
+ }
+
+ public long getMaximumFrameSize()
+ {
+ return _maximumFrameSize;
+ }
+
+ public Map getSessions()
+ {
+ return _sessions;
+ }
+
+ public String getUsername()
+ {
+ return _username;
+ }
+
+ public String getPassword()
+ {
+ return _password;
+ }
+
+ public String getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public AMQProtocolHandler getProtocolHandler()
+ {
+ return _protocolHandler;
+ }
+
+ public boolean started()
+ {
+ return _started;
+ }
+
+ public void bytesSent(long writtenBytes)
+ {
+ if (_connectionListener != null)
+ {
+ _connectionListener.bytesSent(writtenBytes);
+ }
+ }
+
+ public void bytesReceived(long receivedBytes)
+ {
+ if (_connectionListener != null)
+ {
+ _connectionListener.bytesReceived(receivedBytes);
+ }
+ }
+
+ /**
+ * Fire the preFailover event to the registered connection listener (if any)
+ *
+ * @param redirect true if this is the result of a redirect request rather than a connection error
+ *
+ * @return true if no listener or listener does not veto change
+ */
+ public boolean firePreFailover(boolean redirect)
+ {
+ boolean proceed = true;
+ if (_connectionListener != null)
+ {
+ proceed = _connectionListener.preFailover(redirect);
+ }
+
+ return proceed;
+ }
+
+ /**
+ * Fire the preResubscribe event to the registered connection listener (if any). If the listener vetoes
+ * resubscription then all the sessions are closed.
+ *
+ * @return true if no listener or listener does not veto resubscription.
+ *
+ * @throws JMSException
+ */
+ public boolean firePreResubscribe() throws JMSException
+ {
+ if (_connectionListener != null)
+ {
+ boolean resubscribe = _connectionListener.preResubscribe();
+ if (!resubscribe)
+ {
+ markAllSessionsClosed();
+ }
+
+ return resubscribe;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ /** Fires a failover complete event to the registered connection listener (if any). */
+ public void fireFailoverComplete()
+ {
+ if (_connectionListener != null)
+ {
+ _connectionListener.failoverComplete();
+ }
+ }
+
+ /**
+ * In order to protect the consistency of the connection and its child sessions, consumers and producers, the
+ * "failover mutex" must be held when doing any operations that could be corrupted during failover.
+ *
+ * @return a mutex. Guaranteed never to change for the lifetime of this connection even if failover occurs.
+ */
+ public final Object getFailoverMutex()
+ {
+ return _failoverMutex;
+ }
+
+ /**
+ * If failover is taking place this will block until it has completed. If failover is not taking place it will
+ * return immediately.
+ *
+ * @throws InterruptedException
+ */
+ public void blockUntilNotFailingOver() throws InterruptedException
+ {
+ _protocolHandler.blockUntilNotFailingOver();
+ }
+
+ /**
+ * Invoked by the AMQProtocolSession when a protocol session exception has occurred. This method sends the exception
+ * to a JMS exception listener, if configured, and propagates the exception to sessions, which in turn will
+ * propagate to consumers. This allows synchronous consumers to have exceptions thrown to them.
+ *
+ * @param cause the exception
+ */
+ public void exceptionReceived(Throwable cause)
+ {
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("exceptionReceived done by:" + Thread.currentThread().getName(), cause);
+ }
+
+ final JMSException je;
+ if (cause instanceof JMSException)
+ {
+ je = (JMSException) cause;
+ }
+ else
+ {
+ if (cause instanceof AMQException)
+ {
+ je =
+ new JMSException(Integer.toString(((AMQException) cause).getErrorCode().getCode()),
+ "Exception thrown against " + toString() + ": " + cause);
+ }
+ else
+ {
+ je = new JMSException("Exception thrown against " + toString() + ": " + cause);
+ }
+
+ if (cause instanceof Exception)
+ {
+ je.setLinkedException((Exception) cause);
+ }
+ }
+
+ // in the case of an IOException, MINA has closed the protocol session so we set _closed to true
+ // so that any generic client code that tries to close the connection will not mess up this error
+ // handling sequence
+ if (cause instanceof IOException)
+ {
+ _closed.set(true);
+ }
+
+ if (_exceptionListener != null)
+ {
+ _exceptionListener.onException(je);
+ }
+ else
+ {
+ _logger.error("Throwable Received but no listener set: " + cause.getMessage());
+ }
+
+ if (!(cause instanceof AMQUndeliveredException) && !(cause instanceof AMQAuthenticationException))
+ {
+ try
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Closing AMQConnection due to :" + cause.getMessage());
+ }
+
+ _closed.set(true);
+ closeAllSessions(cause, -1, -1); // FIXME: when doing this end up with RejectedExecutionException from executor.
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error closing all sessions: " + e, e);
+ }
+
+ }
+ else
+ {
+ _logger.info("Not a hard-error connection not closing: " + cause.getMessage());
+ }
+ }
+
+ void registerSession(int channelId, AMQSession session)
+ {
+ _sessions.put(channelId, session);
+ }
+
+ void deregisterSession(int channelId)
+ {
+ _sessions.remove(channelId);
+ }
+
+ /**
+ * For all sessions, and for all consumers in those sessions, resubscribe. This is called during failover handling.
+ * The caller must hold the failover mutex before calling this method.
+ */
+ public void resubscribeSessions() throws JMSException, AMQException, FailoverException
+ {
+ ArrayList sessions = new ArrayList(_sessions.values());
+ _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey?
+ for (Iterator it = sessions.iterator(); it.hasNext();)
+ {
+ AMQSession s = (AMQSession) it.next();
+ // _protocolHandler.addSessionByChannel(s.getChannelId(), s);
+ reopenChannel(s.getChannelId(), s.getDefaultPrefetchHigh(), s.getDefaultPrefetchLow(), s.getTransacted());
+ s.resubscribe();
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer("AMQConnection:\n");
+ if (_failoverPolicy.getCurrentBrokerDetails() == null)
+ {
+ buf.append("No active broker connection");
+ }
+ else
+ {
+ BrokerDetails bd = _failoverPolicy.getCurrentBrokerDetails();
+ buf.append("Host: ").append(String.valueOf(bd.getHost()));
+ buf.append("\nPort: ").append(String.valueOf(bd.getPort()));
+ }
+
+ buf.append("\nVirtual Host: ").append(String.valueOf(_virtualHost));
+ buf.append("\nClient ID: ").append(String.valueOf(_clientName));
+ buf.append("\nActive session count: ").append((_sessions == null) ? 0 : _sessions.size());
+
+ return buf.toString();
+ }
+
+ public String toURL()
+ {
+ return _connectionURL.toString();
+ }
+
+ public Reference getReference() throws NamingException
+ {
+ return new Reference(AMQConnection.class.getName(), new StringRefAddr(AMQConnection.class.getName(), toURL()),
+ AMQConnectionFactory.class.getName(), null); // factory location
+ }
+
+ public SSLConfiguration getSSLConfiguration()
+ {
+ return _sslConfiguration;
+ }
+
+ public AMQShortString getDefaultTopicExchangeName()
+ {
+ return _defaultTopicExchangeName;
+ }
+
+ public void setDefaultTopicExchangeName(AMQShortString defaultTopicExchangeName)
+ {
+ _defaultTopicExchangeName = defaultTopicExchangeName;
+ }
+
+ public AMQShortString getDefaultQueueExchangeName()
+ {
+ return _defaultQueueExchangeName;
+ }
+
+ public void setDefaultQueueExchangeName(AMQShortString defaultQueueExchangeName)
+ {
+ _defaultQueueExchangeName = defaultQueueExchangeName;
+ }
+
+ public AMQShortString getTemporaryTopicExchangeName()
+ {
+ return _temporaryTopicExchangeName;
+ }
+
+ public AMQShortString getTemporaryQueueExchangeName()
+ {
+ return _temporaryQueueExchangeName; // To change body of created methods use File | Settings | File Templates.
+ }
+
+ public void setTemporaryTopicExchangeName(AMQShortString temporaryTopicExchangeName)
+ {
+ _temporaryTopicExchangeName = temporaryTopicExchangeName;
+ }
+
+ public void setTemporaryQueueExchangeName(AMQShortString temporaryQueueExchangeName)
+ {
+ _temporaryQueueExchangeName = temporaryQueueExchangeName;
+ }
+
+ public void performConnectionTask(Runnable task)
+ {
+ _taskPool.execute(task);
+ }
+
+ public AMQSession getSession(int channelId)
+ {
+ return _sessions.get(channelId);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java
new file mode 100644
index 0000000000..7c0803a61a
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java
@@ -0,0 +1,409 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Hashtable;
+import java.util.UUID;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.URLSyntaxException;
+
+
+public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, ObjectFactory, Referenceable
+{
+ private String _host;
+ private int _port;
+ private String _defaultUsername;
+ private String _defaultPassword;
+ private String _virtualPath;
+
+ private ConnectionURL _connectionDetails;
+ private SSLConfiguration _sslConfig;
+
+ public AMQConnectionFactory()
+ {
+ }
+
+ public AMQConnectionFactory(String url) throws URLSyntaxException
+ {
+ _connectionDetails = new AMQConnectionURL(url);
+ }
+
+ public AMQConnectionFactory(ConnectionURL url)
+ {
+ _connectionDetails = url;
+ }
+
+ public AMQConnectionFactory(String broker, String username, String password,
+ String clientName, String virtualHost) throws URLSyntaxException
+ {
+ this(new AMQConnectionURL(ConnectionURL.AMQ_PROTOCOL + "://" +
+ username + ":" + password + "@" + clientName + "/" +
+ virtualHost + "?brokerlist='" + broker + "'"));
+ }
+
+ public AMQConnectionFactory(String host, int port, String virtualPath)
+ {
+ this(host, port, "guest", "guest", virtualPath);
+ }
+
+ public AMQConnectionFactory(String host, int port, String defaultUsername, String defaultPassword,
+ String virtualPath)
+ {
+ _host = host;
+ _port = port;
+ _defaultUsername = defaultUsername;
+ _defaultPassword = defaultPassword;
+ _virtualPath = virtualPath;
+
+//todo when setting Host/Port has been resolved then we can use this otherwise those methods won't work with the following line.
+// _connectionDetails = new AMQConnectionURL(
+// ConnectionURL.AMQ_PROTOCOL + "://" +
+// _defaultUsername + ":" + _defaultPassword + "@" +
+// virtualPath + "?brokerlist='tcp://" + host + ":" + port + "'");
+ }
+
+ /**
+ * @return The _defaultPassword.
+ */
+ public final String getDefaultPassword(String password)
+ {
+ if (_connectionDetails != null)
+ {
+ return _connectionDetails.getPassword();
+ }
+ else
+ {
+ return _defaultPassword;
+ }
+ }
+
+ /**
+ * @param password The _defaultPassword to set.
+ */
+ public final void setDefaultPassword(String password)
+ {
+ if (_connectionDetails != null)
+ {
+ _connectionDetails.setPassword(password);
+ }
+ _defaultPassword = password;
+ }
+
+ /**
+ * Getter for SSLConfiguration
+ *
+ * @return SSLConfiguration if set, otherwise null
+ */
+ public final SSLConfiguration getSSLConfiguration()
+ {
+ return _sslConfig;
+ }
+
+ /**
+ * Setter for SSLConfiguration
+ *
+ * @param sslConfig config to store
+ */
+ public final void setSSLConfiguration(SSLConfiguration sslConfig)
+ {
+ _sslConfig = sslConfig;
+ }
+
+ /**
+ * @return The _defaultPassword.
+ */
+ public final String getDefaultUsername(String password)
+ {
+ if (_connectionDetails != null)
+ {
+ return _connectionDetails.getUsername();
+ }
+ else
+ {
+ return _defaultUsername;
+ }
+ }
+
+ /**
+ * @param username The _defaultUsername to set.
+ */
+ public final void setDefaultUsername(String username)
+ {
+ if (_connectionDetails != null)
+ {
+ _connectionDetails.setUsername(username);
+ }
+ _defaultUsername = username;
+ }
+
+ /**
+ * @return The _host .
+ */
+ public final String getHost()
+ {
+ //todo this doesn't make sense in a multi broker URL as we have no current as that is done by AMQConnection
+ return _host;
+ }
+
+ /**
+ * @param host The _host to set.
+ */
+ public final void setHost(String host)
+ {
+ //todo if _connectionDetails is set then run _connectionDetails.addBrokerDetails()
+ // Should perhaps have this method changed to setBroker(host,port)
+ _host = host;
+ }
+
+ /**
+ * @return _port The _port to set.
+ */
+ public final int getPort()
+ {
+ //todo see getHost
+ return _port;
+ }
+
+ /**
+ * @param port The port to set.
+ */
+ public final void setPort(int port)
+ {
+ //todo see setHost
+ _port = port;
+ }
+
+ /**
+ * @return he _virtualPath.
+ */
+ public final String getVirtualPath()
+ {
+ if (_connectionDetails != null)
+ {
+ return _connectionDetails.getVirtualHost();
+ }
+ else
+ {
+ return _virtualPath;
+ }
+ }
+
+ /**
+ * @param path The _virtualPath to set.
+ */
+ public final void setVirtualPath(String path)
+ {
+ if (_connectionDetails != null)
+ {
+ _connectionDetails.setVirtualHost(path);
+ }
+
+ _virtualPath = path;
+ }
+
+ static String getUniqueClientID()
+ {
+ try
+ {
+ InetAddress addr = InetAddress.getLocalHost();
+ return addr.getHostName() + System.currentTimeMillis();
+ }
+ catch (UnknownHostException e)
+ {
+ return "UnknownHost" + UUID.randomUUID();
+ }
+ }
+
+ public Connection createConnection() throws JMSException
+ {
+ try
+ {
+ if (_connectionDetails != null)
+ {
+ if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals(""))
+ {
+ _connectionDetails.setClientName(getUniqueClientID());
+ }
+ return new AMQConnection(_connectionDetails, _sslConfig);
+ }
+ else
+ {
+ return new AMQConnection(_host, _port, _defaultUsername, _defaultPassword, getUniqueClientID(),
+ _virtualPath);
+ }
+ }
+ catch (Exception e)
+ {
+ JMSException jmse = new JMSException("Error creating connection: " + e.getMessage());
+ jmse.setLinkedException(e);
+ throw jmse;
+ }
+
+
+ }
+
+ public Connection createConnection(String userName, String password) throws JMSException
+ {
+ try
+ {
+ if (_connectionDetails != null)
+ {
+ _connectionDetails.setUsername(userName);
+ _connectionDetails.setPassword(password);
+
+ if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals(""))
+ {
+ _connectionDetails.setClientName(getUniqueClientID());
+ }
+ return new AMQConnection(_connectionDetails, _sslConfig);
+ }
+ else
+ {
+ return new AMQConnection(_host, _port, userName, password, getUniqueClientID(), _virtualPath);
+ }
+ }
+ catch (Exception e)
+ {
+ JMSException jmse = new JMSException("Error creating connection: " + e.getMessage());
+ jmse.setLinkedException(e);
+ throw jmse;
+ }
+ }
+
+ public QueueConnection createQueueConnection() throws JMSException
+ {
+ return (QueueConnection) createConnection();
+ }
+
+ public QueueConnection createQueueConnection(String username, String password) throws JMSException
+ {
+ return (QueueConnection) createConnection(username, password);
+ }
+
+ public TopicConnection createTopicConnection() throws JMSException
+ {
+ return (TopicConnection) createConnection();
+ }
+
+ public TopicConnection createTopicConnection(String username, String password) throws JMSException
+ {
+ return (TopicConnection) createConnection(username, password);
+ }
+
+
+ public ConnectionURL getConnectionURL()
+ {
+ return _connectionDetails;
+ }
+
+ /**
+ * JNDI interface to create objects from References.
+ *
+ * @param obj The Reference from JNDI
+ * @param name
+ * @param ctx
+ * @param env
+ *
+ * @return AMQConnection,AMQTopic,AMQQueue, or AMQConnectionFactory.
+ *
+ * @throws Exception
+ */
+ public Object getObjectInstance(Object obj, Name name, Context ctx,
+ Hashtable env) throws Exception
+ {
+ if (obj instanceof Reference)
+ {
+ Reference ref = (Reference) obj;
+
+ if (ref.getClassName().equals(AMQConnection.class.getName()))
+ {
+ RefAddr addr = ref.get(AMQConnection.class.getName());
+
+ if (addr != null)
+ {
+ return new AMQConnection((String) addr.getContent());
+ }
+ }
+
+ if (ref.getClassName().equals(AMQQueue.class.getName()))
+ {
+ RefAddr addr = ref.get(AMQQueue.class.getName());
+
+ if (addr != null)
+ {
+ return new AMQQueue(new AMQBindingURL((String) addr.getContent()));
+ }
+ }
+
+ if (ref.getClassName().equals(AMQTopic.class.getName()))
+ {
+ RefAddr addr = ref.get(AMQTopic.class.getName());
+
+ if (addr != null)
+ {
+ return new AMQTopic(new AMQBindingURL((String) addr.getContent()));
+ }
+ }
+
+ if (ref.getClassName().equals(AMQConnectionFactory.class.getName()))
+ {
+ RefAddr addr = ref.get(AMQConnectionFactory.class.getName());
+
+ if (addr != null)
+ {
+ return new AMQConnectionFactory(new AMQConnectionURL((String) addr.getContent()));
+ }
+ }
+
+ }
+ return null;
+ }
+
+
+ public Reference getReference() throws NamingException
+ {
+ return new Reference(
+ AMQConnectionFactory.class.getName(),
+ new StringRefAddr(AMQConnectionFactory.class.getName(), _connectionDetails.getURL()),
+ AMQConnectionFactory.class.getName(),
+ null); // factory location
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java
new file mode 100644
index 0000000000..24f5ead2d0
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java
@@ -0,0 +1,455 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.url.URLHelper;
+import org.apache.qpid.url.URLSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class AMQConnectionURL implements ConnectionURL
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AMQConnectionURL.class);
+
+ private String _url;
+ private String _failoverMethod;
+ private HashMap<String, String> _failoverOptions;
+ private HashMap<String, String> _options;
+ private List<BrokerDetails> _brokers;
+ private String _clientName;
+ private String _username;
+ private String _password;
+ private String _virtualHost;
+ private AMQShortString _defaultQueueExchangeName;
+ private AMQShortString _defaultTopicExchangeName;
+ private AMQShortString _temporaryTopicExchangeName;
+ private AMQShortString _temporaryQueueExchangeName;
+
+ public AMQConnectionURL(String fullURL) throws URLSyntaxException
+ {
+ _url = fullURL;
+ _options = new HashMap<String, String>();
+ _brokers = new LinkedList<BrokerDetails>();
+ _failoverOptions = new HashMap<String, String>();
+
+ // Connection URL format
+ // amqp://[user:pass@][clientid]/virtualhost?brokerlist='tcp://host:port?option=\'value\',option=\'value\';vm://:3/virtualpath?option=\'value\'',failover='method?option=\'value\',option='value''"
+ // Options are of course optional except for requiring a single broker in the broker list.
+ try
+ {
+ URI connection = new URI(fullURL);
+
+ if ((connection.getScheme() == null) || !(connection.getScheme().equalsIgnoreCase(AMQ_PROTOCOL)))
+ {
+ throw new URISyntaxException(fullURL, "Not an AMQP URL");
+ }
+
+ if ((connection.getHost() == null) || connection.getHost().equals(""))
+ {
+ String uid = AMQConnectionFactory.getUniqueClientID();
+ if (uid == null)
+ {
+ throw URLHelper.parseError(-1, "Client Name not specified", fullURL);
+ }
+ else
+ {
+ setClientName(uid);
+ }
+
+ }
+ else
+ {
+ setClientName(connection.getHost());
+ }
+
+ String userInfo = connection.getUserInfo();
+
+ if (userInfo == null)
+ {
+ // Fix for Java 1.5 which doesn't parse UserInfo for non http URIs
+ userInfo = connection.getAuthority();
+
+ if (userInfo != null)
+ {
+ int atIndex = userInfo.indexOf('@');
+
+ if (atIndex != -1)
+ {
+ userInfo = userInfo.substring(0, atIndex);
+ }
+ else
+ {
+ userInfo = null;
+ }
+ }
+
+ }
+
+ if (userInfo == null)
+ {
+ throw URLHelper.parseError(AMQ_PROTOCOL.length() + 3, "User information not found on url", fullURL);
+ }
+ else
+ {
+ parseUserInfo(userInfo);
+ }
+
+ String virtualHost = connection.getPath();
+
+ if ((virtualHost != null) && (!virtualHost.equals("")))
+ {
+ setVirtualHost(virtualHost);
+ }
+ else
+ {
+ int authLength = connection.getAuthority().length();
+ int start = AMQ_PROTOCOL.length() + 3;
+ int testIndex = start + authLength;
+ if ((testIndex < fullURL.length()) && (fullURL.charAt(testIndex) == '?'))
+ {
+ throw URLHelper.parseError(start, testIndex - start, "Virtual host found", fullURL);
+ }
+ else
+ {
+ throw URLHelper.parseError(-1, "Virtual host not specified", fullURL);
+ }
+
+ }
+
+ URLHelper.parseOptions(_options, connection.getQuery());
+
+ processOptions();
+ }
+ catch (URISyntaxException uris)
+ {
+ if (uris instanceof URLSyntaxException)
+ {
+ throw (URLSyntaxException) uris;
+ }
+
+ int slash = fullURL.indexOf("\\");
+
+ if (slash == -1)
+ {
+ throw URLHelper.parseError(uris.getIndex(), uris.getReason(), uris.getInput());
+ }
+ else
+ {
+ if ((slash != 0) && (fullURL.charAt(slash - 1) == ':'))
+ {
+ throw URLHelper.parseError(slash - 2, fullURL.indexOf('?') - slash + 2,
+ "Virtual host looks like a windows path, forward slash not allowed in URL", fullURL);
+ }
+ else
+ {
+ throw URLHelper.parseError(slash, "Forward slash not allowed in URL", fullURL);
+ }
+ }
+
+ }
+ }
+
+ private void parseUserInfo(String userinfo) throws URLSyntaxException
+ {
+ // user info = user:pass
+
+ int colonIndex = userinfo.indexOf(':');
+
+ if (colonIndex == -1)
+ {
+ throw URLHelper.parseError(AMQ_PROTOCOL.length() + 3, userinfo.length(),
+ "Null password in user information not allowed.", _url);
+ }
+ else
+ {
+ setUsername(userinfo.substring(0, colonIndex));
+ setPassword(userinfo.substring(colonIndex + 1));
+ }
+
+ }
+
+ private void processOptions() throws URLSyntaxException
+ {
+ if (_options.containsKey(OPTIONS_BROKERLIST))
+ {
+ String brokerlist = _options.get(OPTIONS_BROKERLIST);
+
+ // brokerlist tcp://host:port?option='value',option='value';vm://:3/virtualpath?option='value'
+ StringTokenizer st = new StringTokenizer(brokerlist, "" + URLHelper.BROKER_SEPARATOR);
+
+ while (st.hasMoreTokens())
+ {
+ String broker = st.nextToken();
+
+ _brokers.add(new AMQBrokerDetails(broker));
+ }
+
+ _options.remove(OPTIONS_BROKERLIST);
+ }
+
+ if (_options.containsKey(OPTIONS_FAILOVER))
+ {
+ String failover = _options.get(OPTIONS_FAILOVER);
+
+ // failover='method?option='value',option='value''
+
+ int methodIndex = failover.indexOf('?');
+
+ if (methodIndex > -1)
+ {
+ _failoverMethod = failover.substring(0, methodIndex);
+ URLHelper.parseOptions(_failoverOptions, failover.substring(methodIndex + 1));
+ }
+ else
+ {
+ _failoverMethod = failover;
+ }
+
+ _options.remove(OPTIONS_FAILOVER);
+ }
+
+ if (_options.containsKey(OPTIONS_DEFAULT_TOPIC_EXCHANGE))
+ {
+ _defaultTopicExchangeName = new AMQShortString(_options.get(OPTIONS_DEFAULT_TOPIC_EXCHANGE));
+ }
+
+ if (_options.containsKey(OPTIONS_DEFAULT_QUEUE_EXCHANGE))
+ {
+ _defaultQueueExchangeName = new AMQShortString(_options.get(OPTIONS_DEFAULT_QUEUE_EXCHANGE));
+ }
+
+ if (_options.containsKey(OPTIONS_TEMPORARY_QUEUE_EXCHANGE))
+ {
+ _temporaryQueueExchangeName = new AMQShortString(_options.get(OPTIONS_TEMPORARY_QUEUE_EXCHANGE));
+ }
+
+ if (_options.containsKey(OPTIONS_TEMPORARY_TOPIC_EXCHANGE))
+ {
+ _temporaryTopicExchangeName = new AMQShortString(_options.get(OPTIONS_TEMPORARY_TOPIC_EXCHANGE));
+ }
+ }
+
+ public String getURL()
+ {
+ return _url;
+ }
+
+ public String getFailoverMethod()
+ {
+ return _failoverMethod;
+ }
+
+ public String getFailoverOption(String key)
+ {
+ return _failoverOptions.get(key);
+ }
+
+ public void setFailoverOption(String key, String value)
+ {
+ _failoverOptions.put(key, value);
+ }
+
+ public int getBrokerCount()
+ {
+ return _brokers.size();
+ }
+
+ public BrokerDetails getBrokerDetails(int index)
+ {
+ if (index < _brokers.size())
+ {
+ return _brokers.get(index);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public void addBrokerDetails(BrokerDetails broker)
+ {
+ if (!(_brokers.contains(broker)))
+ {
+ _brokers.add(broker);
+ }
+ }
+
+ public List<BrokerDetails> getAllBrokerDetails()
+ {
+ return _brokers;
+ }
+
+ public String getClientName()
+ {
+ return _clientName;
+ }
+
+ public void setClientName(String clientName)
+ {
+ _clientName = clientName;
+ }
+
+ public String getUsername()
+ {
+ return _username;
+ }
+
+ public void setUsername(String username)
+ {
+ _username = username;
+ }
+
+ public String getPassword()
+ {
+ return _password;
+ }
+
+ public void setPassword(String password)
+ {
+ _password = password;
+ }
+
+ public String getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public void setVirtualHost(String virtuaHost)
+ {
+ _virtualHost = virtuaHost;
+ }
+
+ public String getOption(String key)
+ {
+ return _options.get(key);
+ }
+
+ public void setOption(String key, String value)
+ {
+ _options.put(key, value);
+ }
+
+ public AMQShortString getDefaultQueueExchangeName()
+ {
+ return _defaultQueueExchangeName;
+ }
+
+ public AMQShortString getDefaultTopicExchangeName()
+ {
+ return _defaultTopicExchangeName;
+ }
+
+ public AMQShortString getTemporaryQueueExchangeName()
+ {
+ return _temporaryQueueExchangeName;
+ }
+
+ public AMQShortString getTemporaryTopicExchangeName()
+ {
+ return _temporaryTopicExchangeName;
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(AMQ_PROTOCOL);
+ sb.append("://");
+
+ if (_username != null)
+ {
+ sb.append(_username);
+
+ if (_password != null)
+ {
+ sb.append(':');
+ if (_logger.isDebugEnabled())
+ {
+ sb.append(_password);
+ }
+ else
+ {
+ sb.append("********");
+ }
+ }
+
+ sb.append('@');
+ }
+
+ sb.append(_clientName);
+
+ sb.append(_virtualHost);
+
+ sb.append(optionsToString());
+
+ return sb.toString();
+ }
+
+ private String optionsToString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("?" + OPTIONS_BROKERLIST + "='");
+
+ for (BrokerDetails service : _brokers)
+ {
+ sb.append(service.toString());
+ sb.append(';');
+ }
+
+ sb.deleteCharAt(sb.length() - 1);
+ sb.append("'");
+
+ if (_failoverMethod != null)
+ {
+ sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ sb.append(OPTIONS_FAILOVER + "='");
+ sb.append(_failoverMethod);
+ sb.append(URLHelper.printOptions(_failoverOptions));
+ sb.append("'");
+ }
+
+ return sb.toString();
+ }
+
+ public static void main(String[] args) throws URLSyntaxException
+ {
+ String url2 =
+ "amqp://ritchiem:bob@temp?brokerlist='tcp://localhost:5672;jcp://fancyserver:3000/',failover='roundrobin'";
+ // "amqp://user:pass@clientid/virtualhost?brokerlist='tcp://host:1?option1=\'value\',option2=\'value\';vm://:3?option1=\'value\'',failover='method?option1=\'value\',option2='value''";
+
+ ConnectionURL connectionurl2 = new AMQConnectionURL(url2);
+
+ System.out.println(url2);
+ System.out.println(connectionurl2);
+
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
new file mode 100644
index 0000000000..cc5af07b20
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java
@@ -0,0 +1,451 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.Destination;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.StringRefAddr;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.BindingURL;
+import org.apache.qpid.url.URLHelper;
+import org.apache.qpid.url.URLSyntaxException;
+
+
+public abstract class AMQDestination implements Destination, Referenceable
+{
+ protected final AMQShortString _exchangeName;
+
+ protected final AMQShortString _exchangeClass;
+
+ protected final AMQShortString _destinationName;
+
+ protected final boolean _isDurable;
+
+ protected final boolean _isExclusive;
+
+ protected final boolean _isAutoDelete;
+
+ private AMQShortString _queueName;
+
+ private String _url;
+ private AMQShortString _urlAsShortString;
+
+ private boolean _validated;
+
+ private byte[] _byteEncoding;
+ private static final int IS_DURABLE_MASK = 0x1;
+ private static final int IS_EXCLUSIVE_MASK = 0x2;
+ private static final int IS_AUTODELETE_MASK = 0x4;
+
+ public static final Integer QUEUE_TYPE = Integer.valueOf(1);
+ public static final Integer TOPIC_TYPE = Integer.valueOf(2);
+ public static final Integer UNKNOWN_TYPE = Integer.valueOf(3);
+
+ protected AMQDestination(String url) throws URLSyntaxException
+ {
+ this(new AMQBindingURL(url));
+ }
+
+ protected AMQDestination(BindingURL binding)
+ {
+ _exchangeName = binding.getExchangeName();
+ _exchangeClass = binding.getExchangeClass();
+ _destinationName = binding.getDestinationName();
+
+ _isExclusive = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_EXCLUSIVE));
+ _isAutoDelete = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_AUTODELETE));
+ _isDurable = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_DURABLE));
+ _queueName = binding.getQueueName() == null ? null : new AMQShortString(binding.getQueueName());
+ }
+
+ protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName, AMQShortString queueName)
+ {
+ this(exchangeName, exchangeClass, destinationName, false, false, queueName);
+ }
+
+ protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName)
+ {
+ this(exchangeName, exchangeClass, destinationName, false, false, null);
+ }
+
+ protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName, boolean isExclusive,
+ boolean isAutoDelete, AMQShortString queueName)
+ {
+ this(exchangeName, exchangeClass, destinationName, isExclusive, isAutoDelete, queueName, false);
+ }
+
+ protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName, boolean isExclusive,
+ boolean isAutoDelete, AMQShortString queueName, boolean isDurable)
+ {
+ if (destinationName == null)
+ {
+ throw new IllegalArgumentException("Destination exchange must not be null");
+ }
+ if (exchangeName == null)
+ {
+ throw new IllegalArgumentException("Exchange exchange must not be null");
+ }
+ if (exchangeClass == null)
+ {
+ throw new IllegalArgumentException("Exchange class must not be null");
+ }
+ _exchangeName = exchangeName;
+ _exchangeClass = exchangeClass;
+ _destinationName = destinationName;
+ _isExclusive = isExclusive;
+ _isAutoDelete = isAutoDelete;
+ _queueName = queueName;
+ _isDurable = isDurable;
+ }
+
+ public AMQShortString getEncodedName()
+ {
+ if(_urlAsShortString == null)
+ {
+ toURL();
+ }
+ return _urlAsShortString;
+ }
+
+ public boolean isDurable()
+ {
+ return _isDurable;
+ }
+
+ public AMQShortString getExchangeName()
+ {
+ return _exchangeName;
+ }
+
+ public AMQShortString getExchangeClass()
+ {
+ return _exchangeClass;
+ }
+
+ public boolean isTopic()
+ {
+ return ExchangeDefaults.TOPIC_EXCHANGE_CLASS.equals(_exchangeClass);
+ }
+
+ public boolean isQueue()
+ {
+ return ExchangeDefaults.DIRECT_EXCHANGE_CLASS.equals(_exchangeClass);
+ }
+
+ public AMQShortString getDestinationName()
+ {
+ return _destinationName;
+ }
+
+ public String getQueueName()
+ {
+ return _queueName == null ? null : _queueName.toString();
+ }
+
+ public AMQShortString getAMQQueueName()
+ {
+ return _queueName;
+ }
+
+
+
+ public void setQueueName(AMQShortString queueName)
+ {
+
+ _queueName = queueName;
+ // calculated URL now out of date
+ _url = null;
+ _urlAsShortString = null;
+ _byteEncoding = null;
+ }
+
+ public abstract AMQShortString getRoutingKey();
+
+ public boolean isExclusive()
+ {
+ return _isExclusive;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _isAutoDelete;
+ }
+
+ public abstract boolean isNameRequired();
+
+ public String toString()
+ {
+ return toURL();
+
+ }
+
+ public boolean isValidated()
+ {
+ return _validated;
+ }
+
+ public void setValidated(boolean validated)
+ {
+ _validated = validated;
+ }
+
+ public String toURL()
+ {
+ String url = _url;
+ if(url == null)
+ {
+
+
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(_exchangeClass);
+ sb.append("://");
+ sb.append(_exchangeName);
+
+ sb.append('/');
+
+ if (_destinationName != null)
+ {
+ sb.append(_destinationName);
+ }
+
+ sb.append('/');
+
+ if (_queueName != null)
+ {
+ sb.append(_queueName);
+ }
+
+ sb.append('?');
+
+ if (_isDurable)
+ {
+ sb.append(BindingURL.OPTION_DURABLE);
+ sb.append("='true'");
+ sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ }
+
+ if (_isExclusive)
+ {
+ sb.append(BindingURL.OPTION_EXCLUSIVE);
+ sb.append("='true'");
+ sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ }
+
+ if (_isAutoDelete)
+ {
+ sb.append(BindingURL.OPTION_AUTODELETE);
+ sb.append("='true'");
+ sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR);
+ }
+
+ //removeKey the last char '?' if there is no options , ',' if there are.
+ sb.deleteCharAt(sb.length() - 1);
+ url = sb.toString();
+ _url = url;
+ _urlAsShortString = new AMQShortString(url);
+ }
+ return url;
+ }
+
+ public byte[] toByteEncoding()
+ {
+ byte[] encoding = _byteEncoding;
+ if(encoding == null)
+ {
+ int size = _exchangeClass.length() + 1 +
+ _exchangeName.length() + 1 +
+ (_destinationName == null ? 0 : _destinationName.length()) + 1 +
+ (_queueName == null ? 0 : _queueName.length()) + 1 +
+ 1;
+ encoding = new byte[size];
+ int pos = 0;
+
+ pos = _exchangeClass.writeToByteArray(encoding, pos);
+ pos = _exchangeName.writeToByteArray(encoding, pos);
+ if(_destinationName == null)
+ {
+ encoding[pos++] = (byte)0;
+ }
+ else
+ {
+ pos = _destinationName.writeToByteArray(encoding,pos);
+ }
+ if(_queueName == null)
+ {
+ encoding[pos++] = (byte)0;
+ }
+ else
+ {
+ pos = _queueName.writeToByteArray(encoding,pos);
+ }
+ byte options = 0;
+ if(_isDurable)
+ {
+ options |= IS_DURABLE_MASK;
+ }
+ if(_isExclusive)
+ {
+ options |= IS_EXCLUSIVE_MASK;
+ }
+ if(_isAutoDelete)
+ {
+ options |= IS_AUTODELETE_MASK;
+ }
+ encoding[pos] = options;
+
+
+ _byteEncoding = encoding;
+
+ }
+ return encoding;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass())
+ {
+ return false;
+ }
+
+ final AMQDestination that = (AMQDestination) o;
+
+ if (!_destinationName.equals(that._destinationName))
+ {
+ return false;
+ }
+ if (!_exchangeClass.equals(that._exchangeClass))
+ {
+ return false;
+ }
+ if (!_exchangeName.equals(that._exchangeName))
+ {
+ return false;
+ }
+ if ((_queueName == null && that._queueName != null) ||
+ (_queueName != null && !_queueName.equals(that._queueName)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int result;
+ result = _exchangeName.hashCode();
+ result = 29 * result + _exchangeClass.hashCode();
+ result = 29 * result + _destinationName.hashCode();
+ if (_queueName != null)
+ {
+ result = 29 * result + _queueName.hashCode();
+ }
+
+ return result;
+ }
+
+ public Reference getReference() throws NamingException
+ {
+ return new Reference(
+ this.getClass().getName(),
+ new StringRefAddr(this.getClass().getName(), toURL()),
+ AMQConnectionFactory.class.getName(),
+ null); // factory location
+ }
+
+
+ public static Destination createDestination(byte[] byteEncodedDestination)
+ {
+ AMQShortString exchangeClass;
+ AMQShortString exchangeName;
+ AMQShortString destinationName;
+ AMQShortString queueName;
+ boolean isDurable;
+ boolean isExclusive;
+ boolean isAutoDelete;
+
+ int pos = 0;
+ exchangeClass = AMQShortString.readFromByteArray(byteEncodedDestination, pos);
+ pos+= exchangeClass.length() + 1;
+ exchangeName = AMQShortString.readFromByteArray(byteEncodedDestination, pos);
+ pos+= exchangeName.length() + 1;
+ destinationName = AMQShortString.readFromByteArray(byteEncodedDestination, pos);
+ pos+= (destinationName == null ? 0 : destinationName.length()) + 1;
+ queueName = AMQShortString.readFromByteArray(byteEncodedDestination, pos);
+ pos+= (queueName == null ? 0 : queueName.length()) + 1;
+ int options = byteEncodedDestination[pos];
+ isDurable = (options & IS_DURABLE_MASK) != 0;
+ isExclusive = (options & IS_EXCLUSIVE_MASK) != 0;
+ isAutoDelete = (options & IS_AUTODELETE_MASK) != 0;
+
+ if (exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS))
+ {
+ return new AMQQueue(exchangeName,destinationName,queueName,isExclusive,isAutoDelete,isDurable);
+ }
+ else if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
+ {
+ return new AMQTopic(exchangeName,destinationName,isAutoDelete,queueName,isDurable);
+ }
+ else if (exchangeClass.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS))
+ {
+ return new AMQHeadersExchange(destinationName);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown Exchange Class:" + exchangeClass);
+ }
+
+
+
+ }
+
+ public static Destination createDestination(BindingURL binding)
+ {
+ AMQShortString type = binding.getExchangeClass();
+
+ if (type.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS))
+ {
+ return new AMQQueue(binding);
+ }
+ else if (type.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
+ {
+ return new AMQTopic(binding);
+ }
+ else if (type.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS))
+ {
+ return new AMQHeadersExchange(binding);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown Exchange Class:" + type + " in binding:" + binding);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java
new file mode 100644
index 0000000000..1a2fe0d355
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.url.BindingURL;
+
+/**
+ * A destination backed by a headers exchange
+ */
+public class AMQHeadersExchange extends AMQDestination
+{
+ public AMQHeadersExchange(BindingURL binding)
+ {
+ this(binding.getExchangeName());
+ }
+
+ public AMQHeadersExchange(String name)
+ {
+ this(new AMQShortString(name));
+ }
+
+ public AMQHeadersExchange(AMQShortString queueName)
+ {
+ super(queueName, ExchangeDefaults.HEADERS_EXCHANGE_CLASS, queueName, true, true, null);
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return getDestinationName();
+ }
+
+ public boolean isNameRequired()
+ {
+ //Not sure what the best approach is here, probably to treat this like a topic
+ //and allow server to generate names. As it is AMQ specific it doesn't need to
+ //fit the JMS API expectations so this is not as yet critical.
+ return false;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java
new file mode 100644
index 0000000000..54d5a0426f
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.AMQUndeliveredException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQNoConsumersException indicates failure to pass an immediate message to a consumer.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to pass an immediate message to a consumer.
+ * <tr><td>
+ */
+public class AMQNoConsumersException extends AMQUndeliveredException
+{
+ public AMQNoConsumersException(String msg, Object bounced)
+ {
+ super(AMQConstant.NO_CONSUMERS, msg, bounced);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java
new file mode 100644
index 0000000000..a314101acf
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.AMQUndeliveredException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQNoRouteException indicates that a mandatory message could not be routed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to route a mandatory message.
+ * <tr><td>
+ */
+public class AMQNoRouteException extends AMQUndeliveredException
+{
+ public AMQNoRouteException(String msg, Object bounced)
+ {
+ super(AMQConstant.NO_ROUTE, msg, bounced);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java
new file mode 100644
index 0000000000..9185bc87e8
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java
@@ -0,0 +1,149 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.Queue;
+import javax.jms.Connection;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.url.BindingURL;
+
+public class AMQQueue extends AMQDestination implements Queue
+{
+
+ /**
+ * Create a reference to a non temporary queue using a BindingURL object.
+ * Note this does not actually imply the queue exists.
+ * @param binding a BindingURL object
+ */
+ public AMQQueue(BindingURL binding)
+ {
+ super(binding);
+ }
+
+ /**
+ * Create a reference to a non temporary queue. Note this does not actually imply the queue exists.
+ * @param name the name of the queue
+ */
+ public AMQQueue(AMQShortString exchangeName, String name)
+ {
+ this(exchangeName, new AMQShortString(name));
+ }
+
+
+ /**
+ * Create a reference to a non temporary queue. Note this does not actually imply the queue exists.
+ * @param name the name of the queue
+ */
+ public AMQQueue(AMQShortString exchangeName, AMQShortString name)
+ {
+ this(exchangeName, name, false);
+ }
+
+ public AMQQueue(AMQShortString exchangeName, AMQShortString routingKey, AMQShortString queueName)
+ {
+ super(exchangeName, ExchangeDefaults.DIRECT_EXCHANGE_CLASS, routingKey, false,
+ false, queueName, false); }
+
+
+ /**
+ * Create a reference to a non temporary queue. Note this does not actually imply the queue exists.
+ * @param name the name of the queue
+ */
+ public AMQQueue(String exchangeName, String name)
+ {
+ this(new AMQShortString(exchangeName), new AMQShortString(name), false);
+ }
+
+
+ public AMQQueue(AMQConnection connection, String name)
+ {
+ this(connection.getDefaultQueueExchangeName(),name);
+ }
+
+ public AMQQueue(AMQConnection connection, String name, boolean temporary)
+ {
+ this(connection.getDefaultQueueExchangeName(), new AMQShortString(name),temporary);
+ }
+
+
+ /**
+ * Create a queue with a specified name.
+ *
+ * @param name the destination name (used in the routing key)
+ * @param temporary if true the broker will generate a queue name, also if true then the queue is autodeleted
+ * and exclusive
+ */
+ public AMQQueue(String exchangeName, String name, boolean temporary)
+ {
+ this(new AMQShortString(exchangeName), new AMQShortString(name),temporary);
+ }
+
+
+ /**
+ * Create a queue with a specified name.
+ *
+ * @param name the destination name (used in the routing key)
+ * @param temporary if true the broker will generate a queue name, also if true then the queue is autodeleted
+ * and exclusive
+ */
+ public AMQQueue(AMQShortString exchangeName, AMQShortString name, boolean temporary)
+ {
+ // queue name is set to null indicating that the broker assigns a name in the case of temporary queues
+ // temporary queues are typically used as response queues
+ this(exchangeName, name, temporary?null:name, temporary, temporary, !temporary);
+
+ }
+
+ /**
+ * Create a reference to a queue. Note this does not actually imply the queue exists.
+ * @param destinationName the queue name
+ * @param queueName the queue name
+ * @param exclusive true if the queue should only permit a single consumer
+ * @param autoDelete true if the queue should be deleted automatically when the last consumers detaches
+ */
+ public AMQQueue(AMQShortString exchangeName, AMQShortString destinationName, AMQShortString queueName, boolean exclusive, boolean autoDelete)
+ {
+ this(exchangeName, destinationName, queueName, exclusive, autoDelete, false);
+ }
+
+
+ public AMQQueue(AMQShortString exchangeName, AMQShortString destinationName, AMQShortString queueName, boolean exclusive, boolean autoDelete, boolean durable)
+ {
+ super(exchangeName, ExchangeDefaults.DIRECT_EXCHANGE_CLASS, destinationName, exclusive,
+ autoDelete, queueName, durable);
+ }
+
+
+
+ public AMQShortString getRoutingKey()
+ {
+ return getAMQQueueName();
+ }
+
+ public boolean isNameRequired()
+ {
+ //If the name is null, we require one to be generated by the client so that it will#
+ //remain valid if we failover (see BLZ-24)
+ return getQueueName() == null;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java
new file mode 100644
index 0000000000..28e5992b26
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java
@@ -0,0 +1,136 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class AMQQueueBrowser implements QueueBrowser
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AMQQueueBrowser.class);
+
+ private AtomicBoolean _isClosed = new AtomicBoolean();
+ private final AMQSession _session;
+ private final AMQQueue _queue;
+ private final ArrayList<BasicMessageConsumer> _consumers = new ArrayList<BasicMessageConsumer>();
+ private final String _messageSelector;
+
+ AMQQueueBrowser(AMQSession session, AMQQueue queue, String messageSelector) throws JMSException
+ {
+ _session = session;
+ _queue = queue;
+ _messageSelector = ((messageSelector == null) || (messageSelector.trim().length() == 0)) ? null : messageSelector;
+ // Create Consumer to verify message selector.
+ BasicMessageConsumer consumer =
+ (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false);
+ consumer.close();
+ }
+
+ public Queue getQueue() throws JMSException
+ {
+ checkState();
+
+ return _queue;
+ }
+
+ private void checkState() throws JMSException
+ {
+ if (_isClosed.get())
+ {
+ throw new IllegalStateException("Queue Browser");
+ }
+
+ if (_session.isClosed())
+ {
+ throw new IllegalStateException("Session is closed");
+ }
+
+ }
+
+ public String getMessageSelector() throws JMSException
+ {
+
+ checkState();
+
+ return _messageSelector;
+ }
+
+ public Enumeration getEnumeration() throws JMSException
+ {
+ checkState();
+ final BasicMessageConsumer consumer =
+ (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false);
+ _consumers.add(consumer);
+
+ return new Enumeration()
+ {
+
+ Message _nextMessage = consumer.receive();
+
+ public boolean hasMoreElements()
+ {
+ _logger.info("QB:hasMoreElements:" + (_nextMessage != null));
+
+ return (_nextMessage != null);
+ }
+
+ public Object nextElement()
+ {
+ 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;
+ }
+ };
+ }
+
+ public void close() throws JMSException
+ {
+ for (BasicMessageConsumer consumer : _consumers)
+ {
+ consumer.close();
+ }
+
+ _consumers.clear();
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java
new file mode 100644
index 0000000000..a8c83d8868
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java
@@ -0,0 +1,204 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import java.io.Serializable;
+
+import javax.jms.BytesMessage;
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.jms.StreamMessage;
+import javax.jms.TemporaryQueue;
+import javax.jms.TemporaryTopic;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicSubscriber;
+
+/**
+ * Need this adaptor class to conform to JMS spec and throw IllegalStateException
+ * from createDurableSubscriber, unsubscribe, createTopic & createTemporaryTopic
+ */
+public class AMQQueueSessionAdaptor implements QueueSession, AMQSessionAdapter
+{
+ //holds a session for delegation
+ protected final AMQSession _session;
+
+ /**
+ * Construct an adaptor with a session to wrap
+ * @param session
+ */
+ public AMQQueueSessionAdaptor(Session session)
+ {
+ _session = (AMQSession) session;
+ }
+
+ public TemporaryQueue createTemporaryQueue() throws JMSException {
+ return _session.createTemporaryQueue();
+ }
+
+ public Queue createQueue(String string) throws JMSException {
+ return _session.createQueue(string);
+ }
+
+ public QueueReceiver createReceiver(Queue queue) throws JMSException {
+ return _session.createReceiver(queue);
+ }
+
+ public QueueReceiver createReceiver(Queue queue, String string) throws JMSException {
+ return _session.createReceiver(queue, string);
+ }
+
+ public QueueSender createSender(Queue queue) throws JMSException {
+ return _session.createSender(queue);
+ }
+
+ public QueueBrowser createBrowser(Queue queue) throws JMSException {
+ return _session.createBrowser(queue);
+ }
+
+ public QueueBrowser createBrowser(Queue queue, String string) throws JMSException {
+ return _session.createBrowser(queue, string);
+ }
+
+ public BytesMessage createBytesMessage() throws JMSException {
+ return _session.createBytesMessage();
+ }
+
+ public MapMessage createMapMessage() throws JMSException {
+ return _session.createMapMessage();
+ }
+
+ public Message createMessage() throws JMSException {
+ return _session.createMessage();
+ }
+
+ public ObjectMessage createObjectMessage() throws JMSException {
+ return _session.createObjectMessage();
+ }
+
+ public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException {
+ return _session.createObjectMessage(serializable);
+ }
+
+ public StreamMessage createStreamMessage() throws JMSException {
+ return _session.createStreamMessage();
+ }
+
+ public TextMessage createTextMessage() throws JMSException {
+ return _session.createTextMessage();
+ }
+
+ public TextMessage createTextMessage(String string) throws JMSException {
+ return _session.createTextMessage(string);
+ }
+
+ public boolean getTransacted() throws JMSException {
+ return _session.getTransacted();
+ }
+
+ public int getAcknowledgeMode() throws JMSException {
+ return _session.getAcknowledgeMode();
+ }
+
+ public void commit() throws JMSException {
+ _session.commit();
+ }
+
+ public void rollback() throws JMSException {
+ _session.rollback();
+ }
+
+ public void close() throws JMSException {
+ _session.close();
+ }
+
+ public void recover() throws JMSException {
+ _session.recover();
+ }
+
+ public MessageListener getMessageListener() throws JMSException {
+ return _session.getMessageListener();
+ }
+
+ public void setMessageListener(MessageListener messageListener) throws JMSException {
+ _session.setMessageListener(messageListener);
+ }
+
+ public void run() {
+ _session.run();
+ }
+
+ public MessageProducer createProducer(Destination destination) throws JMSException {
+ return _session.createProducer(destination);
+ }
+
+ public MessageConsumer createConsumer(Destination destination) throws JMSException {
+ return _session.createConsumer(destination);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, String string) throws JMSException {
+ return _session.createConsumer(destination,string);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, String string, boolean b) throws JMSException {
+ return _session.createConsumer(destination,string,b);
+ }
+
+ //The following methods cannot be called from a QueueSession as per JMS spec
+
+ public Topic createTopic(String string) throws JMSException {
+ throw new IllegalStateException("Cannot call createTopic from QueueSession");
+ }
+
+ public TopicSubscriber createDurableSubscriber(Topic topic, String string) throws JMSException {
+ throw new IllegalStateException("Cannot call createDurableSubscriber from QueueSession");
+ }
+
+ public TopicSubscriber createDurableSubscriber(Topic topic, String string, String string1, boolean b) throws JMSException {
+ throw new IllegalStateException("Cannot call createDurableSubscriber from QueueSession");
+ }
+
+ public TemporaryTopic createTemporaryTopic() throws JMSException {
+ throw new IllegalStateException("Cannot call createTemporaryTopic from QueueSession");
+ }
+
+ public void unsubscribe(String string) throws JMSException {
+ throw new IllegalStateException("Cannot call unsubscribe from QueueSession");
+ }
+
+ public AMQSession getSession()
+ {
+ return _session;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
new file mode 100644
index 0000000000..a0b79b135d
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
@@ -0,0 +1,2800 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInvalidArgumentException;
+import org.apache.qpid.AMQInvalidRoutingKeyException;
+import org.apache.qpid.AMQUndeliveredException;
+import org.apache.qpid.AMQDisconnectedException;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.client.failover.FailoverNoopSupport;
+import org.apache.qpid.client.failover.FailoverProtectedOperation;
+import org.apache.qpid.client.failover.FailoverRetrySupport;
+import org.apache.qpid.client.message.AbstractJMSMessage;
+import org.apache.qpid.client.message.JMSBytesMessage;
+import org.apache.qpid.client.message.JMSMapMessage;
+import org.apache.qpid.client.message.JMSObjectMessage;
+import org.apache.qpid.client.message.JMSStreamMessage;
+import org.apache.qpid.client.message.JMSTextMessage;
+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.common.AMQPFilterTypes;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicAckBody;
+import org.apache.qpid.framing.BasicConsumeBody;
+import org.apache.qpid.framing.BasicConsumeOkBody;
+import org.apache.qpid.framing.BasicRecoverBody;
+import org.apache.qpid.framing.BasicRecoverOkBody;
+import org.apache.qpid.framing.BasicRejectBody;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.framing.ChannelFlowBody;
+import org.apache.qpid.framing.ChannelFlowOkBody;
+import org.apache.qpid.framing.ExchangeBoundBody;
+import org.apache.qpid.framing.ExchangeBoundOkBody;
+import org.apache.qpid.framing.ExchangeDeclareBody;
+import org.apache.qpid.framing.ExchangeDeclareOkBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+import org.apache.qpid.framing.QueueBindBody;
+import org.apache.qpid.framing.QueueBindOkBody;
+import org.apache.qpid.framing.QueueDeclareBody;
+import org.apache.qpid.framing.QueueDeclareOkBody;
+import org.apache.qpid.framing.QueueDeleteBody;
+import org.apache.qpid.framing.QueueDeleteOkBody;
+import org.apache.qpid.framing.TxCommitBody;
+import org.apache.qpid.framing.TxCommitOkBody;
+import org.apache.qpid.framing.TxRollbackBody;
+import org.apache.qpid.framing.TxRollbackOkBody;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.URLSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.BytesMessage;
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.InvalidDestinationException;
+import javax.jms.InvalidSelectorException;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.StreamMessage;
+import javax.jms.TemporaryQueue;
+import javax.jms.TemporaryTopic;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+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.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ *
+ * @todo Different FailoverSupport implementation are needed on the same method call, in different situations. For
+ * example, when failing-over and reestablishing the bindings, the bind cannot be interrupted by a second
+ * fail-over, if it fails with an exception, the fail-over process should also fail. When binding outside of
+ * the fail-over process, the retry handler could be used to automatically retry the operation once the connection
+ * has been reestablished. All fail-over protected operations should be placed in private methods, with
+ * FailoverSupport passed in by the caller to provide the correct support for the calling context. Sometimes the
+ * fail-over process sets a nowait flag and uses an async method call instead.
+ * @todo Two new objects created on every failover supported method call. Consider more efficient ways of doing this,
+ * after looking at worse bottlenecks first.
+ */
+public class AMQSession extends Closeable implements Session, QueueSession, TopicSession
+{
+ /** Used for debugging. */
+ private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class);
+
+ /** Used for debugging in the dispatcher. */
+ private static final Logger _dispatcherLogger = LoggerFactory.getLogger(Dispatcher.class);
+
+ /** The default maximum number of prefetched message at which to suspend the channel. */
+ public static final int DEFAULT_PREFETCH_HIGH_MARK = 5000;
+
+ /** The default minimum number of prefetched messages at which to resume the channel. */
+ public static final int DEFAULT_PREFETCH_LOW_MARK = 2500;
+
+ /**
+ * The default value for immediate flag used by producers created by this session is false. That is, a consumer does
+ * not need to be attached to a queue.
+ */
+ protected static final boolean DEFAULT_IMMEDIATE = false;
+
+ /**
+ * The default value for mandatory flag used by producers created by this session is true. That is, server will not
+ * silently drop messages where no queue is connected to the exchange for the message.
+ */
+ protected static final boolean DEFAULT_MANDATORY = true;
+
+ /** System property to enable strict AMQP compliance. */
+ public static final String STRICT_AMQP = "STRICT_AMQP";
+
+ /** Strict AMQP default setting. */
+ public static final String STRICT_AMQP_DEFAULT = "false";
+
+ /** System property to enable failure if strict AMQP compliance is violated. */
+ public static final String STRICT_AMQP_FATAL = "STRICT_AMQP_FATAL";
+
+ /** Strickt AMQP failure default. */
+ public static final String STRICT_AMQP_FATAL_DEFAULT = "true";
+
+ /** System property to enable immediate message prefetching. */
+ public static final String IMMEDIATE_PREFETCH = "IMMEDIATE_PREFETCH";
+
+ /** Immediate message prefetch default. */
+ public static final String IMMEDIATE_PREFETCH_DEFAULT = "false";
+
+ /** The connection to which this session belongs. */
+ private AMQConnection _connection;
+
+ /** Used to indicate whether or not this is a transactional session. */
+ private boolean _transacted;
+
+ /** Holds the sessions acknowledgement mode. */
+ private int _acknowledgeMode;
+
+ /** Holds this session unique identifier, used to distinguish it from other sessions. */
+ private int _channelId;
+
+ /** @todo This does not appear to be set? */
+ private int _ticket;
+
+ /** Holds the high mark for prefetched message, at which the session is suspended. */
+ private int _defaultPrefetchHighMark = DEFAULT_PREFETCH_HIGH_MARK;
+
+ /** Holds the low mark for prefetched messages, below which the session is resumed. */
+ private int _defaultPrefetchLowMark = DEFAULT_PREFETCH_LOW_MARK;
+
+ /** Holds the message listener, if any, which is attached to this session. */
+ private MessageListener _messageListener = null;
+
+ /** Used to indicate that this session has been started at least once. */
+ private AtomicBoolean _startedAtLeastOnce = new AtomicBoolean(false);
+
+ /**
+ * Used to reference durable subscribers so that requests for unsubscribe can be handled correctly. Note this only
+ * keeps a record of subscriptions which have been created in the current instance. It does not remember
+ * subscriptions between executions of the client.
+ */
+ private final ConcurrentHashMap<String, TopicSubscriberAdaptor> _subscriptions =
+ new ConcurrentHashMap<String, TopicSubscriberAdaptor>();
+
+ /**
+ * Holds a mapping from message consumers to their identifying names, so that their subscriptions may be looked
+ * up in the {@link #_subscriptions} map.
+ */
+ private final ConcurrentHashMap<BasicMessageConsumer, String> _reverseSubscriptionMap =
+ new ConcurrentHashMap<BasicMessageConsumer, String>();
+
+ /**
+ * Used to hold incoming messages.
+ *
+ * @todo Weaken the type once {@link FlowControllingBlockingQueue} implements Queue.
+ */
+ private final FlowControllingBlockingQueue _queue;
+
+ /** Holds the highest received delivery tag. */
+ private final AtomicLong _highestDeliveryTag = new AtomicLong(-1);
+
+ /** Holds the dispatcher thread for this session. */
+ private Dispatcher _dispatcher;
+
+ /** Holds the message factory factory for this session. */
+ private MessageFactoryRegistry _messageFactoryRegistry;
+
+ /** Holds all of the producers created by this session, keyed by their unique identifiers. */
+ private Map<Long, MessageProducer> _producers = new ConcurrentHashMap<Long, MessageProducer>();
+
+ /** Used as a source of unique identifiers so that the consumers can be tagged to match them to BasicConsume methods. */
+ private int _nextTag = 1;
+
+ /**
+ * 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>();
+
+ /** Provides a count of consumers on destinations, in order to be able to know if a destination has consumers. */
+ private ConcurrentHashMap<Destination, AtomicInteger> _destinationConsumerCount =
+ new ConcurrentHashMap<Destination, AtomicInteger>();
+
+ /**
+ * Used as a source of unique identifiers for producers within the session.
+ *
+ * <p/> Access to this id does not require to be synchronized since according to the JMS specification only one
+ * thread of control is allowed to create producers for any given session instance.
+ */
+ private long _nextProducerId;
+
+ /**
+ * Set when recover is called. This is to handle the case where recover() is called by application code during
+ * onMessage() processing to enure that an auto ack is not sent.
+ */
+ private boolean _inRecovery;
+
+ /** Used to indicates that the connection to which this session belongs, has been stopped. */
+ private boolean _connectionStopped;
+
+ /** Used to indicate that this session has a message listener attached to it. */
+ private boolean _hasMessageListeners;
+
+ /** Used to indicate that this session has been suspended. */
+ private boolean _suspended;
+
+ /**
+ * Used to protect the suspension of this session, so that critical code can be executed during suspension,
+ * without the session being resumed by other threads.
+ */
+ private final Object _suspensionLock = new Object();
+
+ /**
+ * Used to ensure that onlt the first call to start the dispatcher can unsuspend the channel.
+ *
+ * @todo This is accessed only within a synchronized method, so does not need to be atomic.
+ */
+ private final AtomicBoolean _firstDispatcher = new AtomicBoolean(true);
+
+ /** Used to indicate that the session should start pre-fetching messages as soon as it is started. */
+ private final boolean _immediatePrefetch;
+
+ /** Indicates that warnings should be generated on violations of the strict AMQP. */
+ private final boolean _strictAMQP;
+
+ /** Indicates that runtime exceptions should be generated on vilations of the strict AMQP. */
+ private final boolean _strictAMQPFATAL;
+ private final Object _messageDeliveryLock = new Object();
+
+ /**
+ * Creates a new session on a connection.
+ *
+ * @param con The connection on which to create the session.
+ * @param channelId The unique identifier for the session.
+ * @param transacted Indicates whether or not the session is transactional.
+ * @param acknowledgeMode The acknoledgement mode for the session.
+ * @param messageFactoryRegistry The message factory factory for the session.
+ * @param defaultPrefetchHighMark The maximum number of messages to prefetched before suspending the session.
+ * @param defaultPrefetchLowMark The number of prefetched messages at which to resume the session.
+ */
+ AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode,
+ MessageFactoryRegistry messageFactoryRegistry, int defaultPrefetchHighMark, int defaultPrefetchLowMark)
+ {
+
+ _strictAMQP = Boolean.parseBoolean(System.getProperties().getProperty(STRICT_AMQP, STRICT_AMQP_DEFAULT));
+ _strictAMQPFATAL =
+ Boolean.parseBoolean(System.getProperties().getProperty(STRICT_AMQP_FATAL, STRICT_AMQP_FATAL_DEFAULT));
+ _immediatePrefetch =
+ _strictAMQP
+ || Boolean.parseBoolean(System.getProperties().getProperty(IMMEDIATE_PREFETCH, IMMEDIATE_PREFETCH_DEFAULT));
+
+ _connection = con;
+ _transacted = transacted;
+ if (transacted)
+ {
+ _acknowledgeMode = javax.jms.Session.SESSION_TRANSACTED;
+ }
+ else
+ {
+ _acknowledgeMode = acknowledgeMode;
+ }
+
+ _channelId = channelId;
+ _messageFactoryRegistry = messageFactoryRegistry;
+ _defaultPrefetchHighMark = defaultPrefetchHighMark;
+ _defaultPrefetchLowMark = defaultPrefetchLowMark;
+
+ if (_acknowledgeMode == NO_ACKNOWLEDGE)
+ {
+ _queue =
+ new FlowControllingBlockingQueue(_defaultPrefetchHighMark, _defaultPrefetchLowMark,
+ new FlowControllingBlockingQueue.ThresholdListener()
+ {
+ 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();
+ }
+ }
+ });
+ }
+ else
+ {
+ _queue = new FlowControllingBlockingQueue(_defaultPrefetchHighMark, null);
+ }
+ }
+
+ /**
+ * Creates a new session on a connection with the default message factory factory.
+ *
+ * @param con The connection on which to create the session.
+ * @param channelId The unique identifier for the session.
+ * @param transacted Indicates whether or not the session is transactional.
+ * @param acknowledgeMode The acknoledgement mode for the session.
+ * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session.
+ * @param defaultPrefetchLow The number of prefetched messages at which to resume the session.
+ */
+ AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh,
+ int defaultPrefetchLow)
+ {
+ this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh,
+ defaultPrefetchLow);
+ }
+
+ // ===== JMS Session methods.
+
+ /**
+ * Closes the session with no timeout.
+ *
+ * @throws JMSException If the JMS provider fails to close the session due to some internal error.
+ */
+ public void close() throws JMSException
+ {
+ close(-1);
+ }
+
+ public BytesMessage createBytesMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new JMSBytesMessage();
+ }
+
+ /**
+ * Acknowledges all unacknowledged messages on the session, for all message consumers on the session.
+ *
+ * @throws IllegalStateException If the session is closed.
+ */
+ public void acknowledge() throws IllegalStateException
+ {
+ if (isClosed())
+ {
+ throw new IllegalStateException("Session is already closed");
+ }
+
+ for (BasicMessageConsumer consumer : _consumers.values())
+ {
+ consumer.acknowledge();
+ }
+ }
+
+ /**
+ * Acknowledge one or many messages.
+ *
+ * @param deliveryTag The tag of the last message to be acknowledged.
+ * @param multiple <tt>true</tt> to acknowledge all messages up to and including the one specified by the
+ * delivery tag, <tt>false</tt> to just acknowledge that message.
+ *
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ public void acknowledgeMessage(long deliveryTag, boolean multiple)
+ {
+ final AMQFrame ackFrame =
+ BasicAckBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag,
+ multiple);
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Sending ack for delivery tag " + deliveryTag + " on channel " + _channelId);
+ }
+
+ getProtocolHandler().writeFrame(ackFrame);
+ }
+
+ /**
+ * Binds the named queue, with the specified routing key, to the named exchange.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param queueName The name of the queue to bind.
+ * @param routingKey The routing key to bind the queue with.
+ * @param arguments Additional arguments.
+ * @param exchangeName The exchange to bind the queue on.
+ *
+ * @throws AMQException If the queue cannot be bound for any reason.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ * @todo Document the additional arguments that may be passed in the field table. Are these for headers exchanges?
+ */
+ public void bindQueue(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments,
+ final AMQShortString exchangeName) throws AMQException
+ {
+ /*new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()*/
+ new FailoverNoopSupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()
+ {
+ public Object execute() throws AMQException, FailoverException
+ {
+ AMQFrame queueBind =
+ QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(),
+ arguments, // arguments
+ exchangeName, // exchange
+ false, // nowait
+ queueName, // queue
+ routingKey, // routingKey
+ getTicket()); // ticket
+
+ getProtocolHandler().syncWrite(queueBind, QueueBindOkBody.class);
+
+ return null;
+ }
+ }, _connection).execute();
+ }
+
+ /**
+ * Closes the session.
+ *
+ * <p/>Note that this operation succeeds automatically if a fail-over interupts the sycnronous request to close
+ * the channel. This is because the channel is marked as closed before the request to close it is made, so the
+ * fail-over should not re-open it.
+ *
+ * @param timeout The timeout in milliseconds to wait for the session close acknoledgement from the broker.
+ *
+ * @throws JMSException If the JMS provider fails to close the session due to some internal error.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ * @todo Not certain about the logic of ignoring the failover exception, because the channel won't be
+ * re-opened. May need to examine this more carefully.
+ * @todo Note that taking the failover mutex doesn't prevent this operation being interrupted by a failover,
+ * because the failover process sends the failover event before acquiring the mutex itself.
+ */
+ public void close(long timeout) throws JMSException
+ {
+ if (_logger.isInfoEnabled())
+ {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ _logger.info("Closing session: " + this + ":"
+ + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
+ }
+
+ synchronized (_connection.getFailoverMutex())
+ {
+ // 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)
+ {
+ // Ensure we only try and close an open session.
+ if (!_closed.getAndSet(true))
+ {
+ // we pass null since this is not an error case
+ closeProducersAndConsumers(null);
+
+ try
+ {
+
+ getProtocolHandler().closeSession(this);
+
+ final AMQFrame frame =
+ ChannelCloseBody.createAMQFrame(getChannelId(), getProtocolMajorVersion(), getProtocolMinorVersion(),
+ 0, // classId
+ 0, // methodId
+ AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
+ new AMQShortString("JMS client closing channel")); // replyText
+
+ getProtocolHandler().syncWrite(frame, ChannelCloseOkBody.class, timeout);
+
+ // When control resumes at this point, a reply will have been received that
+ // indicates the broker has closed the channel successfully.
+ }
+ catch (AMQException e)
+ {
+ JMSException jmse = new JMSException("Error closing session: " + e);
+ jmse.setLinkedException(e);
+ throw jmse;
+ }
+ // This is ignored because the channel is already marked as closed so the fail-over process will
+ // not re-open it.
+ catch (FailoverException e)
+ {
+ _logger.debug(
+ "Got FailoverException during channel close, ignored as channel already marked as closed.");
+ }
+ finally
+ {
+ _connection.deregisterSession(_channelId);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the server initiates the closure of the session unilaterally.
+ *
+ * @param e the exception that caused this session to be closed. Null causes the
+ */
+ public void closed(Throwable e) throws JMSException
+ {
+ synchronized (_connection.getFailoverMutex())
+ {
+ if (e instanceof AMQDisconnectedException)
+ {
+ if (_dispatcher != null)
+ {
+ // Failover failed and ain't coming back. Knife the dispatcher.
+ _dispatcher.interrupt();
+ }
+ }
+ synchronized (_messageDeliveryLock)
+ {
+ // 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
+ {
+ amqe = new AMQException("Closing session forcibly", e);
+ }
+
+ _connection.deregisterSession(_channelId);
+ closeProducersAndConsumers(amqe);
+ }
+ }
+ }
+
+ /**
+ * Commits all messages done in this transaction and releases any locks currently held.
+ *
+ * <p/>If the commit fails, because the commit itself is interrupted by a fail-over between requesting that the
+ * commit be done, and receiving an acknowledgement that it has been done, then a JMSException will be thrown.
+ * The client will be unable to determine whether or not the commit actually happened on the broker in this case.
+ *
+ * @throws JMSException If the JMS provider fails to commit the transaction due to some internal error. This does
+ * not mean that the commit is known to have failed, merely that it is not known whether it
+ * failed or not.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ public void commit() throws JMSException
+ {
+ checkTransacted();
+
+ try
+ {
+ // Acknowledge up to message last delivered (if any) for each consumer.
+ // need to send ack for messages delivered to consumers so far
+ for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();)
+ {
+ // Sends acknowledgement to server
+ i.next().acknowledgeLastDelivered();
+ }
+
+ // Commits outstanding messages sent and outstanding acknowledgements.
+ final AMQProtocolHandler handler = getProtocolHandler();
+
+ handler.syncWrite(TxCommitBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion()),
+ TxCommitOkBody.class);
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException("Failed to commit: " + e.getMessage(), e);
+ }
+ catch (FailoverException e)
+ {
+ throw new JMSAMQException("Fail-over interrupted commit. Status of the commit is uncertain.", e);
+ }
+ }
+
+ public void confirmConsumerCancelled(AMQShortString consumerTag)
+ {
+
+ // Remove the consumer from the map
+ BasicMessageConsumer consumer = (BasicMessageConsumer) _consumers.get(consumerTag);
+ 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())
+ {
+ // Clean the Maps up first
+ // Flush any pending messages for this consumerTag
+ if (_dispatcher != null)
+ {
+ _logger.info("Dispatcher is not null");
+ }
+ else
+ {
+ _logger.info("Dispatcher is null so created stopped dispatcher");
+ startDistpatcherIfNecessary(true);
+ }
+
+ _dispatcher.rejectPending(consumer);
+ }
+ else
+ {
+ // Just close the consumer
+ // fixme the CancelOK is being processed before the arriving messages..
+ // The dispatcher is still to process them so the server sent in order but the client
+ // has yet to receive before the close comes in.
+
+ // consumer.markClosed();
+ }
+ }
+ else
+ {
+ _logger.warn("Unable to confirm cancellation of consumer (" + consumerTag + "). Not found in consumer map.");
+ }
+
+ }
+
+ public QueueBrowser createBrowser(Queue queue) throws JMSException
+ {
+ if (isStrictAMQP())
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ return createBrowser(queue, null);
+ }
+
+ public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException
+ {
+ if (isStrictAMQP())
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ checkNotClosed();
+ checkValidQueue(queue);
+
+ return new AMQQueueBrowser(this, (AMQQueue) queue, messageSelector);
+ }
+
+ public MessageConsumer createBrowserConsumer(Destination destination, String messageSelector, boolean noLocal)
+ throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false,
+ messageSelector, null, true, true);
+ }
+
+ public MessageConsumer createConsumer(Destination destination) throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, null, null,
+ false, false);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false,
+ messageSelector, null, false, false);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal)
+ throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false,
+ messageSelector, null, false, false);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive,
+ String selector) throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, null, false, false);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal,
+ boolean exclusive, String selector) throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null, false, false);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive,
+ String selector, FieldTable rawSelector) throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, rawSelector, false, false);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal,
+ boolean exclusive, String selector, FieldTable rawSelector) throws JMSException
+ {
+ checkValidDestination(destination);
+
+ return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, rawSelector, false,
+ false);
+ }
+
+ public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException
+ {
+
+ checkNotClosed();
+ AMQTopic origTopic = checkValidTopic(topic);
+ AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection);
+ TopicSubscriberAdaptor subscriber = _subscriptions.get(name);
+ if (subscriber != null)
+ {
+ if (subscriber.getTopic().equals(topic))
+ {
+ throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange "
+ + name);
+ }
+ else
+ {
+ unsubscribe(name);
+ }
+ }
+ else
+ {
+ AMQShortString topicName;
+ if (topic instanceof AMQTopic)
+ {
+ topicName = ((AMQTopic) topic).getDestinationName();
+ }
+ else
+ {
+ topicName = new AMQShortString(topic.getTopicName());
+ }
+
+ if (_strictAMQP)
+ {
+ if (_strictAMQPFATAL)
+ {
+ throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP.");
+ }
+ else
+ {
+ _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' "
+ + "for creation durableSubscriber. Requesting queue deletion regardless.");
+ }
+
+ deleteQueue(dest.getAMQQueueName());
+ }
+ else
+ {
+ // if the queue is bound to the exchange but NOT for this topic, then the JMS spec
+ // says we must trash the subscription.
+ if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName())
+ && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName))
+ {
+ deleteQueue(dest.getAMQQueueName());
+ }
+ }
+ }
+
+ subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest));
+
+ _subscriptions.put(name, subscriber);
+ _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name);
+
+ return subscriber;
+ }
+
+ /** Note, currently this does not handle reuse of the same name with different topics correctly. */
+ public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal)
+ throws JMSException
+ {
+ checkNotClosed();
+ checkValidTopic(topic);
+ AMQTopic dest = AMQTopic.createDurableTopic((AMQTopic) topic, name, _connection);
+ BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal);
+ TopicSubscriberAdaptor subscriber = new TopicSubscriberAdaptor(dest, consumer);
+ _subscriptions.put(name, subscriber);
+ _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name);
+
+ return subscriber;
+ }
+
+ public MapMessage createMapMessage() throws JMSException
+ {
+ checkNotClosed();
+ return new JMSMapMessage();
+ }
+
+ public javax.jms.Message createMessage() throws JMSException
+ {
+ return createBytesMessage();
+ }
+
+ public ObjectMessage createObjectMessage() throws JMSException
+ {
+ checkNotClosed();
+ return (ObjectMessage) new JMSObjectMessage();
+ }
+
+ public ObjectMessage createObjectMessage(Serializable object) throws JMSException
+ {
+ ObjectMessage msg = createObjectMessage();
+ msg.setObject(object);
+
+ return msg;
+ }
+
+ public BasicMessageProducer createProducer(Destination destination) throws JMSException
+ {
+ return createProducerImpl(destination, DEFAULT_MANDATORY, DEFAULT_IMMEDIATE);
+ }
+
+ public BasicMessageProducer createProducer(Destination destination, boolean immediate) throws JMSException
+ {
+ return createProducerImpl(destination, DEFAULT_MANDATORY, immediate);
+ }
+
+ public BasicMessageProducer createProducer(Destination destination, boolean mandatory, boolean immediate)
+ throws JMSException
+ {
+ return createProducerImpl(destination, mandatory, immediate);
+ }
+
+ public BasicMessageProducer createProducer(Destination destination, boolean mandatory, boolean immediate,
+ boolean waitUntilSent) throws JMSException
+ {
+ return createProducerImpl(destination, mandatory, immediate, waitUntilSent);
+ }
+
+ public TopicPublisher createPublisher(Topic topic) throws JMSException
+ {
+ checkNotClosed();
+
+ return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic), topic);
+ }
+
+ public Queue createQueue(String queueName) throws JMSException
+ {
+ checkNotClosed();
+ if (queueName.indexOf('/') == -1)
+ {
+ return new AMQQueue(getDefaultQueueExchangeName(), new AMQShortString(queueName));
+ }
+ else
+ {
+ try
+ {
+ return new AMQQueue(new AMQBindingURL(queueName));
+ }
+ catch (URLSyntaxException urlse)
+ {
+ JMSException jmse = new JMSException(urlse.getReason());
+ jmse.setLinkedException(urlse);
+
+ throw jmse;
+ }
+ }
+ }
+
+ /**
+ * Declares the named queue.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param name The name of the queue to declare.
+ * @param autoDelete
+ * @param durable Flag to indicate that the queue is durable.
+ * @param exclusive Flag to indicate that the queue is exclusive to this client.
+ *
+ * @throws AMQException If the queue cannot be declared for any reason.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable,
+ final boolean exclusive) throws AMQException
+ {
+ new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()
+ {
+ public Object execute() throws AMQException, FailoverException
+ {
+ AMQFrame queueDeclare =
+ QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(),
+ null, // arguments
+ autoDelete, // autoDelete
+ durable, // durable
+ exclusive, // exclusive
+ false, // nowait
+ false, // passive
+ name, // queue
+ getTicket()); // ticket
+
+ getProtocolHandler().syncWrite(queueDeclare, QueueDeclareOkBody.class);
+
+ return null;
+ }
+ }, _connection).execute();
+ }
+
+ /**
+ * Creates a QueueReceiver
+ *
+ * @param destination
+ *
+ * @return QueueReceiver - a wrapper around our MessageConsumer
+ *
+ * @throws JMSException
+ */
+ public QueueReceiver createQueueReceiver(Destination destination) throws JMSException
+ {
+ checkValidDestination(destination);
+ AMQQueue dest = (AMQQueue) destination;
+ BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(destination);
+
+ return new QueueReceiverAdaptor(dest, consumer);
+ }
+
+ /**
+ * Creates a QueueReceiver using a message selector
+ *
+ * @param destination
+ * @param messageSelector
+ *
+ * @return QueueReceiver - a wrapper around our MessageConsumer
+ *
+ * @throws JMSException
+ */
+ public QueueReceiver createQueueReceiver(Destination destination, String messageSelector) throws JMSException
+ {
+ checkValidDestination(destination);
+ AMQQueue dest = (AMQQueue) destination;
+ BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(destination, messageSelector);
+
+ return new QueueReceiverAdaptor(dest, consumer);
+ }
+
+ /**
+ * Creates a QueueReceiver wrapping a MessageConsumer
+ *
+ * @param queue
+ *
+ * @return QueueReceiver
+ *
+ * @throws JMSException
+ */
+ public QueueReceiver createReceiver(Queue queue) throws JMSException
+ {
+ checkNotClosed();
+ AMQQueue dest = (AMQQueue) queue;
+ BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest);
+
+ return new QueueReceiverAdaptor(dest, consumer);
+ }
+
+ /**
+ * Creates a QueueReceiver wrapping a MessageConsumer using a message selector
+ *
+ * @param queue
+ * @param messageSelector
+ *
+ * @return QueueReceiver
+ *
+ * @throws JMSException
+ */
+ public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException
+ {
+ checkNotClosed();
+ AMQQueue dest = (AMQQueue) queue;
+ BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest, messageSelector);
+
+ return new QueueReceiverAdaptor(dest, consumer);
+ }
+
+ public QueueSender createSender(Queue queue) throws JMSException
+ {
+ checkNotClosed();
+
+ // return (QueueSender) createProducer(queue);
+ return new QueueSenderAdapter(createProducer(queue), queue);
+ }
+
+ public StreamMessage createStreamMessage() throws JMSException
+ {
+ synchronized (_connection.getFailoverMutex())
+ {
+ checkNotClosed();
+
+ return new JMSStreamMessage();
+ }
+ }
+
+ /**
+ * Creates a non-durable subscriber
+ *
+ * @param topic
+ *
+ * @return TopicSubscriber - a wrapper round our MessageConsumer
+ *
+ * @throws JMSException
+ */
+ public TopicSubscriber createSubscriber(Topic topic) throws JMSException
+ {
+ checkNotClosed();
+ AMQTopic dest = checkValidTopic(topic);
+
+ // AMQTopic dest = new AMQTopic(topic.getTopicName());
+ return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest));
+ }
+
+ /**
+ * Creates a non-durable subscriber with a message selector
+ *
+ * @param topic
+ * @param messageSelector
+ * @param noLocal
+ *
+ * @return TopicSubscriber - a wrapper round our MessageConsumer
+ *
+ * @throws JMSException
+ */
+ public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException
+ {
+ checkNotClosed();
+ AMQTopic dest = checkValidTopic(topic);
+
+ // AMQTopic dest = new AMQTopic(topic.getTopicName());
+ return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal));
+ }
+
+ public TemporaryQueue createTemporaryQueue() throws JMSException
+ {
+ checkNotClosed();
+
+ return new AMQTemporaryQueue(this);
+ }
+
+ public TemporaryTopic createTemporaryTopic() throws JMSException
+ {
+ checkNotClosed();
+
+ return new AMQTemporaryTopic(this);
+ }
+
+ public TextMessage createTextMessage() throws JMSException
+ {
+ synchronized (_connection.getFailoverMutex())
+ {
+ checkNotClosed();
+
+ return new JMSTextMessage();
+ }
+ }
+
+ public TextMessage createTextMessage(String text) throws JMSException
+ {
+
+ TextMessage msg = createTextMessage();
+ msg.setText(text);
+
+ return msg;
+ }
+
+ public Topic createTopic(String topicName) throws JMSException
+ {
+ checkNotClosed();
+
+ if (topicName.indexOf('/') == -1)
+ {
+ return new AMQTopic(getDefaultTopicExchangeName(), new AMQShortString(topicName));
+ }
+ else
+ {
+ try
+ {
+ return new AMQTopic(new AMQBindingURL(topicName));
+ }
+ catch (URLSyntaxException urlse)
+ {
+ JMSException jmse = new JMSException(urlse.getReason());
+ jmse.setLinkedException(urlse);
+
+ throw jmse;
+ }
+ }
+ }
+
+ public void declareExchange(AMQShortString name, AMQShortString type, boolean nowait) throws AMQException
+ {
+ declareExchange(name, type, getProtocolHandler(), nowait);
+ }
+
+ public int getAcknowledgeMode() throws JMSException
+ {
+ checkNotClosed();
+
+ return _acknowledgeMode;
+ }
+
+ public AMQConnection getAMQConnection()
+ {
+ return _connection;
+ }
+
+ public int getChannelId()
+ {
+ return _channelId;
+ }
+
+ public int getDefaultPrefetch()
+ {
+ return _defaultPrefetchHighMark;
+ }
+
+ public int getDefaultPrefetchHigh()
+ {
+ return _defaultPrefetchHighMark;
+ }
+
+ public int getDefaultPrefetchLow()
+ {
+ return _defaultPrefetchLowMark;
+ }
+
+ public AMQShortString getDefaultQueueExchangeName()
+ {
+ return _connection.getDefaultQueueExchangeName();
+ }
+
+ public AMQShortString getDefaultTopicExchangeName()
+ {
+ return _connection.getDefaultTopicExchangeName();
+ }
+
+ public MessageListener getMessageListener() throws JMSException
+ {
+ // checkNotClosed();
+ return _messageListener;
+ }
+
+ public AMQShortString getTemporaryQueueExchangeName()
+ {
+ return _connection.getTemporaryQueueExchangeName();
+ }
+
+ public AMQShortString getTemporaryTopicExchangeName()
+ {
+ return _connection.getTemporaryTopicExchangeName();
+ }
+
+ public int getTicket()
+ {
+ return _ticket;
+ }
+
+ public boolean getTransacted() throws JMSException
+ {
+ checkNotClosed();
+
+ return _transacted;
+ }
+
+ public boolean hasConsumer(Destination destination)
+ {
+ AtomicInteger counter = _destinationConsumerCount.get(destination);
+
+ return (counter != null) && (counter.get() != 0);
+ }
+
+ public boolean isStrictAMQP()
+ {
+ return _strictAMQP;
+ }
+
+ public boolean isSuspended()
+ {
+ return _suspended;
+ }
+
+ /**
+ * Invoked by the MINA IO thread (indirectly) when a message is received from the transport. Puts the message onto
+ * the queue read by the dispatcher.
+ *
+ * @param message the message that has been received
+ */
+ public void messageReceived(UnprocessedMessage message)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Message["
+ + ((message.getDeliverBody() == null) ? ("B:" + message.getBounceBody()) : ("D:" + message.getDeliverBody()))
+ + "] received in session with channel id " + _channelId);
+ }
+
+ if (message.getDeliverBody() == null)
+ {
+ // Return of the bounced message.
+ returnBouncedMessage(message);
+ }
+ else
+ {
+ _highestDeliveryTag.set(message.getDeliverBody().deliveryTag);
+ _queue.add(message);
+ }
+ }
+
+ /**
+ * Stops message delivery in this session, and restarts message delivery with the oldest unacknowledged message.
+ *
+ * <p/>All consumers deliver messages in a serial order. Acknowledging a received message automatically acknowledges all
+ * messages that have been delivered to the client.
+ *
+ * <p/>Restarting a session causes it to take the following actions:
+ *
+ * <ul>
+ * <li>Stop message delivery.</li>
+ * <li>Mark all messages that might have been delivered but not acknowledged as "redelivered".
+ * <li>Restart the delivery sequence including all unacknowledged messages that had been previously delivered.
+ * Redelivered messages do not have to be delivered in exactly their original delivery order.</li>
+ * </ul>
+ *
+ * <p/>If the recover operation is interrupted by a fail-over, between asking that the broker begin recovery and
+ * receiving acknolwedgement that it hasm then a JMSException will be thrown. In this case it will not be possible
+ * for the client to determine whether the broker is going to recover the session or not.
+ *
+ * @throws JMSException If the JMS provider fails to stop and restart message delivery due to some internal error.
+ * Not that this does not necessarily mean that the recovery has failed, but simply that it
+ * is not possible to tell if it has or not.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ public void recover() throws JMSException
+ {
+ // Ensure that the session is open.
+ checkNotClosed();
+
+ // Ensure that the session is not transacted.
+ checkNotTransacted();
+
+ // this is set only here, and the before the consumer's onMessage is called it is set to false
+ _inRecovery = true;
+ try
+ {
+
+ boolean isSuspended = isSuspended();
+
+ if (!isSuspended)
+ {
+ suspendChannel(true);
+ }
+
+ for (BasicMessageConsumer consumer : _consumers.values())
+ {
+ consumer.clearUnackedMessages();
+ }
+
+ if (_dispatcher != null)
+ {
+ _dispatcher.rollback();
+ }
+
+ if (isStrictAMQP())
+ {
+ // We can't use the BasicRecoverBody-OK method as it isn't part of the spec.
+ _connection.getProtocolHandler().writeFrame(BasicRecoverBody.createAMQFrame(_channelId,
+ getProtocolMajorVersion(), getProtocolMinorVersion(), false)); // requeue
+ _logger.warn("Session Recover cannot be guaranteed with STRICT_AMQP. Messages may arrive out of order.");
+ }
+ else
+ {
+
+ _connection.getProtocolHandler().syncWrite(
+ BasicRecoverBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false) // requeue
+ , BasicRecoverOkBody.class);
+ }
+
+ if (!isSuspended)
+ {
+ suspendChannel(false);
+ }
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException("Recover failed: " + e.getMessage(), e);
+ }
+ catch (FailoverException e)
+ {
+ throw new JMSAMQException("Recovery was interrupted by fail-over. Recovery status is not known.", e);
+ }
+ }
+
+ public void rejectMessage(UnprocessedMessage message, boolean requeue)
+ {
+
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Rejecting Unacked message:" + message.getDeliverBody().deliveryTag);
+ }
+
+ rejectMessage(message.getDeliverBody().deliveryTag, requeue);
+ }
+
+ public void rejectMessage(AbstractJMSMessage message, boolean requeue)
+ {
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Rejecting Abstract message:" + message.getDeliveryTag());
+ }
+
+ rejectMessage(message.getDeliveryTag(), requeue);
+
+ }
+
+ public void rejectMessage(long deliveryTag, boolean requeue)
+ {
+ if ((_acknowledgeMode == CLIENT_ACKNOWLEDGE) || (_acknowledgeMode == SESSION_TRANSACTED))
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Rejecting delivery tag:" + deliveryTag + ":SessionHC:" + this.hashCode());
+ }
+
+ AMQFrame basicRejectBody =
+ BasicRejectBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag,
+ requeue);
+
+ _connection.getProtocolHandler().writeFrame(basicRejectBody);
+ }
+ }
+
+ /**
+ * Commits all messages done in this transaction and releases any locks currently held.
+ *
+ * <p/>If the rollback fails, because the rollback itself is interrupted by a fail-over between requesting that the
+ * rollback be done, and receiving an acknowledgement that it has been done, then a JMSException will be thrown.
+ * The client will be unable to determine whether or not the rollback actually happened on the broker in this case.
+ *
+ * @throws JMSException If the JMS provider fails to rollback the transaction due to some internal error. This does
+ * not mean that the rollback is known to have failed, merely that it is not known whether it
+ * failed or not.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ public void rollback() throws JMSException
+ {
+ synchronized (_suspensionLock)
+ {
+ checkTransacted();
+
+ try
+ {
+ boolean isSuspended = isSuspended();
+
+ if (!isSuspended)
+ {
+ suspendChannel(true);
+ }
+
+ if (_dispatcher != null)
+ {
+ _dispatcher.rollback();
+ }
+
+ _connection.getProtocolHandler().syncWrite(TxRollbackBody.createAMQFrame(_channelId,
+ getProtocolMajorVersion(), getProtocolMinorVersion()), TxRollbackOkBody.class);
+
+ if (!isSuspended)
+ {
+ suspendChannel(false);
+ }
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException("Failed to rollback: " + e, e);
+ }
+ catch (FailoverException e)
+ {
+ throw new JMSAMQException("Fail-over interrupted rollback. Status of the rollback is uncertain.", e);
+ }
+ }
+ }
+
+ public void run()
+ {
+ throw new java.lang.UnsupportedOperationException();
+ }
+
+ public void setMessageListener(MessageListener listener) throws JMSException
+ {
+ // checkNotClosed();
+ //
+ // if (_dispatcher != null && !_dispatcher.connectionStopped())
+ // {
+ // throw new javax.jms.IllegalStateException("Attempt to set listener while session is started.");
+ // }
+ //
+ // // We are stopped
+ // for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();)
+ // {
+ // BasicMessageConsumer consumer = i.next();
+ //
+ // if (consumer.isReceiving())
+ // {
+ // throw new javax.jms.IllegalStateException("Another thread is already receiving synchronously.");
+ // }
+ // }
+ //
+ // _messageListener = listener;
+ //
+ // for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();)
+ // {
+ // i.next().setMessageListener(_messageListener);
+ // }
+
+ }
+
+ /*public void setTicket(int ticket)
+ {
+ _ticket = ticket;
+ }*/
+
+ public void unsubscribe(String name) throws JMSException
+ {
+ checkNotClosed();
+ TopicSubscriberAdaptor subscriber = _subscriptions.get(name);
+ if (subscriber != null)
+ {
+ // send a queue.delete for the subscription
+ deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection));
+ _subscriptions.remove(name);
+ _reverseSubscriptionMap.remove(subscriber);
+ }
+ else
+ {
+ if (_strictAMQP)
+ {
+ if (_strictAMQPFATAL)
+ {
+ throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP.");
+ }
+ else
+ {
+ _logger.warn("Unable to determine if subscription already exists for '" + name + "' for unsubscribe."
+ + " Requesting queue deletion regardless.");
+ }
+
+ deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection));
+ }
+ else
+ {
+
+ if (isQueueBound(getDefaultTopicExchangeName(), AMQTopic.getDurableTopicQueueName(name, _connection)))
+ {
+ deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection));
+ }
+ else
+ {
+ throw new InvalidDestinationException("Unknown subscription exchange:" + name);
+ }
+ }
+ }
+ }
+
+ protected MessageConsumer createConsumerImpl(final Destination destination, final int prefetchHigh,
+ final int prefetchLow, final boolean noLocal, final boolean exclusive, String selector, final FieldTable rawSelector,
+ final boolean noConsume, final boolean autoClose) throws JMSException
+ {
+ checkTemporaryDestination(destination);
+
+ final String messageSelector;
+
+ if (_strictAMQP && !((selector == null) || selector.equals("")))
+ {
+ if (_strictAMQPFATAL)
+ {
+ throw new UnsupportedOperationException("Selectors not currently supported by AMQP.");
+ }
+ else
+ {
+ messageSelector = null;
+ }
+ }
+ else
+ {
+ messageSelector = selector;
+ }
+
+ return new FailoverRetrySupport<MessageConsumer, JMSException>(
+ new FailoverProtectedOperation<MessageConsumer, JMSException>()
+ {
+ public MessageConsumer execute() throws JMSException, FailoverException
+ {
+ checkNotClosed();
+
+ AMQDestination amqd = (AMQDestination) destination;
+
+ final AMQProtocolHandler protocolHandler = getProtocolHandler();
+ // TODO: Define selectors in AMQP
+ // TODO: construct the rawSelector from the selector string if rawSelector == null
+ final FieldTable ft = FieldTableFactory.newFieldTable();
+ // if (rawSelector != null)
+ // ft.put("headers", rawSelector.getDataAsBytes());
+ if (rawSelector != null)
+ {
+ ft.addAll(rawSelector);
+ }
+
+ BasicMessageConsumer consumer =
+ new BasicMessageConsumer(_channelId, _connection, amqd, messageSelector, noLocal,
+ _messageFactoryRegistry, AMQSession.this, protocolHandler, ft, prefetchHigh, prefetchLow,
+ exclusive, _acknowledgeMode, noConsume, autoClose);
+
+ if (_messageListener != null)
+ {
+ consumer.setMessageListener(_messageListener);
+ }
+
+ try
+ {
+ registerConsumer(consumer, false);
+ }
+ catch (AMQInvalidArgumentException ise)
+ {
+ JMSException ex = new InvalidSelectorException(ise.getMessage());
+ ex.setLinkedException(ise);
+ throw ex;
+ }
+ catch (AMQInvalidRoutingKeyException e)
+ {
+ JMSException ide =
+ new InvalidDestinationException("Invalid routing key:" + amqd.getRoutingKey().toString());
+ ide.setLinkedException(e);
+ throw ide;
+ }
+ catch (AMQException e)
+ {
+ JMSException ex = new JMSException("Error registering consumer: " + e);
+
+ if (_logger.isDebugEnabled())
+ {
+ e.printStackTrace();
+ }
+
+ ex.setLinkedException(e);
+ throw ex;
+ }
+
+ synchronized (destination)
+ {
+ _destinationConsumerCount.putIfAbsent(destination, new AtomicInteger());
+ _destinationConsumerCount.get(destination).incrementAndGet();
+ }
+
+ return consumer;
+ }
+ }, _connection).execute();
+ }
+
+ /**
+ * Called by the MessageConsumer when closing, to deregister the consumer from the map from consumerTag to consumer
+ * instance.
+ *
+ * @param consumer the consum
+ */
+ void deregisterConsumer(BasicMessageConsumer consumer)
+ {
+ if (_consumers.remove(consumer.getConsumerTag()) != null)
+ {
+ String subscriptionName = _reverseSubscriptionMap.remove(consumer);
+ if (subscriptionName != null)
+ {
+ _subscriptions.remove(subscriptionName);
+ }
+
+ Destination dest = consumer.getDestination();
+ synchronized (dest)
+ {
+ if (_destinationConsumerCount.get(dest).decrementAndGet() == 0)
+ {
+ _destinationConsumerCount.remove(dest);
+ }
+ }
+ }
+ }
+
+ void deregisterProducer(long producerId)
+ {
+ _producers.remove(new Long(producerId));
+ }
+
+ boolean isInRecovery()
+ {
+ return _inRecovery;
+ }
+
+ boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName) throws JMSException
+ {
+ return isQueueBound(exchangeName, queueName, null);
+ }
+
+ /**
+ * Tests whether or not the specified queue is bound to the specified exchange under a particular routing key.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param exchangeName The exchange name to test for binding against.
+ * @param queueName The queue name to check if bound.
+ * @param routingKey The routing key to check if the queue is bound under.
+ *
+ * @return <tt>true</tt> if the queue is bound to the exchange and routing key, <tt>false</tt> if not.
+ *
+ * @throws JMSException If the query fails for any reason.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey)
+ throws JMSException
+ {
+ try
+ {
+ AMQMethodEvent response =
+ new FailoverRetrySupport<AMQMethodEvent, AMQException>(
+ new FailoverProtectedOperation<AMQMethodEvent, AMQException>()
+ {
+ public AMQMethodEvent execute() throws AMQException, FailoverException
+ {
+ AMQFrame boundFrame =
+ ExchangeBoundBody.createAMQFrame(_channelId, getProtocolMajorVersion(),
+ getProtocolMinorVersion(), exchangeName, // exchange
+ queueName, // queue
+ routingKey); // routingKey
+
+ return getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class);
+
+ }
+ }, _connection).execute();
+
+ // Extract and return the response code from the query.
+ ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod();
+
+ return (responseBody.replyCode == 0);
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException("Queue bound query failed: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Called to mark the session as being closed. Useful when the session needs to be made invalid, e.g. after failover
+ * when the client has veoted resubscription. <p/> The caller of this method must already hold the failover mutex.
+ */
+ void markClosed()
+ {
+ _closed.set(true);
+ _connection.deregisterSession(_channelId);
+ markClosedProducersAndConsumers();
+
+ }
+
+ /**
+ * Resubscribes all producers and consumers. This is called when performing failover.
+ *
+ * @throws AMQException
+ */
+ void resubscribe() throws AMQException
+ {
+ resubscribeProducers();
+ resubscribeConsumers();
+ }
+
+ void setHasMessageListeners()
+ {
+ _hasMessageListeners = true;
+ }
+
+ void setInRecovery(boolean inRecovery)
+ {
+ _inRecovery = inRecovery;
+ }
+
+ /**
+ * Starts the session, which ensures that it is not suspended and that its event dispatcher is running.
+ *
+ * @throws AMQException If the session cannot be started for any reason.
+ * @todo This should be controlled by _stopped as it pairs with the stop method fixme or check the
+ * FlowControlledBlockingQueue _queue to see if we have flow controlled. will result in sending Flow messages
+ * for each subsequent call to flow.. only need to do this if we have called stop.
+ */
+ void start() throws AMQException
+ {
+ // Check if the session has perviously been started and suspended, in which case it must be unsuspended.
+ if (_startedAtLeastOnce.getAndSet(true))
+ {
+ suspendChannel(false);
+ }
+
+ // If the event dispatcher is not running then start it too.
+ if (hasMessageListeners())
+ {
+ startDistpatcherIfNecessary();
+ }
+ }
+
+ void startDistpatcherIfNecessary()
+ {
+ //If we are the dispatcher then we don't need to check we are started
+ if (Thread.currentThread() == _dispatcher)
+ {
+ return;
+ }
+
+ // If IMMEDIATE_PREFETCH is not set then we need to start fetching
+ // This is final per session so will be multi-thread safe.
+ if (!_immediatePrefetch)
+ {
+ // We do this now if this is the first call on a started connection
+ if (isSuspended() && _startedAtLeastOnce.get() && _firstDispatcher.getAndSet(false))
+ {
+ try
+ {
+ suspendChannel(false);
+ }
+ catch (AMQException e)
+ {
+ _logger.info("Unsuspending channel threw an exception:" + e);
+ }
+ }
+ }
+
+ startDistpatcherIfNecessary(false);
+ }
+
+ synchronized void startDistpatcherIfNecessary(boolean initiallyStopped)
+ {
+ if (_dispatcher == null)
+ {
+ _dispatcher = new Dispatcher();
+ _dispatcher.setDaemon(true);
+ _dispatcher.setConnectionStopped(initiallyStopped);
+ _dispatcher.start();
+ }
+ else
+ {
+ _dispatcher.setConnectionStopped(initiallyStopped);
+ }
+ }
+
+ void stop() throws AMQException
+ {
+ // Stop the server delivering messages to this session.
+ suspendChannel(true);
+
+ if (_dispatcher != null)
+ {
+ _dispatcher.setConnectionStopped(true);
+ }
+ }
+
+ /*
+ * Binds the named queue, with the specified routing key, to the named exchange.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param queueName The name of the queue to bind.
+ * @param routingKey The routing key to bind the queue with.
+ * @param arguments Additional arguments.
+ * @param exchangeName The exchange to bind the queue on.
+ *
+ * @throws AMQException If the queue cannot be bound for any reason.
+ */
+ /*private void bindQueue(AMQDestination amqd, AMQShortString queueName, AMQProtocolHandler protocolHandler, FieldTable ft)
+ throws AMQException, FailoverException
+ {
+ AMQFrame queueBind =
+ QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), ft, // arguments
+ amqd.getExchangeName(), // exchange
+ false, // nowait
+ queueName, // queue
+ amqd.getRoutingKey(), // routingKey
+ getTicket()); // ticket
+
+ protocolHandler.syncWrite(queueBind, QueueBindOkBody.class);
+ }*/
+
+ private void checkNotTransacted() throws JMSException
+ {
+ if (getTransacted())
+ {
+ throw new IllegalStateException("Session is transacted");
+ }
+ }
+
+ private void checkTemporaryDestination(Destination destination) throws JMSException
+ {
+ if ((destination instanceof TemporaryDestination))
+ {
+ _logger.debug("destination is temporary");
+ final TemporaryDestination tempDest = (TemporaryDestination) destination;
+ if (tempDest.getSession() != this)
+ {
+ _logger.debug("destination is on different session");
+ throw new JMSException("Cannot consume from a temporary destination created onanother session");
+ }
+
+ if (tempDest.isDeleted())
+ {
+ _logger.debug("destination is deleted");
+ throw new JMSException("Cannot consume from a deleted destination");
+ }
+ }
+ }
+
+ private void checkTransacted() throws JMSException
+ {
+ if (!getTransacted())
+ {
+ throw new IllegalStateException("Session is not transacted");
+ }
+ }
+
+ private void checkValidDestination(Destination destination) throws InvalidDestinationException
+ {
+ if (destination == null)
+ {
+ throw new javax.jms.InvalidDestinationException("Invalid Queue");
+ }
+ }
+
+ private void checkValidQueue(Queue queue) throws InvalidDestinationException
+ {
+ if (queue == null)
+ {
+ throw new javax.jms.InvalidDestinationException("Invalid Queue");
+ }
+ }
+
+ /*
+ * I could have combined the last 3 methods, but this way it improves readability
+ */
+ private AMQTopic checkValidTopic(Topic topic) throws JMSException
+ {
+ if (topic == null)
+ {
+ throw new javax.jms.InvalidDestinationException("Invalid Topic");
+ }
+
+ if ((topic instanceof TemporaryDestination) && (((TemporaryDestination) topic).getSession() != this))
+ {
+ throw new javax.jms.InvalidDestinationException(
+ "Cannot create a subscription on a temporary topic created in another session");
+ }
+
+ if (!(topic instanceof AMQTopic))
+ {
+ throw new javax.jms.InvalidDestinationException(
+ "Cannot create a subscription on topic created for another JMS Provider, class of topic provided is: "
+ + topic.getClass().getName());
+ }
+
+ return (AMQTopic) topic;
+ }
+
+ /**
+ * Called to close message consumers cleanly. This may or may <b>not</b> be as a result of an error.
+ *
+ * @param error not null if this is a result of an error occurring at the connection level
+ */
+ private void closeConsumers(Throwable error) throws JMSException
+ {
+ // we need to clone the list of consumers since the close() method updates the _consumers collection
+ // which would result in a concurrent modification exception
+ final ArrayList<BasicMessageConsumer> clonedConsumers = new ArrayList<BasicMessageConsumer>(_consumers.values());
+
+ final Iterator<BasicMessageConsumer> it = clonedConsumers.iterator();
+ while (it.hasNext())
+ {
+ final BasicMessageConsumer con = it.next();
+ if (error != null)
+ {
+ con.notifyError(error);
+ }
+ else
+ {
+ con.close(false);
+ }
+ }
+ // at this point the _consumers map will be empty
+ if (_dispatcher != null)
+ {
+ _dispatcher.close();
+ _dispatcher = null;
+ }
+ }
+
+ /**
+ * Called to close message producers cleanly. This may or may <b>not</b> be as a result of an error. There is
+ * currently no way of propagating errors to message producers (this is a JMS limitation).
+ */
+ private void closeProducers() throws JMSException
+ {
+ // we need to clone the list of producers since the close() method updates the _producers collection
+ // which would result in a concurrent modification exception
+ final ArrayList clonedProducers = new ArrayList(_producers.values());
+
+ final Iterator it = clonedProducers.iterator();
+ while (it.hasNext())
+ {
+ final BasicMessageProducer prod = (BasicMessageProducer) it.next();
+ prod.close();
+ }
+ // at this point the _producers map is empty
+ }
+
+ /**
+ * Close all producers or consumers. This is called either in the error case or when closing the session normally.
+ *
+ * @param amqe the exception, may be null to indicate no error has occurred
+ */
+ private void closeProducersAndConsumers(AMQException amqe) throws JMSException
+ {
+ JMSException jmse = null;
+ try
+ {
+ closeProducers();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error closing session: " + e, e);
+ jmse = e;
+ }
+
+ try
+ {
+ closeConsumers(amqe);
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error closing session: " + e, e);
+ if (jmse == null)
+ {
+ jmse = e;
+ }
+ }
+
+ if (jmse != null)
+ {
+ throw jmse;
+ }
+ }
+
+ /**
+ * Register to consume from the queue.
+ *
+ * @param queueName
+ */
+ private void consumeFromQueue(BasicMessageConsumer consumer, AMQShortString queueName,
+ AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector) throws AMQException, FailoverException
+ {
+ // need to generate a consumer tag on the client so we can exploit the nowait flag
+ AMQShortString tag = new AMQShortString(Integer.toString(_nextTag++));
+
+ FieldTable arguments = FieldTableFactory.newFieldTable();
+ if ((messageSelector != null) && !messageSelector.equals(""))
+ {
+ arguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), messageSelector);
+ }
+
+ if (consumer.isAutoClose())
+ {
+ arguments.put(AMQPFilterTypes.AUTO_CLOSE.getValue(), Boolean.TRUE);
+ }
+
+ if (consumer.isNoConsume())
+ {
+ arguments.put(AMQPFilterTypes.NO_CONSUME.getValue(), Boolean.TRUE);
+ }
+
+ consumer.setConsumerTag(tag);
+ // we must register the consumer in the map before we actually start listening
+ _consumers.put(tag, consumer);
+
+ try
+ {
+ // TODO: Be aware of possible changes to parameter order as versions change.
+ AMQFrame jmsConsume =
+ BasicConsumeBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), arguments, // arguments
+ tag, // consumerTag
+ consumer.isExclusive(), // exclusive
+ consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, // noAck
+ consumer.isNoLocal(), // noLocal
+ nowait, // nowait
+ queueName, // queue
+ getTicket()); // ticket
+
+ if (nowait)
+ {
+ protocolHandler.writeFrame(jmsConsume);
+ }
+ else
+ {
+ protocolHandler.syncWrite(jmsConsume, BasicConsumeOkBody.class);
+ }
+ }
+ catch (AMQException e)
+ {
+ // clean-up the map in the event of an error
+ _consumers.remove(tag);
+ throw e;
+ }
+ }
+
+ private BasicMessageProducer createProducerImpl(Destination destination, boolean mandatory, boolean immediate)
+ throws JMSException
+ {
+ return createProducerImpl(destination, mandatory, immediate, false);
+ }
+
+ private BasicMessageProducer createProducerImpl(final Destination destination, final boolean mandatory,
+ final boolean immediate, final boolean waitUntilSent) throws JMSException
+ {
+ return new FailoverRetrySupport<BasicMessageProducer, JMSException>(
+ new FailoverProtectedOperation<BasicMessageProducer, JMSException>()
+ {
+ public BasicMessageProducer execute() throws JMSException, FailoverException
+ {
+ checkNotClosed();
+ long producerId = getNextProducerId();
+ BasicMessageProducer producer =
+ new BasicMessageProducer(_connection, (AMQDestination) destination, _transacted, _channelId,
+ AMQSession.this, getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent);
+ registerProducer(producerId, producer);
+
+ return producer;
+ }
+ }, _connection).execute();
+ }
+
+ private void declareExchange(AMQDestination amqd, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException
+ {
+ declareExchange(amqd.getExchangeName(), amqd.getExchangeClass(), protocolHandler, nowait);
+ }
+
+ /**
+ * Declares the named exchange and type of exchange.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param name The name of the exchange to declare.
+ * @param type The type of the exchange to declare.
+ * @param protocolHandler The protocol handler to process the communication through.
+ * @param nowait
+ *
+ * @throws AMQException If the exchange cannot be declared for any reason.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ private void declareExchange(final AMQShortString name, final AMQShortString type,
+ final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException
+ {
+ new FailoverNoopSupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()
+ {
+ public Object execute() throws AMQException, FailoverException
+ {
+ AMQFrame exchangeDeclare =
+ ExchangeDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(),
+ null, // arguments
+ false, // autoDelete
+ false, // durable
+ name, // exchange
+ false, // internal
+ nowait, // nowait
+ false, // passive
+ getTicket(), // ticket
+ type); // type
+
+ protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class);
+
+ return null;
+ }
+ }, _connection).execute();
+ }
+
+ /**
+ * Declares a queue for a JMS destination.
+ *
+ * <p/>Note that for queues but not topics the name is generated in the client rather than the server. This allows
+ * the name to be reused on failover if required. In general, the destination indicates whether it wants a name
+ * generated or not.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param amqd The destination to declare as a queue.
+ * @param protocolHandler The protocol handler to communicate through.
+ *
+ * @return The name of the decalred queue. This is useful where the broker is generating a queue name on behalf of
+ * the client.
+ *
+ * @throws AMQException If the queue cannot be declared for any reason.
+ * @todo Verify the destiation is valid or throw an exception.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ private AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler)
+ throws AMQException
+ {
+ /*return new FailoverRetrySupport<AMQShortString, AMQException>(*/
+ return new FailoverNoopSupport<AMQShortString, AMQException>(
+ new FailoverProtectedOperation<AMQShortString, AMQException>()
+ {
+ public AMQShortString execute() throws AMQException, FailoverException
+ {
+ // Generate the queue name if the destination indicates that a client generated name is to be used.
+ if (amqd.isNameRequired())
+ {
+ amqd.setQueueName(protocolHandler.generateQueueName());
+ }
+
+ AMQFrame queueDeclare =
+ QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(),
+ null, // arguments
+ amqd.isAutoDelete(), // autoDelete
+ amqd.isDurable(), // durable
+ amqd.isExclusive(), // exclusive
+ false, // nowait
+ false, // passive
+ amqd.getAMQQueueName(), // queue
+ getTicket()); // ticket
+
+ protocolHandler.syncWrite(queueDeclare, QueueDeclareOkBody.class);
+
+ return amqd.getAMQQueueName();
+ }
+ }, _connection).execute();
+ }
+
+ /**
+ * Undeclares the specified queue.
+ *
+ * <p/>Note that this operation automatically retries in the event of fail-over.
+ *
+ * @param queueName The name of the queue to delete.
+ *
+ * @throws JMSException If the queue could not be deleted for any reason.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ private void deleteQueue(final AMQShortString queueName) throws JMSException
+ {
+ try
+ {
+ new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()
+ {
+ public Object execute() throws AMQException, FailoverException
+ {
+ AMQFrame queueDeleteFrame =
+ QueueDeleteBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(),
+ false, // ifEmpty
+ false, // ifUnused
+ true, // nowait
+ queueName, // queue
+ getTicket()); // ticket
+
+ getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class);
+
+ return null;
+ }
+ }, _connection).execute();
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException("The queue deletion failed: " + e.getMessage(), e);
+ }
+ }
+
+ private long getNextProducerId()
+ {
+ return ++_nextProducerId;
+ }
+
+ private AMQProtocolHandler getProtocolHandler()
+ {
+ return _connection.getProtocolHandler();
+ }
+
+ private byte getProtocolMajorVersion()
+ {
+ return getProtocolHandler().getProtocolMajorVersion();
+ }
+
+ private byte getProtocolMinorVersion()
+ {
+ return getProtocolHandler().getProtocolMinorVersion();
+ }
+
+ private boolean hasMessageListeners()
+ {
+ return _hasMessageListeners;
+ }
+
+ private void markClosedConsumers() throws JMSException
+ {
+ if (_dispatcher != null)
+ {
+ _dispatcher.close();
+ _dispatcher = null;
+ }
+ // we need to clone the list of consumers since the close() method updates the _consumers collection
+ // which would result in a concurrent modification exception
+ final ArrayList<BasicMessageConsumer> clonedConsumers = new ArrayList<BasicMessageConsumer>(_consumers.values());
+
+ final Iterator<BasicMessageConsumer> it = clonedConsumers.iterator();
+ while (it.hasNext())
+ {
+ final BasicMessageConsumer con = it.next();
+ con.markClosed();
+ }
+ // at this point the _consumers map will be empty
+ }
+
+ private void markClosedProducersAndConsumers()
+ {
+ try
+ {
+ // no need for a markClosed* method in this case since there is no protocol traffic closing a producer
+ closeProducers();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error closing session: " + e, e);
+ }
+
+ try
+ {
+ markClosedConsumers();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error closing session: " + e, e);
+ }
+ }
+
+ public void declareAndBind(AMQDestination amqd)
+ throws
+ AMQException
+ {
+ AMQProtocolHandler protocolHandler = getProtocolHandler();
+ declareExchange(amqd, protocolHandler, false);
+ AMQShortString queueName = declareQueue(amqd, protocolHandler);
+ bindQueue(queueName, amqd.getRoutingKey(), new FieldTable(), amqd.getExchangeName());
+ }
+
+ /**
+ * Callers must hold the failover mutex before calling this method.
+ *
+ * @param consumer
+ *
+ * @throws AMQException
+ */
+ private void registerConsumer(BasicMessageConsumer consumer, boolean nowait) throws AMQException // , FailoverException
+ {
+ AMQDestination amqd = consumer.getDestination();
+
+ AMQProtocolHandler protocolHandler = getProtocolHandler();
+
+ declareExchange(amqd, protocolHandler, false);
+
+ AMQShortString queueName = declareQueue(amqd, protocolHandler);
+
+ // bindQueue(amqd, queueName, protocolHandler, consumer.getRawSelectorFieldTable());
+ bindQueue(queueName, amqd.getRoutingKey(), consumer.getRawSelectorFieldTable(), amqd.getExchangeName());
+
+ // If IMMEDIATE_PREFETCH is not required then suspsend the channel to delay prefetch
+ if (!_immediatePrefetch)
+ {
+ // The dispatcher will be null if we have just created this session
+ // so suspend the channel before we register our consumer so that we don't
+ // start prefetching until a receive/mListener is set.
+ if (_dispatcher == null)
+ {
+ if (!isSuspended())
+ {
+ try
+ {
+ suspendChannel(true);
+ _logger.info(
+ "Prefetching delayed existing messages will not flow until requested via receive*() or setML().");
+ }
+ catch (AMQException e)
+ {
+ _logger.info("Suspending channel threw an exception:" + e);
+ }
+ }
+ }
+ }
+ else
+ {
+ _logger.info("Immediately prefetching existing messages to new consumer.");
+ }
+
+ try
+ {
+ consumeFromQueue(consumer, queueName, protocolHandler, nowait, consumer.getMessageSelector());
+ }
+ catch (JMSException e) // thrown by getMessageSelector
+ {
+ throw new AMQException(e.getMessage(), e);
+ }
+ catch (FailoverException e)
+ {
+ throw new AMQException("Fail-over exception interrupted basic consume.", e);
+ }
+ }
+
+ private void registerProducer(long producerId, MessageProducer producer)
+ {
+ _producers.put(new Long(producerId), producer);
+ }
+
+ /**
+ * @param consumerTag The consumerTag to prune from queue or all if null
+ * @param requeue Should the removed messages be requeued (or discarded. Possibly to DLQ)
+ */
+
+ private void rejectMessagesForConsumerTag(AMQShortString consumerTag, boolean requeue)
+ {
+ Iterator messages = _queue.iterator();
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Rejecting messages from _queue for Consumer tag(" + consumerTag + ") (PDispatchQ) requeue:"
+ + requeue);
+
+ if (messages.hasNext())
+ {
+ _logger.info("Checking all messages in _queue for Consumer tag(" + consumerTag + ")");
+ }
+ else
+ {
+ _logger.info("No messages in _queue to reject");
+ }
+ }
+ while (messages.hasNext())
+ {
+ UnprocessedMessage message = (UnprocessedMessage) messages.next();
+
+ if ((consumerTag == null) || message.getDeliverBody().consumerTag.equals(consumerTag))
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Removing message(" + System.identityHashCode(message) + ") from _queue DT:"
+ + message.getDeliverBody().deliveryTag);
+ }
+
+ messages.remove();
+
+ rejectMessage(message, requeue);
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Rejected the message(" + message.getDeliverBody() + ") for consumer :" + consumerTag);
+ }
+ }
+ }
+ }
+
+ private void resubscribeConsumers() throws AMQException
+ {
+ ArrayList consumers = new ArrayList(_consumers.values());
+ _consumers.clear();
+
+ for (Iterator it = consumers.iterator(); it.hasNext();)
+ {
+ BasicMessageConsumer consumer = (BasicMessageConsumer) it.next();
+ registerConsumer(consumer, true);
+ }
+ }
+
+ private void resubscribeProducers() throws AMQException
+ {
+ ArrayList producers = new ArrayList(_producers.values());
+ _logger.info(MessageFormat.format("Resubscribing producers = {0} producers.size={1}", producers, producers.size())); // FIXME: removeKey
+ for (Iterator it = producers.iterator(); it.hasNext();)
+ {
+ BasicMessageProducer producer = (BasicMessageProducer) it.next();
+ producer.resubscribe();
+ }
+ }
+
+ private void returnBouncedMessage(final UnprocessedMessage message)
+ {
+ _connection.performConnectionTask(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ // Bounced message is processed here, away from the mina thread
+ AbstractJMSMessage bouncedMessage =
+ _messageFactoryRegistry.createMessage(0, false, message.getBounceBody().exchange,
+ message.getBounceBody().routingKey, message.getContentHeader(), message.getBodies());
+
+ AMQConstant errorCode = AMQConstant.getConstant(message.getBounceBody().replyCode);
+ AMQShortString reason = message.getBounceBody().replyText;
+ _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")");
+
+ // @TODO should this be moved to an exception handler of sorts. Somewhere errors are converted to correct execeptions.
+ if (errorCode == AMQConstant.NO_CONSUMERS)
+ {
+ _connection.exceptionReceived(new AMQNoConsumersException("Error: " + reason, bouncedMessage));
+ }
+ else if (errorCode == AMQConstant.NO_ROUTE)
+ {
+ _connection.exceptionReceived(new AMQNoRouteException("Error: " + reason, bouncedMessage));
+ }
+ else
+ {
+ _connection.exceptionReceived(
+ new AMQUndeliveredException(errorCode, "Error: " + reason, bouncedMessage));
+ }
+
+ }
+ catch (Exception e)
+ {
+ _logger.error(
+ "Caught exception trying to raise undelivered message exception (dump follows) - ignoring...",
+ e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Suspends or unsuspends this session.
+ *
+ * @param suspend <tt>true</tt> indicates that the session should be suspended, <tt>false<tt> indicates that it
+ * should be unsuspended.
+ *
+ * @throws AMQException If the session cannot be suspended for any reason.
+ * @todo Be aware of possible changes to parameter order as versions change.
+ */
+ private void suspendChannel(boolean suspend) throws AMQException // , FailoverException
+ {
+ synchronized (_suspensionLock)
+ {
+ try
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Setting channel flow : " + (suspend ? "suspended" : "unsuspended"));
+ }
+
+ _suspended = suspend;
+
+ AMQFrame channelFlowFrame =
+ ChannelFlowBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(),
+ !suspend);
+
+ _connection.getProtocolHandler().syncWrite(channelFlowFrame, ChannelFlowOkBody.class);
+ }
+ catch (FailoverException e)
+ {
+ throw new AMQException("Fail-over interrupted suspend/unsuspend channel.", e);
+ }
+ }
+ }
+
+ Object getMessageDeliveryLock()
+ {
+ return _messageDeliveryLock;
+ }
+
+ /** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */
+ private class Dispatcher extends Thread
+ {
+
+ /** Track the 'stopped' state of the dispatcher, a session starts in the stopped state. */
+ private final AtomicBoolean _dispatcherClosed = new AtomicBoolean(false);
+
+ private final Object _lock = new Object();
+ private final AtomicLong _rollbackMark = new AtomicLong(-1);
+ private String dispatcherID = "" + System.identityHashCode(this);
+
+ public Dispatcher()
+ {
+ super("Dispatcher-Channel-" + _channelId);
+ if (_dispatcherLogger.isInfoEnabled())
+ {
+ _dispatcherLogger.info(getName() + " created");
+ }
+ }
+
+ public void close()
+ {
+ _dispatcherClosed.set(true);
+ interrupt();
+
+ // fixme awaitTermination
+
+ }
+
+ public void rejectPending(BasicMessageConsumer consumer)
+ {
+ synchronized (_lock)
+ {
+ boolean stopped = _dispatcher.connectionStopped();
+
+ if (!stopped)
+ {
+ _dispatcher.setConnectionStopped(true);
+ }
+
+ // Reject messages on pre-receive queue
+ consumer.rollback();
+
+ // Reject messages on pre-dispatch queue
+ rejectMessagesForConsumerTag(consumer.getConsumerTag(), true);
+ //Let the dispatcher deal with this when it gets to them.
+
+ // closeConsumer
+ consumer.markClosed();
+
+ _dispatcher.setConnectionStopped(stopped);
+
+ }
+ }
+
+ public void rollback()
+ {
+
+ synchronized (_lock)
+ {
+ boolean isStopped = connectionStopped();
+
+ if (!isStopped)
+ {
+ setConnectionStopped(true);
+ }
+
+ _rollbackMark.set(_highestDeliveryTag.get());
+
+ _dispatcherLogger.debug("Session Pre Dispatch Queue cleared");
+
+ for (BasicMessageConsumer consumer : _consumers.values())
+ {
+ if (!consumer.isNoConsume())
+ {
+ consumer.rollback();
+ }
+ else
+ {
+ // cClear the _SQ here.
+ consumer.clearReceiveQueue();
+ }
+
+ }
+
+ setConnectionStopped(isStopped);
+ }
+
+ }
+
+ public void run()
+ {
+ if (_dispatcherLogger.isInfoEnabled())
+ {
+ _dispatcherLogger.info(getName() + " started");
+ }
+
+ UnprocessedMessage message;
+
+ // Allow disptacher to start stopped
+ synchronized (_lock)
+ {
+ while (!_closed.get() && connectionStopped())
+ {
+ try
+ {
+ _lock.wait(2000);
+ }
+ catch (InterruptedException e)
+ {
+ // ignore
+ }
+ }
+ }
+
+ try
+ {
+ while (!_dispatcherClosed.get())
+ {
+ message = (UnprocessedMessage) _queue.poll(1000, TimeUnit.MILLISECONDS);
+ if (message != null)
+ {
+ synchronized (_lock)
+ {
+
+ while (connectionStopped())
+ {
+ _lock.wait(2000);
+ }
+
+ if (message.getDeliverBody().deliveryTag <= _rollbackMark.get())
+ {
+ rejectMessage(message, true);
+ }
+ else
+ {
+ synchronized (_messageDeliveryLock)
+ {
+ dispatchMessage(message);
+ }
+ }
+
+ }
+ }
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // ignore
+ }
+
+ if (_dispatcherLogger.isInfoEnabled())
+ {
+ _dispatcherLogger.info(getName() + " thread terminating for channel " + _channelId);
+ }
+ }
+
+ // only call while holding lock
+ final boolean connectionStopped()
+ {
+ return _connectionStopped;
+ }
+
+ boolean setConnectionStopped(boolean connectionStopped)
+ {
+ boolean currently;
+ synchronized (_lock)
+ {
+ currently = _connectionStopped;
+ _connectionStopped = connectionStopped;
+ _lock.notify();
+
+ if (_dispatcherLogger.isDebugEnabled())
+ {
+ _dispatcherLogger.debug("Set Dispatcher Connection " + (connectionStopped ? "Stopped" : "Started")
+ + ": Currently " + (currently ? "Stopped" : "Started"));
+ }
+ }
+
+ return currently;
+ }
+
+ private void dispatchMessage(UnprocessedMessage message)
+ {
+ if (message.getDeliverBody() != null)
+ {
+ final BasicMessageConsumer consumer =
+ (BasicMessageConsumer) _consumers.get(message.getDeliverBody().consumerTag);
+
+ if ((consumer == null) || consumer.isClosed())
+ {
+ if (_dispatcherLogger.isInfoEnabled())
+ {
+ if (consumer == null)
+ {
+ _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ")" + "["
+ + message.getDeliverBody().deliveryTag + "] from queue "
+ + message.getDeliverBody().consumerTag + " )without a handler - rejecting(requeue)...");
+ }
+ else
+ {
+ _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ") ["
+ + message.getDeliverBody().deliveryTag + "] from queue consumer("
+ + consumer.debugIdentity() + ") is closed rejecting(requeue)...");
+ }
+ }
+ // Don't reject if we're already closing
+ if (!_dispatcherClosed.get())
+ {
+ rejectMessage(message, true);
+ }
+ }
+ else
+ {
+ consumer.notifyMessage(message, _channelId);
+ }
+ }
+ }
+ }
+
+ /*public void requestAccess(AMQShortString realm, boolean exclusive, boolean passive, boolean active, boolean write,
+ boolean read) throws AMQException
+ {
+ getProtocolHandler().writeCommandFrameAndWaitForReply(AccessRequestBody.createAMQFrame(getChannelId(),
+ getProtocolMajorVersion(), getProtocolMinorVersion(), active, exclusive, passive, read, realm, write),
+ new BlockingMethodFrameListener(_channelId)
+ {
+
+ public boolean processMethod(int channelId, AMQMethodBody frame) // throws AMQException
+ {
+ if (frame instanceof AccessRequestOkBody)
+ {
+ setTicket(((AccessRequestOkBody) frame).getTicket());
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ });
+ }*/
+
+ private class SuspenderRunner implements Runnable
+ {
+ private boolean _suspend;
+
+ public SuspenderRunner(boolean suspend)
+ {
+ _suspend = suspend;
+ }
+
+ public void run()
+ {
+ try
+ {
+ suspendChannel(_suspend);
+ }
+ catch (AMQException e)
+ {
+ _logger.warn("Unable to suspend channel");
+ }
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java
new file mode 100644
index 0000000000..93f10761e2
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+public interface AMQSessionAdapter
+{
+ public AMQSession getSession();
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java
new file mode 100644
index 0000000000..f54cb782c8
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java
@@ -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.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.JMSException;
+import javax.jms.TemporaryQueue;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import java.util.Random;
+import java.util.UUID;
+
+/** AMQ implementation of a TemporaryQueue. */
+final class AMQTemporaryQueue extends AMQQueue implements TemporaryQueue, TemporaryDestination
+{
+
+
+ private final AMQSession _session;
+ private boolean _deleted;
+
+ /** Create a new instance of an AMQTemporaryQueue */
+ public AMQTemporaryQueue(AMQSession session)
+ {
+ super(session.getTemporaryQueueExchangeName(), new AMQShortString("TempQueue" + UUID.randomUUID()), true);
+ _session = session;
+ }
+
+ /** @see javax.jms.TemporaryQueue#delete() */
+ public synchronized void delete() throws JMSException
+ {
+ if (_session.hasConsumer(this))
+ {
+ throw new JMSException("Temporary Queue has consumers so cannot be deleted");
+ }
+ _deleted = true;
+
+ // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted
+ // by the server when there are no more subscriptions to that queue. This is probably not
+ // quite right for JMSCompliance.
+ }
+
+ public AMQSession getSession()
+ {
+ return _session;
+ }
+
+ public boolean isDeleted()
+ {
+ return _deleted;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java
new file mode 100644
index 0000000000..7b5781530b
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.jms.JMSException;
+import javax.jms.TemporaryTopic;
+import java.util.UUID;
+
+/**
+ * AMQ implementation of TemporaryTopic.
+ */
+class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic, TemporaryDestination
+{
+
+ private final AMQSession _session;
+ private boolean _deleted;
+ /**
+ * Create new temporary topic.
+ */
+ public AMQTemporaryTopic(AMQSession session)
+ {
+ super(session.getTemporaryTopicExchangeName(),new AMQShortString("tmp_" + UUID.randomUUID()));
+ _session = session;
+ }
+
+ /**
+ * @see javax.jms.TemporaryTopic#delete()
+ */
+ public void delete() throws JMSException
+ {
+ if(_session.hasConsumer(this))
+ {
+ throw new JMSException("Temporary Topic has consumers so cannot be deleted");
+ }
+
+ _deleted = true;
+ // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted
+ // by the server when there are no more subscriptions to that queue. This is probably not
+ // quite right for JMSCompliance.
+ }
+
+ public AMQSession getSession()
+ {
+ return _session;
+ }
+
+ public boolean isDeleted()
+ {
+ return _deleted;
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java
new file mode 100644
index 0000000000..319e728edf
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.JMSException;
+import javax.jms.Topic;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.url.BindingURL;
+
+public class AMQTopic extends AMQDestination implements Topic
+{
+ /**
+ * Constructor for use in creating a topic using a BindingURL.
+ *
+ * @param binding The binding url object.
+ */
+ public AMQTopic(BindingURL binding)
+ {
+ super(binding);
+ }
+
+// public AMQTopic(String exchangeName, String routingKey)
+// {
+// this(new AMQShortString(exchangeName), new AMQShortString(routingKey));
+// }
+
+ public AMQTopic(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName)
+ {
+ super(exchange, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, routingKey, true, true, queueName, false);
+ }
+
+ public AMQTopic(AMQConnection conn, String routingKey)
+ {
+ this(conn.getDefaultTopicExchangeName(), new AMQShortString(routingKey));
+ }
+
+
+ public AMQTopic(AMQShortString exchangeName, String routingKey)
+ {
+ this(exchangeName, new AMQShortString(routingKey));
+ }
+
+ public AMQTopic(AMQShortString exchangeName, AMQShortString routingKey)
+ {
+ this(exchangeName, routingKey, null);
+ }
+
+ public AMQTopic(AMQShortString exchangeName, AMQShortString name, boolean isAutoDelete, AMQShortString queueName, boolean isDurable)
+ {
+ super(exchangeName, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, name, true, isAutoDelete,
+ queueName, isDurable);
+ }
+
+ public static AMQTopic createDurableTopic(AMQTopic topic, String subscriptionName, AMQConnection connection)
+ throws JMSException
+ {
+ return new AMQTopic(topic.getExchangeName(), topic.getDestinationName(), false,
+ getDurableTopicQueueName(subscriptionName, connection),
+ true);
+ }
+
+ public static AMQShortString getDurableTopicQueueName(String subscriptionName, AMQConnection connection) throws JMSException
+ {
+ return new AMQShortString(connection.getClientID() + ":" + subscriptionName);
+ }
+
+ public String getTopicName() throws JMSException
+ {
+ return super.getDestinationName().toString();
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return getDestinationName();
+ }
+
+ public boolean isNameRequired()
+ {
+ // Topics always rely on a server generated queue name.
+ return false;
+ }
+
+ /**
+ * Override since the queue is always private and we must ensure it remains null. If not,
+ * reuse of the topic when registering consumers will make all consumers listen on the same (private) queue rather
+ * than getting their own (private) queue.
+ * <p/>
+ * This is relatively nasty but it is difficult to come up with a more elegant solution, given
+ * the requirement in the case on AMQQueue and possibly other AMQDestination subclasses to
+ * use the underlying queue name even where it is server generated.
+ */
+ public void setQueueName(String queueName)
+ {
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java
new file mode 100644
index 0000000000..f44f8414fa
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java
@@ -0,0 +1,226 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import java.io.Serializable;
+
+import javax.jms.BytesMessage;
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.QueueBrowser;
+import javax.jms.Session;
+import javax.jms.StreamMessage;
+import javax.jms.TemporaryQueue;
+import javax.jms.TemporaryTopic;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+
+public class AMQTopicSessionAdaptor implements TopicSession, AMQSessionAdapter
+{
+ protected final AMQSession _session;
+
+ public AMQTopicSessionAdaptor(Session session)
+ {
+ _session = (AMQSession) session;
+ }
+
+ public Topic createTopic(String string) throws JMSException
+ {
+ return _session.createTopic(string);
+ }
+
+ public TopicSubscriber createSubscriber(Topic topic) throws JMSException
+ {
+ return _session.createSubscriber(topic);
+ }
+
+ public TopicSubscriber createSubscriber(Topic topic, String string, boolean b) throws JMSException
+ {
+ return _session.createSubscriber(topic, string, b);
+ }
+
+ public TopicSubscriber createDurableSubscriber(Topic topic, String string) throws JMSException
+ {
+ return _session.createDurableSubscriber(topic, string);
+ }
+
+ public TopicSubscriber createDurableSubscriber(Topic topic, String string, String string1, boolean b) throws JMSException
+ {
+ return _session.createDurableSubscriber(topic, string, string1, b);
+ }
+
+ public TopicPublisher createPublisher(Topic topic) throws JMSException
+ {
+ return _session.createPublisher(topic);
+ }
+
+ public TemporaryTopic createTemporaryTopic() throws JMSException
+ {
+ return _session.createTemporaryTopic();
+ }
+
+ public void unsubscribe(String string) throws JMSException
+ {
+ _session.unsubscribe(string);
+ }
+
+ public BytesMessage createBytesMessage() throws JMSException
+ {
+ return _session.createBytesMessage();
+ }
+
+ public MapMessage createMapMessage() throws JMSException
+ {
+ return _session.createMapMessage();
+ }
+
+ public Message createMessage() throws JMSException
+ {
+ return _session.createMessage();
+ }
+
+ public ObjectMessage createObjectMessage() throws JMSException
+ {
+ return _session.createObjectMessage();
+ }
+
+ public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException
+ {
+ return _session.createObjectMessage();
+ }
+
+ public StreamMessage createStreamMessage() throws JMSException
+ {
+ return _session.createStreamMessage();
+ }
+
+ public TextMessage createTextMessage() throws JMSException
+ {
+ return _session.createTextMessage();
+ }
+
+ public TextMessage createTextMessage(String string) throws JMSException
+ {
+ return _session.createTextMessage(string);
+ }
+
+ public boolean getTransacted() throws JMSException
+ {
+ return _session.getTransacted();
+ }
+
+ public int getAcknowledgeMode() throws JMSException
+ {
+ return _session.getAcknowledgeMode();
+ }
+
+ public void commit() throws JMSException
+ {
+ _session.commit();
+ }
+
+ public void rollback() throws JMSException
+ {
+ _session.rollback();
+ }
+
+ public void close() throws JMSException
+ {
+ _session.close();
+ }
+
+ public void recover() throws JMSException
+ {
+ _session.recover();
+ }
+
+ public MessageListener getMessageListener() throws JMSException
+ {
+ return _session.getMessageListener();
+ }
+
+ public void setMessageListener(MessageListener messageListener) throws JMSException
+ {
+ _session.setMessageListener(messageListener);
+ }
+
+ public void run()
+ {
+ _session.run();
+ }
+
+ public MessageProducer createProducer(Destination destination) throws JMSException
+ {
+ return _session.createProducer(destination);
+ }
+
+ public MessageConsumer createConsumer(Destination destination) throws JMSException
+ {
+ return _session.createConsumer(destination);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, String string) throws JMSException
+ {
+ return _session.createConsumer(destination, string);
+ }
+
+ public MessageConsumer createConsumer(Destination destination, String string, boolean b) throws JMSException
+ {
+ return _session.createConsumer(destination, string, b);
+ }
+
+ //The following methods cannot be called from a TopicSession as per JMS spec
+ public Queue createQueue(String string) throws JMSException
+ {
+ throw new IllegalStateException("Cannot call createQueue from TopicSession");
+ }
+
+ public QueueBrowser createBrowser(Queue queue) throws JMSException
+ {
+ throw new IllegalStateException("Cannot call createBrowser from TopicSession");
+ }
+
+ public QueueBrowser createBrowser(Queue queue, String string) throws JMSException
+ {
+ throw new IllegalStateException("Cannot call createBrowser from TopicSession");
+ }
+
+ public TemporaryQueue createTemporaryQueue() throws JMSException
+ {
+ throw new IllegalStateException("Cannot call createTemporaryQueue from TopicSession");
+ }
+
+ public AMQSession getSession()
+ {
+ return _session;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java b/Final/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java
new file mode 100644
index 0000000000..0f3723c58b
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.framing.AMQShortString;
+
+public class AMQUndefinedDestination extends AMQDestination
+{
+
+ private static final AMQShortString UNKNOWN_EXCHANGE_CLASS = new AMQShortString("unknown");
+
+
+ public AMQUndefinedDestination(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName)
+ {
+ super(exchange, UNKNOWN_EXCHANGE_CLASS, routingKey, queueName);
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return getDestinationName();
+ }
+
+ public boolean isNameRequired()
+ {
+ return getAMQQueueName() == null;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
new file mode 100644
index 0000000000..ddaf0cfd93
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
@@ -0,0 +1,996 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.client.message.AbstractJMSMessage;
+import org.apache.qpid.client.message.MessageFactoryRegistry;
+import org.apache.qpid.client.message.UnprocessedMessage;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.framing.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.jms.MessageConsumer;
+import org.apache.qpid.jms.Session;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+public 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 String _messageSelector;
+
+ private boolean _noLocal;
+
+ private AMQDestination _destination;
+
+ /** When true indicates that a blocking receive call is in progress */
+ private final AtomicBoolean _receiving = new AtomicBoolean(false);
+ /** Holds an atomic reference to the listener installed. */
+ private final AtomicReference<MessageListener> _messageListener = new AtomicReference<MessageListener>();
+
+ /** The consumer tag allows us to close the consumer by sending a jmsCancel method to the broker */
+ private AMQShortString _consumerTag;
+
+ /** We need to know the channel id when constructing frames */
+ private int _channelId;
+
+ /**
+ * Used in the blocking receive methods to receive a message from the Session thread. <p/> Or to notify of errors
+ * <p/> Argument true indicates we want strict FIFO semantics
+ */
+ private final ArrayBlockingQueue _synchronousQueue;
+
+ private MessageFactoryRegistry _messageFactory;
+
+ private final AMQSession _session;
+
+ private 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;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /** We store the exclusive field in order to be able to reuse it when resubscribing in the event of failover */
+ private 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 ConcurrentLinkedQueue<Long> _unacknowledgedDeliveryTags = new ConcurrentLinkedQueue<Long>();
+
+ /** List of tags delievered, The last of which which should be acknowledged on commit in transaction mode. */
+ private ConcurrentLinkedQueue<Long> _receivedDeliveryTags = new ConcurrentLinkedQueue<Long>();
+
+ /**
+ * The thread that was used to call receive(). This is important for being able to interrupt that thread if a
+ * receive() is in progress.
+ */
+ private Thread _receivingThread;
+
+ /**
+ * 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 boolean _noConsume;
+ private List<StackTraceElement> _closedStack = null;
+
+ protected BasicMessageConsumer(int channelId, AMQConnection connection, AMQDestination destination,
+ String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session,
+ AMQProtocolHandler protocolHandler, FieldTable rawSelectorFieldTable, int prefetchHigh, int prefetchLow,
+ boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose)
+ {
+ _channelId = channelId;
+ _connection = connection;
+ _messageSelector = messageSelector;
+ _noLocal = noLocal;
+ _destination = destination;
+ _messageFactory = messageFactory;
+ _session = session;
+ _protocolHandler = protocolHandler;
+ _rawSelectorFieldTable = rawSelectorFieldTable;
+ _prefetchHigh = prefetchHigh;
+ _prefetchLow = prefetchLow;
+ _exclusive = exclusive;
+ _acknowledgeMode = acknowledgeMode;
+ _synchronousQueue = new ArrayBlockingQueue(prefetchHigh, true);
+ _autoClose = autoClose;
+ _noConsume = noConsume;
+
+ // Force queue browsers not to use acknowledge modes.
+ if (_noConsume)
+ {
+ _acknowledgeMode = Session.NO_ACKNOWLEDGE;
+ }
+ }
+
+ public AMQDestination getDestination()
+ {
+ return _destination;
+ }
+
+ public String getMessageSelector() throws JMSException
+ {
+ checkPreConditions();
+
+ return _messageSelector;
+ }
+
+ public MessageListener getMessageListener() throws JMSException
+ {
+ checkPreConditions();
+
+ return _messageListener.get();
+ }
+
+ public int getAcknowledgeMode()
+ {
+ return _acknowledgeMode;
+ }
+
+ private boolean isMessageListenerSet()
+ {
+ return _messageListener.get() != null;
+ }
+
+ public void setMessageListener(final MessageListener messageListener) throws JMSException
+ {
+ checkPreConditions();
+
+ // if the current listener is non-null and the session is not stopped, then
+ // it is an error to call this method.
+
+ // i.e. it is only valid to call this method if
+ //
+ // (a) the connection is stopped, in which case the dispatcher is not running
+ // OR
+ // (b) the listener is null AND we are not receiving synchronously at present
+ //
+
+ if (!_session.getAMQConnection().started())
+ {
+ _messageListener.set(messageListener);
+ _session.setHasMessageListeners();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Session stopped : Message listener(" + messageListener + ") set for destination "
+ + _destination);
+ }
+ }
+ else
+ {
+ if (_receiving.get())
+ {
+ throw new javax.jms.IllegalStateException("Another thread is already receiving synchronously.");
+ }
+
+ if (!_messageListener.compareAndSet(null, messageListener))
+ {
+ throw new javax.jms.IllegalStateException("Attempt to alter listener while session is started.");
+ }
+
+ _logger.debug("Message listener set for destination " + _destination);
+
+ if (messageListener != null)
+ {
+ //todo: handle case where connection has already been started, and the dispatcher has alreaded started
+ // putting values on the _synchronousQueue
+
+ _messageListener.set(messageListener);
+ _session.setHasMessageListeners();
+ _session.startDistpatcherIfNecessary();
+ }
+ }
+ }
+
+ private void preApplicationProcessing(AbstractJMSMessage msg) throws JMSException
+ {
+
+ switch (_acknowledgeMode)
+ {
+
+ case Session.CLIENT_ACKNOWLEDGE:
+ _unacknowledgedDeliveryTags.add(msg.getDeliveryTag());
+ break;
+
+ case Session.SESSION_TRANSACTED:
+ if (isNoConsume())
+ {
+ _session.acknowledgeMessage(msg.getDeliveryTag(), false);
+ }
+ else
+ {
+ _logger.info("Recording tag for commit:" + msg.getDeliveryTag());
+ _receivedDeliveryTags.add(msg.getDeliveryTag());
+ }
+
+ break;
+ }
+
+ _session.setInRecovery(false);
+ }
+
+ private void acquireReceiving() throws JMSException
+ {
+ if (!_receiving.compareAndSet(false, true))
+ {
+ throw new javax.jms.IllegalStateException("Another thread is already receiving.");
+ }
+
+ if (isMessageListenerSet())
+ {
+ throw new javax.jms.IllegalStateException("A listener has already been set.");
+ }
+
+ _receivingThread = Thread.currentThread();
+ }
+
+ private void releaseReceiving()
+ {
+ _receiving.set(false);
+ _receivingThread = null;
+ }
+
+ public FieldTable getRawSelectorFieldTable()
+ {
+ return _rawSelectorFieldTable;
+ }
+
+ public int getPrefetch()
+ {
+ return _prefetchHigh;
+ }
+
+ public int getPrefetchHigh()
+ {
+ return _prefetchHigh;
+ }
+
+ public int getPrefetchLow()
+ {
+ return _prefetchLow;
+ }
+
+ public boolean isNoLocal()
+ {
+ return _noLocal;
+ }
+
+ public boolean isExclusive()
+ {
+ return _exclusive;
+ }
+
+ public boolean isReceiving()
+ {
+ return _receiving.get();
+ }
+
+ public Message receive() throws JMSException
+ {
+ return receive(0);
+ }
+
+ public Message receive(long l) throws JMSException
+ {
+
+ checkPreConditions();
+
+ acquireReceiving();
+
+ _session.startDistpatcherIfNecessary();
+
+ try
+ {
+ if (closeOnAutoClose())
+ {
+ return null;
+ }
+
+ Object o = null;
+ if (l > 0)
+ {
+ long endtime = System.currentTimeMillis() + l;
+ while (System.currentTimeMillis() < endtime && o == null)
+ {
+ try
+ {
+ o = _synchronousQueue.poll(endtime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ _logger.warn("Interrupted: " + e);
+ if (isClosed())
+ {
+ return null;
+ }
+ }
+ }
+ }
+ else
+ {
+ while (o == null)
+ {
+ try
+ {
+ o = _synchronousQueue.take();
+ }
+ catch (InterruptedException e)
+ {
+ _logger.warn("Interrupted: " + e);
+ if (isClosed())
+ {
+ return null;
+ }
+ }
+ }
+ }
+ final AbstractJMSMessage m = returnMessageOrThrow(o);
+ if (m != null)
+ {
+ preApplicationProcessing(m);
+ postDeliver(m);
+ }
+ return m;
+ }
+ finally
+ {
+ releaseReceiving();
+ }
+ }
+
+ private boolean closeOnAutoClose() throws JMSException
+ {
+ if (isAutoClose() && _closeWhenNoMessages && _synchronousQueue.isEmpty())
+ {
+ close(false);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public Message receiveNoWait() throws JMSException
+ {
+ checkPreConditions();
+
+ acquireReceiving();
+
+ _session.startDistpatcherIfNecessary();
+
+ try
+ {
+ if (closeOnAutoClose())
+ {
+ return null;
+ }
+
+ Object o = _synchronousQueue.poll();
+ final AbstractJMSMessage m = returnMessageOrThrow(o);
+ if (m != null)
+ {
+ preApplicationProcessing(m);
+ postDeliver(m);
+ }
+
+ return m;
+ }
+ finally
+ {
+ releaseReceiving();
+ }
+ }
+
+ /**
+ * 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
+ *
+ * @return a message only if o is a Message
+ *
+ * @throws JMSException if the argument is a throwable. If it is a JMSException it is rethrown as is, but if not a
+ * JMSException is created with the linked exception set appropriately
+ */
+ private AbstractJMSMessage returnMessageOrThrow(Object o) throws JMSException
+ {
+ // errors are passed via the queue too since there is no way of interrupting the poll() via the API.
+ if (o instanceof Throwable)
+ {
+ JMSException e = new JMSException("Message consumer forcibly closed due to error: " + o);
+ if (o instanceof Exception)
+ {
+ e.setLinkedException((Exception) o);
+ }
+
+ throw e;
+ }
+ else
+ {
+ return (AbstractJMSMessage) o;
+ }
+ }
+
+ public void close() throws JMSException
+ {
+ close(true);
+ }
+
+ 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 (_logger.isTraceEnabled())
+ {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ if (_closedStack != null)
+ {
+ _logger.trace(_consumerTag + " previously:" + _closedStack.toString());
+ }
+ else
+ {
+ _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1);
+ }
+ }
+
+ if (sendClose)
+ {
+ // TODO: Be aware of possible changes to parameter order as versions change.
+ final AMQFrame cancelFrame =
+ BasicCancelBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(),
+ _protocolHandler.getProtocolMinorVersion(), _consumerTag, // consumerTag
+ false); // nowait
+
+ try
+ {
+ _protocolHandler.syncWrite(cancelFrame, BasicCancelOkBody.class);
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("CancelOk'd for consumer:" + debugIdentity());
+ }
+
+ }
+ catch (AMQException e)
+ {
+ throw new JMSAMQException("Error closing consumer: " + e, e);
+ }
+ catch (FailoverException e)
+ {
+ throw new JMSAMQException("FailoverException interrupted basic cancel.", e);
+ }
+ }
+ else
+ {
+ // //fixme this probably is not right
+ // if (!isNoConsume())
+ { // done in BasicCancelOK Handler but not sending one so just deregister.
+ deregisterConsumer();
+ }
+ }
+
+ if ((_messageListener != null) && _receiving.get())
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Interrupting thread: " + _receivingThread);
+ }
+
+ _receivingThread.interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when you need to invalidate a consumer. Used for example when failover has occurred and the client has
+ * vetoed automatic resubscription. The caller must hold the failover mutex.
+ */
+ void markClosed()
+ {
+ // synchronized (_closed)
+ {
+ _closed.set(true);
+
+ if (_logger.isTraceEnabled())
+ {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ if (_closedStack != null)
+ {
+ _logger.trace(_consumerTag + " markClosed():"
+ + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
+ _logger.trace(_consumerTag + " previously:" + _closedStack.toString());
+ }
+ else
+ {
+ _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1);
+ }
+ }
+ }
+
+ deregisterConsumer();
+ }
+
+ /**
+ * Called from the AMQSession when a message has arrived for this consumer. This methods handles both the case of a
+ * 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)
+ {
+ final boolean debug = _logger.isDebugEnabled();
+
+ if (debug)
+ {
+ _logger.debug("notifyMessage called with message number " + messageFrame.getDeliverBody().deliveryTag);
+ }
+
+ try
+ {
+ AbstractJMSMessage jmsMessage =
+ _messageFactory.createMessage(messageFrame.getDeliverBody().deliveryTag,
+ messageFrame.getDeliverBody().redelivered, messageFrame.getDeliverBody().exchange,
+ messageFrame.getDeliverBody().routingKey, messageFrame.getContentHeader(), messageFrame.getBodies());
+
+ if (debug)
+ {
+ _logger.debug("Message is of type: " + jmsMessage.getClass().getName());
+ }
+ // synchronized (_closed)
+
+ {
+ // if (!_closed.get())
+ {
+
+ jmsMessage.setConsumer(this);
+
+ preDeliver(jmsMessage);
+
+ notifyMessage(jmsMessage, channelId);
+ }
+ // else
+ // {
+ // _logger.error("MESSAGE REJECTING!");
+ // _session.rejectMessage(jmsMessage, true);
+ // //_logger.error("MESSAGE JUST DROPPED!");
+ // }
+ }
+ }
+ catch (Exception e)
+ {
+ if (e instanceof InterruptedException)
+ {
+ _logger.info("SynchronousQueue.put interupted. Usually result of connection closing");
+ }
+ else
+ {
+ _logger.error("Caught exception (dump follows) - ignoring...", e);
+ }
+ }
+ }
+
+ /**
+ * @param jmsMessage this message has already been processed so can't redo preDeliver
+ * @param channelId
+ */
+ public void notifyMessage(AbstractJMSMessage jmsMessage, int channelId)
+ {
+ try
+ {
+ if (isMessageListenerSet())
+ {
+ // we do not need a lock around the test above, and the dispatch below as it is invalid
+ // for an application to alter an installed listener while the session is started
+ // synchronized (_closed)
+ {
+ // if (!_closed.get())
+ {
+
+ preApplicationProcessing(jmsMessage);
+ getMessageListener().onMessage(jmsMessage);
+ postDeliver(jmsMessage);
+ }
+ }
+ }
+ else
+ {
+ _synchronousQueue.put(jmsMessage);
+ }
+ }
+ catch (Exception e)
+ {
+ if (e instanceof InterruptedException)
+ {
+ _logger.info("reNotification : SynchronousQueue.put interupted. Usually result of connection closing");
+ }
+ else
+ {
+ _logger.error("reNotification : Caught exception (dump follows) - ignoring...", e);
+ }
+ }
+ }
+
+ private void preDeliver(AbstractJMSMessage msg)
+ {
+ switch (_acknowledgeMode)
+ {
+
+ case Session.PRE_ACKNOWLEDGE:
+ _session.acknowledgeMessage(msg.getDeliveryTag(), false);
+ break;
+
+ case Session.CLIENT_ACKNOWLEDGE:
+ // we set the session so that when the user calls acknowledge() it can call the method on session
+ // to send out the appropriate frame
+ msg.setAMQSession(_session);
+ break;
+ }
+ }
+
+ private void postDeliver(AbstractJMSMessage msg) throws JMSException
+ {
+ msg.setJMSDestination(_destination);
+ switch (_acknowledgeMode)
+ {
+
+ case Session.CLIENT_ACKNOWLEDGE:
+ if (isNoConsume())
+ {
+ _session.acknowledgeMessage(msg.getDeliveryTag(), false);
+ }
+
+ break;
+
+ case Session.DUPS_OK_ACKNOWLEDGE:
+ if (++_outstanding >= _prefetchHigh)
+ {
+ _dups_ok_acknowledge_send = true;
+ }
+
+ if (_outstanding <= _prefetchLow)
+ {
+ _dups_ok_acknowledge_send = false;
+ }
+
+ if (_dups_ok_acknowledge_send)
+ {
+ if (!_session.isInRecovery())
+ {
+ _session.acknowledgeMessage(msg.getDeliveryTag(), true);
+ }
+ }
+
+ break;
+
+ case Session.AUTO_ACKNOWLEDGE:
+ // we do not auto ack a message if the application code called recover()
+ if (!_session.isInRecovery())
+ {
+ _session.acknowledgeMessage(msg.getDeliveryTag(), false);
+ }
+
+ break;
+ }
+ }
+
+ /** Acknowledge up to last message delivered (if any). Used when commiting. */
+ void acknowledgeLastDelivered()
+ {
+ if (!_receivedDeliveryTags.isEmpty())
+ {
+ long lastDeliveryTag = _receivedDeliveryTags.poll();
+
+ while (!_receivedDeliveryTags.isEmpty())
+ {
+ lastDeliveryTag = _receivedDeliveryTags.poll();
+ }
+
+ assert _receivedDeliveryTags.isEmpty();
+
+ _session.acknowledgeMessage(lastDeliveryTag, true);
+ }
+ }
+
+ void notifyError(Throwable cause)
+ {
+ // synchronized (_closed)
+ {
+ _closed.set(true);
+ if (_logger.isTraceEnabled())
+ {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ if (_closedStack != null)
+ {
+ _logger.trace(_consumerTag + " notifyError():"
+ + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1));
+ _logger.trace(_consumerTag + " previously" + _closedStack.toString());
+ }
+ else
+ {
+ _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1);
+ }
+ }
+ }
+ // QPID-293 can "request redelivery of this error through dispatcher"
+
+ // we have no way of propagating the exception to a message listener - a JMS limitation - so we
+ // deal with the case where we have a synchronous receive() waiting for a message to arrive
+ if (!isMessageListenerSet())
+ {
+ // offer only succeeds if there is a thread waiting for an item from the queue
+ if (_synchronousQueue.offer(cause))
+ {
+ _logger.debug("Passed exception to synchronous queue for propagation to receive()");
+ }
+ }
+
+ deregisterConsumer();
+ }
+
+ /**
+ * Perform cleanup to deregister this consumer. This occurs when closing the consumer in both the clean case and in
+ * the case of an error occurring.
+ */
+ private void deregisterConsumer()
+ {
+ _session.deregisterConsumer(this);
+ }
+
+ public AMQShortString getConsumerTag()
+ {
+ return _consumerTag;
+ }
+
+ public void setConsumerTag(AMQShortString consumerTag)
+ {
+ _consumerTag = consumerTag;
+ }
+
+ public AMQSession getSession()
+ {
+ return _session;
+ }
+
+ private void checkPreConditions() throws JMSException
+ {
+
+ this.checkNotClosed();
+
+ if ((_session == null) || _session.isClosed())
+ {
+ throw new javax.jms.IllegalStateException("Invalid Session");
+ }
+ }
+
+ public void acknowledge() // throws JMSException
+ {
+ if (!isClosed())
+ {
+
+ Iterator<Long> tags = _unacknowledgedDeliveryTags.iterator();
+ while (tags.hasNext())
+ {
+ _session.acknowledgeMessage(tags.next(), false);
+ tags.remove();
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("Consumer is closed");
+ }
+ }
+
+ /** Called on recovery to reset the list of delivery tags */
+ public void clearUnackedMessages()
+ {
+ _unacknowledgedDeliveryTags.clear();
+ }
+
+ public boolean isAutoClose()
+ {
+ return _autoClose;
+ }
+
+ public boolean isNoConsume()
+ {
+ 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();
+
+ if (!_receivedDeliveryTags.isEmpty())
+ {
+ _logger.debug("Rejecting received messages in _receivedDTs (RQ)");
+ }
+
+ // rollback received but not committed messages
+ while (!_receivedDeliveryTags.isEmpty())
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Rejecting the messages(" + _receivedDeliveryTags.size() + ") in _receivedDTs (RQ)"
+ + "for consumer with tag:" + _consumerTag);
+ }
+
+ Long tag = _receivedDeliveryTags.poll();
+
+ if (tag != null)
+ {
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Rejecting tag from _receivedDTs:" + tag);
+ }
+
+ _session.rejectMessage(tag, true);
+ }
+ }
+
+ if (!_receivedDeliveryTags.isEmpty())
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Queue _receivedDTs (RQ) was not empty after rejection");
+ }
+ }
+
+ // rollback pending messages
+ if (_synchronousQueue.size() > 0)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Rejecting the messages(" + _synchronousQueue.size() + ") in _syncQueue (PRQ)"
+ + "for consumer with tag:" + _consumerTag);
+ }
+
+ Iterator iterator = _synchronousQueue.iterator();
+
+ int initialSize = _synchronousQueue.size();
+
+ boolean removed = false;
+ while (iterator.hasNext())
+ {
+
+ Object o = iterator.next();
+ if (o instanceof AbstractJMSMessage)
+ {
+ _session.rejectMessage(((AbstractJMSMessage) o), true);
+
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Rejected message:" + ((AbstractJMSMessage) o).getDeliveryTag());
+ }
+
+ iterator.remove();
+ removed = true;
+
+ }
+ else
+ {
+ _logger.error("Queue contained a :" + o.getClass()
+ + " unable to reject as it is not an AbstractJMSMessage. Will be cleared");
+ iterator.remove();
+ removed = true;
+ }
+ }
+
+ if (removed && (initialSize == _synchronousQueue.size()))
+ {
+ _logger.error("Queue had content removed but didn't change in size." + initialSize);
+ }
+
+
+ if (_synchronousQueue.size() != 0)
+ {
+ _logger.warn("Queue was not empty after rejecting all messages Remaining:" + _synchronousQueue.size());
+ rollback();
+ }
+
+ clearReceiveQueue();
+ }
+ }
+
+ public String debugIdentity()
+ {
+ return String.valueOf(_consumerTag) + "[" + System.identityHashCode(this) + "]";
+ }
+
+ public void clearReceiveQueue()
+ {
+ _synchronousQueue.clear();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
new file mode 100644
index 0000000000..0ee4882ec2
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java
@@ -0,0 +1,691 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.message.AbstractJMSMessage;
+import org.apache.qpid.client.message.MessageConverter;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicConsumeBody;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.CompositeAMQDataBlock;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ExchangeDeclareBody;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.BytesMessage;
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.InvalidDestinationException;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.StreamMessage;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+
+import java.io.UnsupportedEncodingException;
+import java.util.UUID;
+
+public class BasicMessageProducer extends Closeable implements org.apache.qpid.jms.MessageProducer
+{
+ protected final Logger _logger = LoggerFactory.getLogger(getClass());
+
+ private AMQConnection _connection;
+
+ /**
+ * If true, messages will not get a timestamp.
+ */
+ private boolean _disableTimestamps;
+
+ /**
+ * Priority of messages created by this producer.
+ */
+ private int _messagePriority;
+
+ /**
+ * Time to live of messages. Specified in milliseconds but AMQ has 1 second resolution.
+ */
+ private long _timeToLive;
+
+ /**
+ * Delivery mode used for this producer.
+ */
+ private int _deliveryMode = DeliveryMode.PERSISTENT;
+
+ /**
+ * The Destination used for this consumer, if specified upon creation.
+ */
+ protected AMQDestination _destination;
+
+ /**
+ * Default encoding used for messages produced by this producer.
+ */
+ private String _encoding;
+
+ /**
+ * Default encoding used for message produced by this producer.
+ */
+ private String _mimeType;
+
+ private AMQProtocolHandler _protocolHandler;
+
+ /**
+ * True if this producer was created from a transacted session
+ */
+ private boolean _transacted;
+
+ private int _channelId;
+
+ /**
+ * This is an id generated by the session and is used to tie individual producers to the session. This means we
+ * can deregister a producer with the session when the producer is clsoed. We need to be able to tie producers
+ * to the session so that when an error is propagated to the session it can close the producer (meaning that
+ * a client that happens to hold onto a producer reference will get an error if he tries to use it subsequently).
+ */
+ private long _producerId;
+
+ /**
+ * The session used to create this producer
+ */
+ private AMQSession _session;
+
+ private final boolean _immediate;
+
+ private final boolean _mandatory;
+
+ private final boolean _waitUntilSent;
+
+ private boolean _disableMessageId;
+
+ private static final ContentBody[] NO_CONTENT_BODIES = new ContentBody[0];
+
+ protected BasicMessageProducer(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId,
+ AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory,
+ boolean waitUntilSent)
+ {
+ _connection = connection;
+ _destination = destination;
+ _transacted = transacted;
+ _protocolHandler = protocolHandler;
+ _channelId = channelId;
+ _session = session;
+ _producerId = producerId;
+ if (destination != null)
+ {
+ declareDestination(destination);
+ }
+
+ _immediate = immediate;
+ _mandatory = mandatory;
+ _waitUntilSent = waitUntilSent;
+ }
+
+ void resubscribe() throws AMQException
+ {
+ if (_destination != null)
+ {
+ declareDestination(_destination);
+ }
+ }
+
+ private void declareDestination(AMQDestination destination)
+ {
+ // Declare the exchange
+ // Note that the durable and internal arguments are ignored since passive is set to false
+ // TODO: Be aware of possible changes to parameter order as versions change.
+ AMQFrame declare =
+ ExchangeDeclareBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(),
+ _protocolHandler.getProtocolMinorVersion(), null, // arguments
+ false, // autoDelete
+ false, // durable
+ destination.getExchangeName(), // exchange
+ false, // internal
+ true, // nowait
+ false, // passive
+ _session.getTicket(), // ticket
+ destination.getExchangeClass()); // type
+ _protocolHandler.writeFrame(declare);
+ }
+
+ public void setDisableMessageID(boolean b) throws JMSException
+ {
+ checkPreConditions();
+ checkNotClosed();
+ _disableMessageId = b;
+ }
+
+ public boolean getDisableMessageID() throws JMSException
+ {
+ checkNotClosed();
+
+ return _disableMessageId;
+ }
+
+ public void setDisableMessageTimestamp(boolean b) throws JMSException
+ {
+ checkPreConditions();
+ _disableTimestamps = b;
+ }
+
+ public boolean getDisableMessageTimestamp() throws JMSException
+ {
+ checkNotClosed();
+
+ return _disableTimestamps;
+ }
+
+ public void setDeliveryMode(int i) throws JMSException
+ {
+ checkPreConditions();
+ if ((i != DeliveryMode.NON_PERSISTENT) && (i != DeliveryMode.PERSISTENT))
+ {
+ throw new JMSException("DeliveryMode must be either NON_PERSISTENT or PERSISTENT. Value of " + i
+ + " is illegal");
+ }
+
+ _deliveryMode = i;
+ }
+
+ public int getDeliveryMode() throws JMSException
+ {
+ checkNotClosed();
+
+ return _deliveryMode;
+ }
+
+ public void setPriority(int i) throws JMSException
+ {
+ checkPreConditions();
+ if ((i < 0) || (i > 9))
+ {
+ throw new IllegalArgumentException("Priority of " + i + " is illegal. Value must be in range 0 to 9");
+ }
+
+ _messagePriority = i;
+ }
+
+ public int getPriority() throws JMSException
+ {
+ checkNotClosed();
+
+ return _messagePriority;
+ }
+
+ public void setTimeToLive(long l) throws JMSException
+ {
+ checkPreConditions();
+ if (l < 0)
+ {
+ throw new IllegalArgumentException("Time to live must be non-negative - supplied value was " + l);
+ }
+
+ _timeToLive = l;
+ }
+
+ public long getTimeToLive() throws JMSException
+ {
+ checkNotClosed();
+
+ return _timeToLive;
+ }
+
+ public Destination getDestination() throws JMSException
+ {
+ checkNotClosed();
+
+ return _destination;
+ }
+
+ public void close() throws JMSException
+ {
+ _closed.set(true);
+ _session.deregisterProducer(_producerId);
+ }
+
+ public void send(Message message) throws JMSException
+ {
+ checkPreConditions();
+ checkInitialDestination();
+
+ synchronized (_connection.getFailoverMutex())
+ {
+ sendImpl(_destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate);
+ }
+ }
+
+ public void send(Message message, int deliveryMode) throws JMSException
+ {
+ checkPreConditions();
+ checkInitialDestination();
+
+ synchronized (_connection.getFailoverMutex())
+ {
+ sendImpl(_destination, message, deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate);
+ }
+ }
+
+ public void send(Message message, int deliveryMode, boolean immediate) throws JMSException
+ {
+ checkPreConditions();
+ checkInitialDestination();
+ synchronized (_connection.getFailoverMutex())
+ {
+ sendImpl(_destination, message, deliveryMode, _messagePriority, _timeToLive, _mandatory, immediate);
+ }
+ }
+
+ public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ checkPreConditions();
+ checkInitialDestination();
+ synchronized (_connection.getFailoverMutex())
+ {
+ sendImpl(_destination, message, deliveryMode, priority, timeToLive, _mandatory, _immediate);
+ }
+ }
+
+ public void send(Destination destination, Message message) throws JMSException
+ {
+ checkPreConditions();
+ checkDestination(destination);
+ synchronized (_connection.getFailoverMutex())
+ {
+ validateDestination(destination);
+ sendImpl((AMQDestination) destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory,
+ _immediate);
+ }
+ }
+
+ public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive)
+ throws JMSException
+ {
+ checkPreConditions();
+ checkDestination(destination);
+ synchronized (_connection.getFailoverMutex())
+ {
+ validateDestination(destination);
+ sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, _mandatory, _immediate);
+ }
+ }
+
+ public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
+ boolean mandatory) throws JMSException
+ {
+ checkPreConditions();
+ checkDestination(destination);
+ synchronized (_connection.getFailoverMutex())
+ {
+ validateDestination(destination);
+ sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, _immediate);
+ }
+ }
+
+ public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
+ boolean mandatory, boolean immediate) throws JMSException
+ {
+ checkPreConditions();
+ checkDestination(destination);
+ synchronized (_connection.getFailoverMutex())
+ {
+ validateDestination(destination);
+ sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate);
+ }
+ }
+
+ public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,
+ boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException
+ {
+ checkPreConditions();
+ checkDestination(destination);
+ synchronized (_connection.getFailoverMutex())
+ {
+ validateDestination(destination);
+ sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate,
+ waitUntilSent);
+ }
+ }
+
+ private AbstractJMSMessage convertToNativeMessage(Message message) throws JMSException
+ {
+ if (message instanceof AbstractJMSMessage)
+ {
+ return (AbstractJMSMessage) message;
+ }
+ else
+ {
+ AbstractJMSMessage newMessage;
+
+ if (message instanceof BytesMessage)
+ {
+ newMessage = new MessageConverter((BytesMessage) message).getConvertedMessage();
+ }
+ else if (message instanceof MapMessage)
+ {
+ newMessage = new MessageConverter((MapMessage) message).getConvertedMessage();
+ }
+ else if (message instanceof ObjectMessage)
+ {
+ newMessage = new MessageConverter((ObjectMessage) message).getConvertedMessage();
+ }
+ else if (message instanceof TextMessage)
+ {
+ newMessage = new MessageConverter((TextMessage) message).getConvertedMessage();
+ }
+ else if (message instanceof StreamMessage)
+ {
+ newMessage = new MessageConverter((StreamMessage) message).getConvertedMessage();
+ }
+ else
+ {
+ newMessage = new MessageConverter(message).getConvertedMessage();
+ }
+
+ if (newMessage != null)
+ {
+ return newMessage;
+ }
+ else
+ {
+ throw new JMSException("Unable to send message, due to class conversion error: "
+ + message.getClass().getName());
+ }
+ }
+ }
+
+ private void validateDestination(Destination destination) throws JMSException
+ {
+ if (!(destination instanceof AMQDestination))
+ {
+ throw new JMSException("Unsupported destination class: "
+ + ((destination != null) ? destination.getClass() : null));
+ }
+
+ declareDestination((AMQDestination) destination);
+ }
+
+ protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive,
+ boolean mandatory, boolean immediate) throws JMSException
+ {
+ sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent);
+ }
+
+ /**
+ * The caller of this method must hold the failover mutex.
+ *
+ * @param destination
+ * @param origMessage
+ * @param deliveryMode
+ * @param priority
+ * @param timeToLive
+ * @param mandatory
+ * @param immediate
+ * @throws JMSException
+ */
+ protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive,
+ boolean mandatory, boolean immediate, boolean wait) throws JMSException
+ {
+ checkTemporaryDestination(destination);
+ origMessage.setJMSDestination(destination);
+
+ AbstractJMSMessage message = convertToNativeMessage(origMessage);
+
+ if (_disableMessageId)
+ {
+ message.setJMSMessageID(null);
+ }
+ else
+ {
+ if (message.getJMSMessageID() == null)
+ {
+ message.setJMSMessageID(UUID.randomUUID().toString());
+ }
+ }
+
+ int type;
+ if (destination instanceof Topic)
+ {
+ type = AMQDestination.TOPIC_TYPE;
+ }
+ else if (destination instanceof Queue)
+ {
+ type = AMQDestination.QUEUE_TYPE;
+ }
+ else
+ {
+ type = AMQDestination.UNKNOWN_TYPE;
+ }
+
+ message.getJmsHeaders().setInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName(), type);
+
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame publishFrame =
+ BasicPublishBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(),
+ _protocolHandler.getProtocolMinorVersion(), destination.getExchangeName(), // exchange
+ immediate, // immediate
+ mandatory, // mandatory
+ destination.getRoutingKey(), // routingKey
+ _session.getTicket()); // ticket
+
+ message.prepareForSending();
+ ByteBuffer payload = message.getData();
+ BasicContentHeaderProperties contentHeaderProperties = message.getContentHeaderProperties();
+
+ if (!_disableTimestamps)
+ {
+ final long currentTime = System.currentTimeMillis();
+ contentHeaderProperties.setTimestamp(currentTime);
+
+ if (timeToLive > 0)
+ {
+ contentHeaderProperties.setExpiration(currentTime + timeToLive);
+ }
+ else
+ {
+ contentHeaderProperties.setExpiration(0);
+ }
+ }
+
+ contentHeaderProperties.setDeliveryMode((byte) deliveryMode);
+ contentHeaderProperties.setPriority((byte) priority);
+
+ final int size = (payload != null) ? payload.limit() : 0;
+ final int contentBodyFrameCount = calculateContentBodyFrameCount(payload);
+ final AMQFrame[] frames = new AMQFrame[2 + contentBodyFrameCount];
+
+ if (payload != null)
+ {
+ createContentBodies(payload, frames, 2, _channelId);
+ }
+
+ if ((contentBodyFrameCount != 0) && _logger.isDebugEnabled())
+ {
+ _logger.debug("Sending content body frames to " + destination);
+ }
+
+ // weight argument of zero indicates no child content headers, just bodies
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ AMQFrame contentHeaderFrame =
+ ContentHeaderBody.createAMQFrame(_channelId,
+ BasicConsumeBody.getClazz(_protocolHandler.getProtocolMajorVersion(),
+ _protocolHandler.getProtocolMinorVersion()), 0, contentHeaderProperties, size);
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Sending content header frame to " + destination);
+ }
+
+ frames[0] = publishFrame;
+ frames[1] = contentHeaderFrame;
+ CompositeAMQDataBlock compositeFrame = new CompositeAMQDataBlock(frames);
+ _protocolHandler.writeFrame(compositeFrame, wait);
+
+ if (message != origMessage)
+ {
+ _logger.debug("Updating original message");
+ origMessage.setJMSPriority(message.getJMSPriority());
+ origMessage.setJMSTimestamp(message.getJMSTimestamp());
+ _logger.debug("Setting JMSExpiration:" + message.getJMSExpiration());
+ origMessage.setJMSExpiration(message.getJMSExpiration());
+ origMessage.setJMSMessageID(message.getJMSMessageID());
+ }
+ }
+
+ private void checkTemporaryDestination(AMQDestination destination) throws JMSException
+ {
+ if (destination instanceof TemporaryDestination)
+ {
+ _logger.debug("destination is temporary destination");
+ TemporaryDestination tempDest = (TemporaryDestination) destination;
+ if (tempDest.getSession().isClosed())
+ {
+ _logger.debug("session is closed");
+ throw new JMSException("Session for temporary destination has been closed");
+ }
+
+ if (tempDest.isDeleted())
+ {
+ _logger.debug("destination is deleted");
+ throw new JMSException("Cannot send to a deleted temporary destination");
+ }
+ }
+ }
+
+ /**
+ * Create content bodies. This will split a large message into numerous bodies depending on the negotiated
+ * maximum frame size.
+ *
+ * @param payload
+ * @param frames
+ * @param offset
+ * @param channelId @return the array of content bodies
+ */
+ private void createContentBodies(ByteBuffer payload, AMQFrame[] frames, int offset, int channelId)
+ {
+
+ if (frames.length == (offset + 1))
+ {
+ frames[offset] = ContentBody.createAMQFrame(channelId, new ContentBody(payload));
+ }
+ else
+ {
+
+ final long framePayloadMax = _session.getAMQConnection().getMaximumFrameSize() - 1;
+ long remaining = payload.remaining();
+ for (int i = offset; i < frames.length; i++)
+ {
+ payload.position((int) framePayloadMax * (i - offset));
+ int length = (remaining >= framePayloadMax) ? (int) framePayloadMax : (int) remaining;
+ payload.limit(payload.position() + length);
+ frames[i] = ContentBody.createAMQFrame(channelId, new ContentBody(payload.slice()));
+
+ remaining -= length;
+ }
+ }
+
+ }
+
+ private int calculateContentBodyFrameCount(ByteBuffer payload)
+ {
+ // we substract one from the total frame maximum size to account for the end of frame marker in a body frame
+ // (0xCE byte).
+ int frameCount;
+ if ((payload == null) || (payload.remaining() == 0))
+ {
+ frameCount = 0;
+ }
+ else
+ {
+ int dataLength = payload.remaining();
+ final long framePayloadMax = _session.getAMQConnection().getMaximumFrameSize() - 1;
+ int lastFrame = ((dataLength % framePayloadMax) > 0) ? 1 : 0;
+ frameCount = (int) (dataLength / framePayloadMax) + lastFrame;
+ }
+
+ return frameCount;
+ }
+
+ public void setMimeType(String mimeType) throws JMSException
+ {
+ checkNotClosed();
+ _mimeType = mimeType;
+ }
+
+ public void setEncoding(String encoding) throws JMSException, UnsupportedEncodingException
+ {
+ checkNotClosed();
+ _encoding = encoding;
+ }
+
+ private void checkPreConditions() throws javax.jms.IllegalStateException, JMSException
+ {
+ checkNotClosed();
+
+ if ((_session == null) || _session.isClosed())
+ {
+ throw new javax.jms.IllegalStateException("Invalid Session");
+ }
+ }
+
+ private void checkInitialDestination()
+ {
+ if (_destination == null)
+ {
+ throw new UnsupportedOperationException("Destination is null");
+ }
+ }
+
+ private void checkDestination(Destination suppliedDestination) throws InvalidDestinationException
+ {
+ if ((_destination != null) && (suppliedDestination != null))
+ {
+ throw new UnsupportedOperationException(
+ "This message producer was created with a Destination, therefore you cannot use an unidentified Destination");
+ }
+
+ if (suppliedDestination == null)
+ {
+ throw new InvalidDestinationException("Supplied Destination was invalid");
+ }
+
+ }
+
+ public AMQSession getSession()
+ {
+ return _session;
+ }
+
+ public boolean isBound(AMQDestination destination) throws JMSException
+ {
+ return _session.isQueueBound(destination.getExchangeName(), null, destination.getRoutingKey());
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/Closeable.java b/Final/java/client/src/main/java/org/apache/qpid/client/Closeable.java
new file mode 100644
index 0000000000..7e119343a1
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/Closeable.java
@@ -0,0 +1,83 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.IllegalStateException;
+import javax.jms.JMSException;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Captures the 'closed' state of an object, that is initially open, can be tested to see if it is closed, and provides
+ * a 'close' method to close it.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Mark an object as closed.
+ * <tr><td> Check if an object is closed.
+ * <tr><td> Raise a JMS exception if an object is closed.
+ * </table>
+ *
+ * @todo Might be better to make this an interface. This whole class doesn't really encapsulate a terribly neat
+ * piece of re-usable functionality. A simple interface defining a close method would suffice.
+ *
+ * @todo The convenience method {@link #checkNotClosed} is not that helpfull, what if the caller wants to do something
+ * other than throw an exception? It doesn't really represent a very usefull re-usable piece of code. Consider
+ * inlining it and dropping the method.
+ */
+public abstract class Closeable
+{
+ /**
+ * We use an atomic boolean so that we do not have to synchronized access to this flag. Synchronizing access to this
+ * flag would mean have a synchronized block in every method.
+ */
+ protected final AtomicBoolean _closed = new AtomicBoolean(false);
+
+ /**
+ * Checks if this is closed, and raises a JMSException if it is.
+ *
+ * @throws JMSException If this is closed.
+ */
+ protected void checkNotClosed() throws JMSException
+ {
+ if (isClosed())
+ {
+ throw new IllegalStateException("Object " + toString() + " has been closed");
+ }
+ }
+
+ /**
+ * Checks if this is closed.
+ *
+ * @return <tt>true</tt> if this is closed, <tt>false</tt> otherwise.
+ */
+ public boolean isClosed()
+ {
+ return _closed.get();
+ }
+
+ /**
+ * Closes this object.
+ *
+ * @throws JMSException If this cannot be closed for any reason.
+ */
+ public abstract void close() throws JMSException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java b/Final/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java
new file mode 100644
index 0000000000..b1ec7216bc
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+public class ConnectionTuneParameters
+{
+ private long _frameMax;
+
+ private int _channelMax;
+
+ private int _heartbeat;
+
+ private long _txnLimit;
+
+ public long getFrameMax()
+ {
+ return _frameMax;
+ }
+
+ public void setFrameMax(long frameMax)
+ {
+ _frameMax = frameMax;
+ }
+
+ public int getChannelMax()
+ {
+ return _channelMax;
+ }
+
+ public void setChannelMax(int channelMax)
+ {
+ _channelMax = channelMax;
+ }
+
+ public int getHeartbeat()
+ {
+ return _heartbeat;
+ }
+
+ public void setHeartbeat(int hearbeat)
+ {
+ _heartbeat = hearbeat;
+ }
+
+ public long getTxnLimit()
+ {
+ return _txnLimit;
+ }
+
+ public void setTxnLimit(long txnLimit)
+ {
+ _txnLimit = txnLimit;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java b/Final/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java
new file mode 100644
index 0000000000..a5e89ef4fc
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.apache.qpid.framing.AMQShortString;
+
+public enum CustomJMSXProperty
+{
+ JMS_AMQP_NULL,
+ JMS_QPID_DESTTYPE,
+ JMSXGroupID,
+ JMSXGroupSeq;
+
+
+ private final AMQShortString _nameAsShortString;
+
+ CustomJMSXProperty()
+ {
+ _nameAsShortString = new AMQShortString(toString());
+ }
+
+ public AMQShortString getShortStringName()
+ {
+ return _nameAsShortString;
+ }
+
+ private static Enumeration _names;
+
+ public static synchronized Enumeration asEnumeration()
+ {
+ if(_names == null)
+ {
+ CustomJMSXProperty[] properties = values();
+ ArrayList<String> nameList = new ArrayList<String>(properties.length);
+ for(CustomJMSXProperty property : properties)
+ {
+ nameList.add(property.toString());
+ }
+ _names = Collections.enumeration(nameList);
+ }
+ return _names;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java b/Final/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java
new file mode 100644
index 0000000000..81a55006ed
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client;
+
+import java.util.Queue;
+
+public abstract class DispatcherCallback
+{
+ BasicMessageConsumer _consumer;
+
+ public DispatcherCallback(BasicMessageConsumer mc)
+ {
+ _consumer = mc;
+ }
+
+ abstract public void whilePaused(Queue<MessageConsumerPair> reprocessQueue);
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java b/Final/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java
new file mode 100644
index 0000000000..0927ca3625
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import org.apache.qpid.AMQException;
+
+import javax.jms.JMSException;
+
+/**
+ * JMSException does not accept wrapped exceptions in its constructor. Presumably this is because it is a relatively old
+ * Java exception class, before this was added as a default to Throwable. This exception class accepts wrapped exceptions
+ * as well as error messages, through its constructor, but is a JMSException.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept wrapped exceptions as a JMSException.
+ * </table>
+ */
+public class JMSAMQException extends JMSException
+{
+ /**
+ * Creates a JMSException, wrapping another exception class.
+ *
+ * @param message The error message.
+ * @param cause The underlying exception that caused this one. May be null if none is to be set.
+ */
+ public JMSAMQException(String message, Exception cause)
+ {
+ super(message);
+
+ if (cause != null)
+ {
+ setLinkedException(cause);
+ }
+ }
+
+ /**
+ * @param s The underlying exception.
+ *
+ * @deprecated Use the other constructor and write a helpfull message. This one will be deleted.
+ */
+ public JMSAMQException(AMQException s)
+ {
+ super(s.getMessage(), String.valueOf(s.getErrorCode()));
+ setLinkedException(s);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java b/Final/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java
new file mode 100644
index 0000000000..903514c35f
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.JMSException;
+
+public class JmsNotImplementedException extends JMSException
+{
+ public JmsNotImplementedException()
+ {
+ super("Not implemented");
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java b/Final/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java
new file mode 100644
index 0000000000..585d6db3fd
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.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.client;
+
+public class MessageConsumerPair
+{
+ BasicMessageConsumer _consumer;
+ Object _item;
+
+ public MessageConsumerPair(BasicMessageConsumer consumer, Object item)
+ {
+ _consumer = consumer;
+ _item = item;
+ }
+
+ public BasicMessageConsumer getConsumer()
+ {
+ return _consumer;
+ }
+
+ public Object getItem()
+ {
+ return _item;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java b/Final/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java
new file mode 100644
index 0000000000..3bb5707417
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import java.util.Enumeration;
+
+import javax.jms.ConnectionMetaData;
+import javax.jms.JMSException;
+
+import org.apache.qpid.common.QpidProperties;
+
+public class QpidConnectionMetaData implements ConnectionMetaData
+{
+
+
+ QpidConnectionMetaData(AMQConnection conn)
+ {
+ }
+
+ public int getJMSMajorVersion() throws JMSException
+ {
+ return 1;
+ }
+
+ public int getJMSMinorVersion() throws JMSException
+ {
+ return 1;
+ }
+
+ public String getJMSProviderName() throws JMSException
+ {
+ return "Apache " + QpidProperties.getProductName();
+ }
+
+ public String getJMSVersion() throws JMSException
+ {
+ return "1.1";
+ }
+
+ public Enumeration getJMSXPropertyNames() throws JMSException
+ {
+ return CustomJMSXProperty.asEnumeration();
+ }
+
+ public int getProviderMajorVersion() throws JMSException
+ {
+ return 0;
+ }
+
+ public int getProviderMinorVersion() throws JMSException
+ {
+ return 8;
+ }
+
+ public String getProviderVersion() throws JMSException
+ {
+ return QpidProperties.getProductName() + " (Client: [" + getClientVersion() + "] ; Broker [" + getBrokerVersion() + "] ; Protocol: [ "
+ + getProtocolVersion() + "] )";
+ }
+
+ private String getProtocolVersion()
+ {
+ // TODO - Implement based on connection negotiated protocol
+ return "0.8";
+ }
+
+ public String getBrokerVersion()
+ {
+ // TODO - get broker version
+ return "<unkown>";
+ }
+
+ public String getClientVersion()
+ {
+ return QpidProperties.getBuildVersion();
+ }
+
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java b/Final/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java
new file mode 100644
index 0000000000..7059588367
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Queue;
+import javax.jms.QueueReceiver;
+
+/**
+ * Class that wraps a MessageConsumer for backwards JMS compatibility
+ * Returned by methods in AMQSession etc
+ */
+public class QueueReceiverAdaptor implements QueueReceiver {
+
+ protected MessageConsumer _consumer;
+ protected Queue _queue;
+
+ protected QueueReceiverAdaptor(Queue queue, MessageConsumer consumer)
+ {
+ _consumer = consumer;
+ _queue = queue;
+ }
+
+ public String getMessageSelector() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.getMessageSelector();
+ }
+
+ public MessageListener getMessageListener() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.getMessageListener();
+ }
+
+ public void setMessageListener(MessageListener messageListener) throws JMSException
+ {
+ checkPreConditions();
+ _consumer.setMessageListener(messageListener);
+ }
+
+ public Message receive() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.receive();
+ }
+
+ public Message receive(long l) throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.receive(l);
+ }
+
+ public Message receiveNoWait() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.receiveNoWait();
+ }
+
+ public void close() throws JMSException
+ {
+ _consumer.close();
+ }
+
+ /**
+ * Return the queue associated with this receiver
+ * @return
+ * @throws JMSException
+ */
+ public Queue getQueue() throws JMSException
+ {
+ checkPreConditions();
+ return _queue;
+ }
+
+ private void checkPreConditions() throws javax.jms.IllegalStateException {
+ BasicMessageConsumer msgConsumer = (BasicMessageConsumer)_consumer;
+
+ if (msgConsumer.isClosed() ){
+ throw new javax.jms.IllegalStateException("Consumer is closed");
+ }
+
+ if(_queue == null){
+ throw new UnsupportedOperationException("Queue is null");
+ }
+
+ AMQSession session = msgConsumer.getSession();
+
+ if(session == null || session.isClosed()){
+ throw new javax.jms.IllegalStateException("Invalid Session");
+ }
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/Final/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java
new file mode 100644
index 0000000000..493e2b5ec0
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.client;
+
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.InvalidDestinationException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.QueueSender;
+
+public class QueueSenderAdapter implements QueueSender
+{
+
+ private BasicMessageProducer _delegate;
+ private Queue _queue;
+ private boolean closed = false;
+
+ public QueueSenderAdapter(BasicMessageProducer msgProducer, Queue queue)
+ {
+ _delegate = msgProducer;
+ _queue = queue;
+ }
+
+ public Queue getQueue() throws JMSException
+ {
+ checkPreConditions();
+
+ return _queue;
+ }
+
+ public void send(Message msg) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.send(msg);
+ }
+
+ public void send(Queue queue, Message msg) throws JMSException
+ {
+ checkPreConditions(queue);
+ _delegate.send(queue, msg);
+ }
+
+ public void publish(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.send(msg, deliveryMode, priority, timeToLive);
+ }
+
+ public void send(Queue queue, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ checkPreConditions(queue);
+ _delegate.send(queue, msg, deliveryMode, priority, timeToLive);
+ }
+
+ public void close() throws JMSException
+ {
+ _delegate.close();
+ closed = true;
+ }
+
+ public int getDeliveryMode() throws JMSException
+ {
+ checkPreConditions();
+
+ return _delegate.getDeliveryMode();
+ }
+
+ public Destination getDestination() throws JMSException
+ {
+ checkPreConditions();
+
+ return _delegate.getDestination();
+ }
+
+ public boolean getDisableMessageID() throws JMSException
+ {
+ checkPreConditions();
+
+ return _delegate.getDisableMessageID();
+ }
+
+ public boolean getDisableMessageTimestamp() throws JMSException
+ {
+ checkPreConditions();
+
+ return _delegate.getDisableMessageTimestamp();
+ }
+
+ public int getPriority() throws JMSException
+ {
+ checkPreConditions();
+
+ return _delegate.getPriority();
+ }
+
+ public long getTimeToLive() throws JMSException
+ {
+ checkPreConditions();
+
+ return _delegate.getTimeToLive();
+ }
+
+ public void send(Destination dest, Message msg) throws JMSException
+ {
+ checkPreConditions((Queue) dest);
+ _delegate.send(dest, msg);
+ }
+
+ public void send(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.send(msg, deliveryMode, priority, timeToLive);
+ }
+
+ public void send(Destination dest, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ checkPreConditions((Queue) dest);
+ _delegate.send(dest, msg, deliveryMode, priority, timeToLive);
+ }
+
+ public void setDeliveryMode(int deliveryMode) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setDeliveryMode(deliveryMode);
+ }
+
+ public void setDisableMessageID(boolean disableMessageID) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setDisableMessageID(disableMessageID);
+ }
+
+ public void setDisableMessageTimestamp(boolean disableMessageTimestamp) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setDisableMessageTimestamp(disableMessageTimestamp);
+ }
+
+ public void setPriority(int priority) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setPriority(priority);
+ }
+
+ public void setTimeToLive(long timeToLive) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setTimeToLive(timeToLive);
+ }
+
+ private void checkPreConditions() throws JMSException
+ {
+ checkPreConditions(_queue);
+ }
+
+ private void checkPreConditions(Queue queue) throws JMSException
+ {
+ if (closed)
+ {
+ throw new javax.jms.IllegalStateException("Publisher is closed");
+ }
+
+ AMQSession session = ((BasicMessageProducer) _delegate).getSession();
+
+ if ((session == null) || session.isClosed())
+ {
+ throw new javax.jms.IllegalStateException("Invalid Session");
+ }
+
+ if (queue == null)
+ {
+ throw new UnsupportedOperationException("Queue is null.");
+ }
+
+ if (!(queue instanceof AMQDestination))
+ {
+ throw new InvalidDestinationException("Queue: " + queue + " is not a valid Qpid queue");
+ }
+
+ AMQDestination destination = (AMQDestination) queue;
+ if (!destination.isValidated() && checkQueueBeforePublish())
+ {
+
+ if (_delegate.getSession().isStrictAMQP())
+ {
+ _delegate._logger.warn("AMQP does not support destination validation before publish, ");
+ destination.setValidated(true);
+ }
+ else
+ {
+ if (_delegate.isBound(destination))
+ {
+ destination.setValidated(true);
+ }
+ else
+ {
+ throw new InvalidDestinationException("Queue: " + queue
+ + " is not a valid destination (no bindings on server");
+ }
+ }
+ }
+ }
+
+ private boolean checkQueueBeforePublish()
+ {
+ return "true".equalsIgnoreCase(System.getProperty("org.apache.qpid.client.verifyQueueBindingBeforePublish", "true"));
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java b/Final/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java
new file mode 100644
index 0000000000..2280cc9870
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.client;
+
+public class SSLConfiguration {
+
+ private String _keystorePath;
+
+ private String _keystorePassword;
+
+ private String _certType = "SunX509";
+
+ public void setKeystorePath(String path)
+ {
+ _keystorePath = path;
+ }
+
+ public String getKeystorePath()
+ {
+ return _keystorePath;
+ }
+
+ public void setKeystorePassword(String password)
+ {
+ _keystorePassword = password;
+ }
+
+ public String getKeystorePassword()
+ {
+ return _keystorePassword;
+ }
+
+ public void setCertType(String type)
+ {
+ _certType = type;
+ }
+
+ public String getCertType()
+ {
+ return _certType;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java b/Final/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java
new file mode 100644
index 0000000000..03d25aa243
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.client;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+
+/**
+ * Provides support for covenience interface implemented by both AMQTemporaryTopic and AMQTemporaryQueue
+ * so that operations related to their "temporary-ness" can be abstracted out.
+ */
+interface TemporaryDestination extends Destination
+{
+
+ public void delete() throws JMSException;
+ public AMQSession getSession();
+ public boolean isDeleted();
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java b/Final/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java
new file mode 100644
index 0000000000..81b9940ed5
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.client;
+
+import javax.jms.Destination;
+import javax.jms.IllegalStateException;
+import javax.jms.InvalidDestinationException;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Topic;
+import javax.jms.TopicPublisher;
+
+public class TopicPublisherAdapter implements TopicPublisher
+{
+
+ private BasicMessageProducer _delegate;
+ private Topic _topic;
+
+ public TopicPublisherAdapter(BasicMessageProducer msgProducer, Topic topic)
+ {
+ _delegate = msgProducer;
+ _topic = topic;
+ }
+
+ public Topic getTopic() throws JMSException
+ {
+ checkPreConditions();
+ return _topic;
+ }
+
+ public void publish(Message msg) throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(_topic);
+ _delegate.send(msg);
+ }
+
+ public void publish(Topic topic, Message msg) throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(topic);
+ _delegate.send(topic, msg);
+ }
+
+ public void publish(Message msg, int deliveryMode, int priority, long timeToLive)
+ throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(_topic);
+ _delegate.send(msg, deliveryMode, priority, timeToLive);
+ }
+
+ public int getDeliveryMode() throws JMSException {
+ checkPreConditions();
+ return _delegate.getDeliveryMode();
+ }
+
+ public void publish(Topic topic, Message msg, int deliveryMode, int priority, long timeToLive)
+ throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(topic);
+ _delegate.send(topic, msg, deliveryMode, priority, timeToLive);
+ }
+
+ public void close() throws JMSException
+ {
+ _delegate.close();
+ }
+
+ public boolean getDisableMessageID() throws JMSException {
+ checkPreConditions();
+ return _delegate.getDisableMessageID();
+ }
+
+ public boolean getDisableMessageTimestamp() throws JMSException {
+ checkPreConditions();
+ return _delegate.getDisableMessageTimestamp();
+ }
+
+ public Destination getDestination() throws JMSException
+ {
+ checkPreConditions();
+ return _delegate.getDestination();
+ }
+
+ public int getPriority() throws JMSException {
+ checkPreConditions();
+ return _delegate.getPriority();
+ }
+
+ public long getTimeToLive() throws JMSException {
+ checkPreConditions();
+ return _delegate.getTimeToLive();
+ }
+
+ public void send(Message msg) throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(_topic);
+ _delegate.send(msg);
+ }
+
+ public void send(Destination dest, Message msg) throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(_topic);
+ _delegate.send(dest, msg);
+ }
+
+ public void send(Message msg, int deliveryMode, int priority, long timeToLive)
+ throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(_topic);
+ _delegate.send(msg, deliveryMode, priority, timeToLive);
+ }
+
+ public void send(Destination dest, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException
+ {
+ checkPreConditions();
+ checkTopic(dest);
+ _delegate.send(dest, msg, deliveryMode, priority, timeToLive);
+ }
+
+ public void setDeliveryMode(int deliveryMode) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setDeliveryMode(deliveryMode);
+ }
+
+ public void setDisableMessageID(boolean disableMessageID) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setDisableMessageID(disableMessageID);
+ }
+
+ public void setDisableMessageTimestamp(boolean disableMessageTimestamp) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setDisableMessageTimestamp(disableMessageTimestamp);
+ }
+
+ public void setPriority(int priority) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setPriority(priority);
+ }
+
+ public void setTimeToLive(long timeToLive) throws JMSException
+ {
+ checkPreConditions();
+ _delegate.setTimeToLive(timeToLive);
+ }
+
+ private void checkPreConditions() throws IllegalStateException
+ {
+ if (_delegate.isClosed())
+ {
+ throw new javax.jms.IllegalStateException("Publisher is _closed");
+ }
+
+ AMQSession session = _delegate.getSession();
+ if (session == null || session.isClosed())
+ {
+ throw new javax.jms.IllegalStateException("Invalid Session");
+ }
+ }
+
+ private void checkTopic(Destination topic) throws InvalidDestinationException
+ {
+ if (topic == null)
+ {
+ throw new UnsupportedOperationException("Topic is null");
+ }
+ if (!(topic instanceof Topic))
+ {
+ throw new InvalidDestinationException("Destination " + topic + " is not a topic");
+ }
+ if(!(topic instanceof AMQDestination))
+ {
+ throw new InvalidDestinationException("Destination " + topic + " is not a Qpid topic");
+ }
+
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java b/Final/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java
new file mode 100644
index 0000000000..d4bec5d906
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java
@@ -0,0 +1,126 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.Topic;
+import javax.jms.TopicSubscriber;
+
+/**
+ * Wraps a MessageConsumer to fulfill the extended TopicSubscriber contract
+ *
+ */
+class TopicSubscriberAdaptor implements TopicSubscriber
+{
+ private final Topic _topic;
+ private final BasicMessageConsumer _consumer;
+ private final boolean _noLocal;
+
+ TopicSubscriberAdaptor(Topic topic, BasicMessageConsumer consumer, boolean noLocal)
+ {
+ _topic = topic;
+ _consumer = consumer;
+ _noLocal = noLocal;
+ }
+
+ TopicSubscriberAdaptor(Topic topic, BasicMessageConsumer consumer)
+ {
+ this(topic, consumer, consumer.isNoLocal());
+ }
+
+ public Topic getTopic() throws JMSException
+ {
+ checkPreConditions();
+ return _topic;
+ }
+
+ public boolean getNoLocal() throws JMSException
+ {
+ checkPreConditions();
+ return _noLocal;
+ }
+
+ public String getMessageSelector() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.getMessageSelector();
+ }
+
+ public MessageListener getMessageListener() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.getMessageListener();
+ }
+
+ public void setMessageListener(MessageListener messageListener) throws JMSException
+ {
+ checkPreConditions();
+ _consumer.setMessageListener(messageListener);
+ }
+
+ public Message receive() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.receive();
+ }
+
+ public Message receive(long l) throws JMSException
+ {
+ return _consumer.receive(l);
+ }
+
+ public Message receiveNoWait() throws JMSException
+ {
+ checkPreConditions();
+ return _consumer.receiveNoWait();
+ }
+
+ public void close() throws JMSException
+ {
+ _consumer.close();
+ }
+
+ private void checkPreConditions() throws javax.jms.IllegalStateException{
+ BasicMessageConsumer msgConsumer = (BasicMessageConsumer)_consumer;
+
+ if (msgConsumer.isClosed() ){
+ throw new javax.jms.IllegalStateException("Consumer is closed");
+ }
+
+ if(_topic == null){
+ throw new UnsupportedOperationException("Topic is null");
+ }
+
+ AMQSession session = msgConsumer.getSession();
+
+ if(session == null || session.isClosed()){
+ throw new javax.jms.IllegalStateException("Invalid Session");
+ }
+ }
+
+ BasicMessageConsumer getMessageConsumer()
+ {
+ return _consumer;
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java
new file mode 100644
index 0000000000..037b0dc2d1
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.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.client.failover;
+
+/**
+ * FailoverException is used to indicate that a synchronous request has failed to receive the reply that it is waiting
+ * for because the fail-over process has been started whilst it was waiting for its reply. Synchronous methods generally
+ * raise this exception to indicate that they must be re-tried once the fail-over process has completed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Used to indicate failure of a synchronous request due to fail-over.
+ * </table>
+ *
+ * @todo This exception is created and passed as an argument to a method, rather than thrown. The exception is being
+ * used to represent an event, passed out to other threads. Use of exceptions as arguments rather than as
+ * exceptions is extremly confusing. Ideally use a condition or set a flag and check it instead.
+ * This exceptions-as-events pattern seems to be in a similar style to Mina code, which is not pretty, but
+ * potentially acceptable for that reason. We have the option of extending the mina model to add more events
+ * to it, that is, anything that is interested in handling failover as an event occurs below the main
+ * amq event handler, which knows the specific interface of the qpid handlers, which can pass this down as
+ * an explicit event, without it being an exception. Add failover method to BlockingMethodFrameListener,
+ * have it set a flag or interrupt the waiting thread, which then creates and raises this exception.
+ */
+public class FailoverException extends Exception
+{
+ public FailoverException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java
new file mode 100644
index 0000000000..5303993331
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java
@@ -0,0 +1,240 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.failover;
+
+import org.apache.mina.common.IoSession;
+
+import org.apache.qpid.AMQDisconnectedException;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.client.state.AMQStateManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * FailoverHandler is a continuation that performs the failover procedure on a protocol session. As described in the
+ * class level comment for {@link AMQProtocolHandler}, a protocol connection can span many physical transport
+ * connections, failing over to a new connection if the transport connection fails. The procedure to establish a new
+ * connection is expressed as a continuation, in order that it may be run in a seperate thread to the i/o thread that
+ * detected the failure and is used to handle the communication to establish a new connection.
+ *
+ * </p>The reason this needs to be a separate thread is because this work cannot be done inside the i/o processor
+ * thread. The significant task is the connection setup which involves a protocol exchange until a particular state
+ * is achieved. This procedure waits until the state is achieved which would prevent the i/o thread doing the work
+ * it needs to do to achieve the new state.
+ *
+ * <p/>The failover procedure does the following:
+ *
+ * <ol>
+ * <li>Sets the failing over condition to true.</li>
+ * <li>Creates a {@link FailoverException} and gets the protocol connection handler to propagate this event to all
+ * interested parties.</li>
+ * <li>Takes the failover mutex on the protocol connection handler.</li>
+ * <li>Abandons the fail over if any of the interested parties vetoes it. The mutex is released and the condition
+ * reset.</li>
+ * <li>Creates a new {@link AMQStateManager} and re-established the connection through it.</li>
+ * <li>Informs the AMQConnection if the connection cannot be re-established.</li>
+ * <li>Recreates all sessions from the old connection to the new.</li>
+ * <li>Resets the failing over condition and releases the mutex.</li>
+ * </ol>
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Update fail-over state <td> {@link AMQProtocolHandler}
+ * </table>
+ *
+ * @todo The failover latch and mutex are used like a lock and condition. If the retrotranlator supports lock/condition
+ * then could change over to using them. 1.4 support still needed.
+ *
+ * @todo If the condition is set to null on a vetoes fail-over and there are already other threads waiting on the
+ * condition, they will never be released. It might be an idea to reset the condition in a finally block.
+ *
+ * @todo Creates a {@link AMQDisconnectedException} and passes it to the AMQConnection. No need to use an
+ * exception-as-argument here, could just as easily call a specific method for this purpose on AMQConnection.
+ *
+ * @todo Creates a {@link FailoverException} and propagates it to the MethodHandlers. No need to use an
+ * exception-as-argument here, could just as easily call a specific method for this purpose on
+ * {@link org.apache.qpid.protocol.AMQMethodListener}.
+ */
+public class FailoverHandler implements Runnable
+{
+ /** Used for debugging. */
+ private static final Logger _logger = LoggerFactory.getLogger(FailoverHandler.class);
+
+ /** Holds the MINA session for the connection that has failed, not the connection that is being failed onto. */
+ private final IoSession _session;
+
+ /** Holds the protocol handler for the failed connection, upon which the new connection is to be set up. */
+ private AMQProtocolHandler _amqProtocolHandler;
+
+ /** Used to hold the host to fail over to. This is optional and if not set a reconnect to the previous host is tried. */
+ private String _host;
+
+ /** Used to hold the port to fail over to. */
+ private int _port;
+
+ /**
+ * Creates a failover handler on a protocol session, for a particular MINA session (network connection).
+ *
+ * @param amqProtocolHandler The protocol handler that spans the failover.
+ * @param session The MINA session, for the failing connection.
+ */
+ public FailoverHandler(AMQProtocolHandler amqProtocolHandler, IoSession session)
+ {
+ _amqProtocolHandler = amqProtocolHandler;
+ _session = session;
+ }
+
+ /**
+ * Performs the failover procedure. See the class level comment, {@link FailoverHandler}, for a description of the
+ * failover procedure.
+ */
+ public void run()
+ {
+ if (Thread.currentThread().isDaemon())
+ {
+ throw new IllegalStateException("FailoverHandler must run on a non-daemon thread.");
+ }
+
+ // Create a latch, upon which tasks that must not run in parallel with a failover can wait for completion of
+ // the fail over.
+ _amqProtocolHandler.setFailoverLatch(new CountDownLatch(1));
+
+ // 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"));
+
+ // 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())
+ {
+ // 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
+ // a slightly more complex state model therefore I felt it was worthwhile doing this.
+ AMQStateManager existingStateManager = _amqProtocolHandler.getStateManager();
+ _amqProtocolHandler.setStateManager(new AMQStateManager(_amqProtocolHandler.getProtocolSession()));
+ if (!_amqProtocolHandler.getConnection().firePreFailover(_host != null))
+ {
+ _logger.info("Failover process veto-ed by client");
+
+ _amqProtocolHandler.setStateManager(existingStateManager);
+ if (_host != null)
+ {
+ _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException(
+ "Redirect was vetoed by client"));
+ }
+ else
+ {
+ _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException(
+ "Failover was vetoed by client"));
+ }
+
+ _amqProtocolHandler.getFailoverLatch().countDown();
+ _amqProtocolHandler.setFailoverLatch(null);
+
+ return;
+ }
+
+ _logger.info("Starting failover process");
+
+ boolean failoverSucceeded;
+ // when host is non null we have a specified failover host otherwise we all the client to cycle through
+ // all specified hosts
+
+ // if _host has value then we are performing a redirect.
+ if (_host != null)
+ {
+ failoverSucceeded = _amqProtocolHandler.getConnection().attemptReconnection(_host, _port);
+ }
+ else
+ {
+ failoverSucceeded = _amqProtocolHandler.getConnection().attemptReconnection();
+ }
+
+ if (!failoverSucceeded)
+ {
+ _amqProtocolHandler.setStateManager(existingStateManager);
+ _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException(
+ "Server closed connection and no failover " + "was successful"));
+ }
+ else
+ {
+ _amqProtocolHandler.setStateManager(existingStateManager);
+ try
+ {
+ if (_amqProtocolHandler.getConnection().firePreResubscribe())
+ {
+ _logger.info("Resubscribing on new connection");
+ _amqProtocolHandler.getConnection().resubscribeSessions();
+ }
+ else
+ {
+ _logger.info("Client vetoed automatic resubscription");
+ }
+
+ _amqProtocolHandler.getConnection().fireFailoverComplete();
+ _amqProtocolHandler.setFailoverState(FailoverState.NOT_STARTED);
+ _logger.info("Connection failover completed successfully");
+ }
+ catch (Exception e)
+ {
+ _logger.info("Failover process failed - exception being propagated by protocol handler");
+ _amqProtocolHandler.setFailoverState(FailoverState.FAILED);
+ /*try
+ {*/
+ _amqProtocolHandler.exceptionCaught(_session, e);
+ /*}
+ catch (Exception ex)
+ {
+ _logger.error("Error notifying protocol session of error: " + ex, ex);
+ }*/
+ }
+ }
+ }
+
+ _amqProtocolHandler.getFailoverLatch().countDown();
+ }
+
+ /**
+ * Sets the host name to fail over to. This is optional and if not set a reconnect to the previous host is tried.
+ *
+ * @param host The host name to fail over to.
+ */
+ public void setHost(String host)
+ {
+ _host = host;
+ }
+
+ /**
+ * Sets the port to fail over to.
+ *
+ * @param port The port to fail over to.
+ */
+ public void setPort(int port)
+ {
+ _port = port;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java
new file mode 100644
index 0000000000..1ec98efe0e
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.client.failover;
+
+import org.apache.qpid.client.AMQConnection;
+
+/**
+ * FailoverNoopSupport is a {@link FailoverSupport} implementation that does not really provide any failover support
+ * at all. It wraps a {@link FailoverProtectedOperation} but should that operation throw {@link FailoverException} this
+ * support class simply re-raises that exception as an IllegalStateException. This support wrapper should only be
+ * used where the caller can be certain that the failover protected operation cannot acutally throw a failover exception,
+ * for example, because the caller already holds a lock preventing that condition from arising.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Perform a fail-over protected operation raising providing no handling of fail-over conditions.
+ * </table>
+ */
+public class FailoverNoopSupport<T, E extends Exception> implements FailoverSupport<T, E>
+{
+ /** The protected operation that is to be retried in the event of fail-over. */
+ FailoverProtectedOperation<T, E> operation;
+
+ /** The connection on which the fail-over protected operation is to be performed. */
+ AMQConnection connection;
+
+ /**
+ * Creates an automatic retrying fail-over handler for the specified operation.
+ *
+ * @param operation The fail-over protected operation to wrap in this handler.
+ */
+ public FailoverNoopSupport(FailoverProtectedOperation<T, E> operation, AMQConnection con)
+ {
+ this.operation = operation;
+ this.connection = con;
+ }
+
+ /**
+ * Delegates to another continuation which is to be provided with fail-over handling.
+ *
+ * @return The return value from the delegated to continuation.
+ * @throws E Any exception that the delegated to continuation may raise.
+ */
+ public T execute() throws E
+ {
+ try
+ {
+ return operation.execute();
+ }
+ catch (FailoverException e)
+ {
+ throw new IllegalStateException("Fail-over interupted no-op failover support. "
+ + "No-op support should only be used where the caller is certain fail-over cannot occur.", e);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java
new file mode 100644
index 0000000000..9a7f43926e
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.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.client.failover;
+
+/**
+ * FailoverProtectedOperation is a continuation for an operation that may throw a {@link FailoverException} because
+ * it has been interrupted by the fail-over process. The {@link FailoverRetrySupport} class defines support wrappers
+ * for failover protected operations, in order to provide different handling schemes when failovers occurr.
+ *
+ * <p/>The type of checked exception that the operation may perform has been generified, in order that fail over
+ * protected operations can be defined that raise arbitrary exceptions. The actuall exception types used should not
+ * be sub-classes of FailoverException, or else catching FailoverException in the {@link FailoverRetrySupport} classes
+ * will mask the exception.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Perform an operation that may be interrupted by fail-over.
+ * </table>
+ */
+public interface FailoverProtectedOperation<T, E extends Exception>
+{
+ /**
+ * Performs the continuations work.
+ *
+ * @return Provdes scope for the continuation to return an arbitrary value.
+ *
+ * @throws FailoverException If the operation is interrupted by a fail-over notification.
+ */
+ public abstract T execute() throws E, FailoverException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java
new file mode 100644
index 0000000000..120a07f0fc
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java
@@ -0,0 +1,128 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.failover;
+
+import org.apache.qpid.client.AMQConnection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified
+ * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due
+ * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request,
+ * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This
+ * automatic retrying is continued until the continuation succeeds, or throws an exception (different to
+ * FailoverException, which is used to signal the failure of the original condition).
+ *
+ * <p/>The blocking condition used is that the connection is not currently failing over, and the mutex used is the
+ * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods.
+ * These are used like a lock and condition variable.
+ *
+ * <p/>The wrapped operation may throw a FailoverException, this is an exception that can be raised by a
+ * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a
+ * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are
+ * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous
+ * methods that should not be attempted when a fail-over is in progress.
+ *
+ * <p/>Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be
+ * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want
+ * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation
+ * until it succeeds. Synchronous methods are usually coordinated with a
+ * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants
+ * to start and throws a FailoverException in response to this.
+ *
+ * <p/>Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be
+ * started during fail-over, but be delayed until any current fail-over has completed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a continuation synchronized on a fail-over lock and condition.
+ * <tr><td> Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception.
+ * </table>
+ *
+ * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see
+ * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary
+ * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation.
+ * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type
+ * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that.
+ *
+ * @todo InterruptedException not handled well.
+ */
+public class FailoverRetrySupport<T, E extends Exception> implements FailoverSupport<T, E>
+{
+ /** Used for debugging. */
+ private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class);
+
+ /** The protected operation that is to be retried in the event of fail-over. */
+ FailoverProtectedOperation<T, E> operation;
+
+ /** The connection on which the fail-over protected operation is to be performed. */
+ AMQConnection connection;
+
+ /**
+ * Creates an automatic retrying fail-over handler for the specified operation.
+ *
+ * @param operation The fail-over protected operation to wrap in this handler.
+ */
+ public FailoverRetrySupport(FailoverProtectedOperation<T, E> operation, AMQConnection con)
+ {
+ this.operation = operation;
+ this.connection = con;
+ }
+
+ /**
+ * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats
+ * until the operation throws AMQException or succeeds without being interrupted by fail-over.
+ *
+ * @return The result of executing the continuation.
+ *
+ * @throws E Any underlying exception is allowed to fall through.
+ */
+ public T execute() throws E
+ {
+ while (true)
+ {
+ try
+ {
+ connection.blockUntilNotFailingOver();
+ }
+ catch (InterruptedException e)
+ {
+ _log.debug("Interrupted: " + e, e);
+
+ return null;
+ }
+
+ synchronized (connection.getFailoverMutex())
+ {
+ try
+ {
+ return operation.execute();
+ }
+ catch (FailoverException e)
+ {
+ _log.debug("Failover exception caught during operation: " + e, e);
+ }
+ }
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java
new file mode 100644
index 0000000000..807a5f7d13
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.failover;
+
+/**
+ * Defines the possible states of the failover process; not started, in progress, failed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent a one of the states of the fail-over process.
+ * </table>
+ */
+public final class FailoverState
+{
+ /** The string description on this state. */
+ private final String _state;
+
+ /** Failover has not yet been attempted on this connection. */
+ public static final FailoverState NOT_STARTED = new FailoverState("NOT STARTED");
+
+ /** Failover has been requested on this connection but has not completed. */
+ public static final FailoverState IN_PROGRESS = new FailoverState("IN PROGRESS");
+
+ /** Failover has been attempted and failed. */
+ public static final FailoverState FAILED = new FailoverState("FAILED");
+
+ /**
+ * Creates a type safe enumeration of a fail-over state.
+ *
+ * @param state The fail-over state description string.
+ */
+ private FailoverState(String state)
+ {
+ _state = state;
+ }
+
+ /**
+ * Prints this state, mainly for debugging purposes.
+ *
+ * @return The string description of this state.
+ */
+ public String toString()
+ {
+ return "FailoverState: " + _state;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java
new file mode 100644
index 0000000000..ef2e7e1d65
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java
@@ -0,0 +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.client.failover;
+
+/**
+ * FailoverSupport defines an interface for different types of fail-over handlers, that provide different types of
+ * behaviour for handling fail-overs during operations that can be interrupted by the fail-over process. For example,
+ * the support could automatically retry once the fail-over process completes, could prevent an operation from being
+ * started whilst fail-over is running, or could quietly abandon the operation or raise an exception, and so on.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Perform a fail-over protected operation with handling for fail-over conditions.
+ * </table>
+ *
+ * @todo Continuation, extend some sort of re-usable Continuation interface, which might look very like this one.
+ */
+public interface FailoverSupport<T, E extends Exception>
+{
+ /**
+ * Delegates to another continuation which is to be provided with fail-over handling.
+ *
+ * @return The return value from the delegated to continuation.
+ *
+ * @throws E Any exception that the delegated to continuation may raise.
+ */
+ public T execute() throws E;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java
new file mode 100644
index 0000000000..8f0ee05b3e
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.framing.BasicCancelOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BasicCancelOkMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(BasicCancelOkMethodHandler.class);
+
+ private static final BasicCancelOkMethodHandler _instance = new BasicCancelOkMethodHandler();
+
+ public static BasicCancelOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicCancelOkMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ BasicCancelOkBody body = (BasicCancelOkBody) evt.getMethod();
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("New BasicCancelOk method received for consumer:" + body.consumerTag);
+ }
+
+ protocolSession.confirmConsumerCancelled(evt.getChannelId(), body.consumerTag);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
new file mode 100644
index 0000000000..51120da55c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.message.UnprocessedMessage;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BasicDeliverMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(BasicDeliverMethodHandler.class);
+
+ private static final BasicDeliverMethodHandler _instance = new BasicDeliverMethodHandler();
+
+ public static BasicDeliverMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ final UnprocessedMessage msg = new UnprocessedMessage(evt.getChannelId(), (BasicDeliverBody) evt.getMethod());
+ _logger.debug("New JmsDeliver method received");
+ protocolSession.unprocessedMessageReceived(msg);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java
new file mode 100644
index 0000000000..0f00c6a26e
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.message.UnprocessedMessage;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.framing.BasicReturnBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BasicReturnMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(BasicReturnMethodHandler.class);
+
+ private static final BasicReturnMethodHandler _instance = new BasicReturnMethodHandler();
+
+ public static BasicReturnMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _logger.debug("New JmsBounce method received");
+ final UnprocessedMessage msg = new UnprocessedMessage(evt.getChannelId(), (BasicReturnBody) evt.getMethod());
+
+ protocolSession.unprocessedMessageReceived(msg);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
new file mode 100644
index 0000000000..139a32370e
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQChannelClosedException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInvalidRoutingKeyException;
+import org.apache.qpid.client.AMQNoConsumersException;
+import org.apache.qpid.client.AMQNoRouteException;
+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.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChannelCloseMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandler.class);
+
+ private static ChannelCloseMethodHandler _handler = new ChannelCloseMethodHandler();
+
+ public static ChannelCloseMethodHandler getInstance()
+ {
+ return _handler;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _logger.debug("ChannelClose method received");
+ ChannelCloseBody method = (ChannelCloseBody) evt.getMethod();
+
+ AMQConstant errorCode = AMQConstant.getConstant(method.replyCode);
+ AMQShortString reason = method.replyText;
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason);
+ }
+
+ // TODO: Be aware of possible changes to parameter order as versions change.
+ AMQFrame frame = ChannelCloseOkBody.createAMQFrame(evt.getChannelId(), method.getMajor(), method.getMinor());
+ protocolSession.writeFrame(frame);
+ if (errorCode != AMQConstant.REPLY_SUCCESS)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Channel close received with errorCode " + errorCode + ", and reason " + reason);
+ }
+
+ if (errorCode == AMQConstant.NO_CONSUMERS)
+ {
+ throw new AMQNoConsumersException("Error: " + reason, null);
+ }
+ else if (errorCode == AMQConstant.NO_ROUTE)
+ {
+ throw new AMQNoRouteException("Error: " + reason, null);
+ }
+ else if (errorCode == AMQConstant.INVALID_ARGUMENT)
+ {
+ _logger.debug("Broker responded with Invalid Argument.");
+
+ throw new org.apache.qpid.AMQInvalidArgumentException(String.valueOf(reason));
+ }
+ else if (errorCode == AMQConstant.INVALID_ROUTING_KEY)
+ {
+ _logger.debug("Broker responded with Invalid Routing Key.");
+
+ throw new AMQInvalidRoutingKeyException(String.valueOf(reason));
+ }
+ else
+ {
+ throw new AMQChannelClosedException(errorCode, "Error: " + reason);
+ }
+
+ }
+ // fixme why is this only done when the close is expected...
+ // should the above forced closes not also cause a close?
+ protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason));
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java
new file mode 100644
index 0000000000..e1fe2697e5
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.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.client.handler;
+
+import org.apache.qpid.AMQException;
+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.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChannelCloseOkMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseOkMethodHandler.class);
+
+ private static final ChannelCloseOkMethodHandler _instance = new ChannelCloseOkMethodHandler();
+
+ public static ChannelCloseOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _logger.info("Received channel-close-ok for channel-id " + evt.getChannelId());
+
+ // todo this should do the local closure
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java
new file mode 100644
index 0000000000..ca3f46d08b
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQException;
+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.ChannelFlowOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChannelFlowOkMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowOkMethodHandler.class);
+ private static final ChannelFlowOkMethodHandler _instance = new ChannelFlowOkMethodHandler();
+
+ public static ChannelFlowOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelFlowOkMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ ChannelFlowOkBody method = (ChannelFlowOkBody) evt.getMethod();
+ _logger.debug("Received Channel.Flow-Ok message, active = " + method.active);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java
new file mode 100644
index 0000000000..df096d3c4e
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQConnectionClosedException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQAuthenticationException;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.ConnectionCloseOkBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConnectionCloseMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ConnectionCloseMethodHandler.class);
+
+ private static ConnectionCloseMethodHandler _handler = new ConnectionCloseMethodHandler();
+
+ public static ConnectionCloseMethodHandler getInstance()
+ {
+ return _handler;
+ }
+
+ private ConnectionCloseMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _logger.info("ConnectionClose frame received");
+ ConnectionCloseBody method = (ConnectionCloseBody) evt.getMethod();
+
+ // does it matter
+ // stateManager.changeState(AMQState.CONNECTION_CLOSING);
+
+ AMQConstant errorCode = AMQConstant.getConstant(method.replyCode);
+ AMQShortString reason = method.replyText;
+
+ try
+ {
+ // TODO: check whether channel id of zero is appropriate
+ // Be aware of possible changes to parameter order as versions change.
+ protocolSession.writeFrame(ConnectionCloseOkBody.createAMQFrame((short) 0, method.getMajor(),
+ method.getMinor()));
+
+ if (errorCode != AMQConstant.REPLY_SUCCESS)
+ {
+ if (errorCode == AMQConstant.NOT_ALLOWED)
+ {
+ _logger.info("Authentication Error:" + Thread.currentThread().getName());
+
+ protocolSession.closeProtocolSession();
+
+ // todo this is a bit of a fudge (could be conssidered such as each new connection needs a new state manager or at least a fresh state.
+ stateManager.changeState(AMQState.CONNECTION_NOT_STARTED);
+
+ throw new AMQAuthenticationException(errorCode, (reason == null) ? null : reason.toString());
+ }
+ else
+ {
+ _logger.info("Connection close received with error code " + errorCode);
+
+ throw new AMQConnectionClosedException(errorCode, "Error: " + reason);
+ }
+ }
+ }
+ finally
+ {
+ // this actually closes the connection in the case where it is not an error.
+
+ protocolSession.closeProtocolSession();
+
+ stateManager.changeState(AMQState.CONNECTION_CLOSED);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java
new file mode 100644
index 0000000000..2e0f273c32
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener
+{
+ private static final ConnectionOpenOkMethodHandler _instance = new ConnectionOpenOkMethodHandler();
+
+ public static ConnectionOpenOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionOpenOkMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException
+ {
+ stateManager.changeState(AMQState.CONNECTION_OPEN);
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java
new file mode 100644
index 0000000000..213c0eba6e
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.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.client.handler;
+
+import org.apache.qpid.AMQException;
+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.ConnectionRedirectBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConnectionRedirectMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ConnectionRedirectMethodHandler.class);
+
+ private static final int DEFAULT_REDIRECT_PORT = 5672;
+
+ private static ConnectionRedirectMethodHandler _handler = new ConnectionRedirectMethodHandler();
+
+ public static ConnectionRedirectMethodHandler getInstance()
+ {
+ return _handler;
+ }
+
+ private ConnectionRedirectMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _logger.info("ConnectionRedirect frame received");
+ ConnectionRedirectBody method = (ConnectionRedirectBody) evt.getMethod();
+
+ String host = method.host.toString();
+ // the host is in the form hostname:port with the port being optional
+ int portIndex = host.indexOf(':');
+
+ int port;
+ if (portIndex == -1)
+ {
+ port = DEFAULT_REDIRECT_PORT;
+ }
+ else
+ {
+ port = Integer.parseInt(host.substring(portIndex + 1));
+ host = host.substring(0, portIndex);
+
+ }
+
+ protocolSession.failover(host, port);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java
new file mode 100644
index 0000000000..ab6acffeaf
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.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.client.handler;
+
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+import org.apache.qpid.AMQException;
+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.AMQFrame;
+import org.apache.qpid.framing.ConnectionSecureBody;
+import org.apache.qpid.framing.ConnectionSecureOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+public class ConnectionSecureMethodHandler implements StateAwareMethodListener
+{
+ private static final ConnectionSecureMethodHandler _instance = new ConnectionSecureMethodHandler();
+
+ public static ConnectionSecureMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException
+ {
+ SaslClient client = protocolSession.getSaslClient();
+ if (client == null)
+ {
+ throw new AMQException("No SASL client set up - cannot proceed with authentication");
+ }
+
+ ConnectionSecureBody body = (ConnectionSecureBody) evt.getMethod();
+
+ try
+ {
+ // Evaluate server challenge
+ byte[] response = client.evaluateChallenge(body.challenge);
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ AMQFrame responseFrame = ConnectionSecureOkBody.createAMQFrame(evt.getChannelId(),
+ body.getMajor(), body.getMinor(),
+ response); // response
+ protocolSession.writeFrame(responseFrame);
+ }
+ catch (SaslException e)
+ {
+ throw new AMQException("Error processing SASL challenge: " + e, e);
+ }
+
+
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java
new file mode 100644
index 0000000000..f14e256172
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.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.client.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.security.AMQCallbackHandler;
+import org.apache.qpid.client.security.CallbackHandlerRegistry;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.common.ClientProperties;
+import org.apache.qpid.common.QpidProperties;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionStartBody;
+import org.apache.qpid.framing.ConnectionStartOkBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+import org.apache.qpid.framing.ProtocolVersion;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashSet;
+import java.util.StringTokenizer;
+
+public class ConnectionStartMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _log = LoggerFactory.getLogger(ConnectionStartMethodHandler.class);
+
+ private static final ConnectionStartMethodHandler _instance = new ConnectionStartMethodHandler();
+
+ public static ConnectionStartMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionStartMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _log.debug("public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, "
+ + "AMQMethodEvent evt): called");
+
+ ConnectionStartBody body = (ConnectionStartBody) evt.getMethod();
+
+ ProtocolVersion pv = new ProtocolVersion((byte) body.versionMajor, (byte) body.versionMinor);
+
+ // For the purposes of interop, we can make the client accept the broker's version string.
+ // If it does, it then internally records the version as being the latest one that it understands.
+ // It needs to do this since frame lookup is done by version.
+ if (Boolean.getBoolean("qpid.accept.broker.version") && !pv.isSupported())
+ {
+
+ pv = ProtocolVersion.getLatestSupportedVersion();
+ }
+
+ if (pv.isSupported())
+ {
+ protocolSession.setProtocolVersion(pv.getMajorVersion(), pv.getMinorVersion());
+
+ try
+ {
+ // Used to hold the SASL mechanism to authenticate with.
+ String mechanism;
+
+ if (body.mechanisms == null)
+ {
+ throw new AMQException("mechanism not specified in ConnectionStart method frame");
+ }
+ else
+ {
+ mechanism = chooseMechanism(body.mechanisms);
+ _log.debug("mechanism = " + mechanism);
+ }
+
+ if (mechanism == null)
+ {
+ throw new AMQException("No supported security mechanism found, passed: " + new String(body.mechanisms));
+ }
+
+ byte[] saslResponse;
+ try
+ {
+ SaslClient sc =
+ Sasl.createSaslClient(new String[] { mechanism }, null, "AMQP", "localhost", null,
+ createCallbackHandler(mechanism, protocolSession));
+ if (sc == null)
+ {
+ throw new AMQException(
+ "Client SASL configuration error: no SaslClient could be created for mechanism " + mechanism
+ + ". Please ensure all factories are registered. See DynamicSaslRegistrar for "
+ + " details of how to register non-standard SASL client providers.");
+ }
+
+ protocolSession.setSaslClient(sc);
+ saslResponse = (sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[0]) : null);
+ }
+ catch (SaslException e)
+ {
+ protocolSession.setSaslClient(null);
+ throw new AMQException("Unable to create SASL client: " + e, e);
+ }
+
+ if (body.locales == null)
+ {
+ throw new AMQException("Locales is not defined in Connection Start method");
+ }
+
+ final String locales = new String(body.locales, "utf8");
+ final StringTokenizer tokenizer = new StringTokenizer(locales, " ");
+ String selectedLocale = null;
+ if (tokenizer.hasMoreTokens())
+ {
+ selectedLocale = tokenizer.nextToken();
+ }
+ else
+ {
+ throw new AMQException("No locales sent from server, passed: " + locales);
+ }
+
+ stateManager.changeState(AMQState.CONNECTION_NOT_TUNED);
+ FieldTable clientProperties = FieldTableFactory.newFieldTable();
+
+ clientProperties.setString(new AMQShortString(ClientProperties.instance.toString()),
+ protocolSession.getClientID());
+ clientProperties.setString(new AMQShortString(ClientProperties.product.toString()),
+ QpidProperties.getProductName());
+ clientProperties.setString(new AMQShortString(ClientProperties.version.toString()),
+ QpidProperties.getReleaseVersion());
+ clientProperties.setString(new AMQShortString(ClientProperties.platform.toString()), getFullSystemInfo());
+
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ protocolSession.writeFrame(ConnectionStartOkBody.createAMQFrame(evt.getChannelId(),
+ protocolSession.getProtocolMajorVersion(), protocolSession.getProtocolMinorVersion(),
+ clientProperties, // clientProperties
+ new AMQShortString(selectedLocale), // locale
+ new AMQShortString(mechanism), // mechanism
+ saslResponse)); // response
+
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new AMQException("Unable to decode data: " + e, e);
+ }
+ }
+ else
+ {
+ _log.error("Broker requested Protocol [" + body.versionMajor + "-" + body.versionMinor
+ + "] which is not supported by this version of the client library");
+
+ protocolSession.closeProtocolSession();
+ }
+ }
+
+ private String getFullSystemInfo()
+ {
+ StringBuffer fullSystemInfo = new StringBuffer();
+ fullSystemInfo.append(System.getProperty("java.runtime.name"));
+ fullSystemInfo.append(", " + System.getProperty("java.runtime.version"));
+ fullSystemInfo.append(", " + System.getProperty("java.vendor"));
+ fullSystemInfo.append(", " + System.getProperty("os.arch"));
+ fullSystemInfo.append(", " + System.getProperty("os.name"));
+ fullSystemInfo.append(", " + System.getProperty("os.version"));
+ fullSystemInfo.append(", " + System.getProperty("sun.os.patch.level"));
+
+ return fullSystemInfo.toString();
+ }
+
+ private String chooseMechanism(byte[] availableMechanisms) throws UnsupportedEncodingException
+ {
+ final String mechanisms = new String(availableMechanisms, "utf8");
+ StringTokenizer tokenizer = new StringTokenizer(mechanisms, " ");
+ HashSet mechanismSet = new HashSet();
+ while (tokenizer.hasMoreTokens())
+ {
+ mechanismSet.add(tokenizer.nextToken());
+ }
+
+ String preferredMechanisms = CallbackHandlerRegistry.getInstance().getMechanisms();
+ StringTokenizer prefTokenizer = new StringTokenizer(preferredMechanisms, " ");
+ while (prefTokenizer.hasMoreTokens())
+ {
+ String mech = prefTokenizer.nextToken();
+ if (mechanismSet.contains(mech))
+ {
+ return mech;
+ }
+ }
+
+ return null;
+ }
+
+ private AMQCallbackHandler createCallbackHandler(String mechanism, AMQProtocolSession protocolSession)
+ throws AMQException
+ {
+ Class mechanismClass = CallbackHandlerRegistry.getInstance().getCallbackHandlerClass(mechanism);
+ try
+ {
+ Object instance = mechanismClass.newInstance();
+ AMQCallbackHandler cbh = (AMQCallbackHandler) instance;
+ cbh.initialise(protocolSession);
+
+ return cbh;
+ }
+ catch (Exception e)
+ {
+ throw new AMQException("Unable to create callback handler: " + e, e);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java
new file mode 100644
index 0000000000..68556991d7
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.ConnectionTuneParameters;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.StateAwareMethodListener;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionOpenBody;
+import org.apache.qpid.framing.ConnectionTuneBody;
+import org.apache.qpid.framing.ConnectionTuneOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConnectionTuneMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ConnectionTuneMethodHandler.class);
+
+ private static final ConnectionTuneMethodHandler _instance = new ConnectionTuneMethodHandler();
+
+ public static ConnectionTuneMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ protected ConnectionTuneMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _logger.debug("ConnectionTune frame received");
+ ConnectionTuneBody frame = (ConnectionTuneBody) evt.getMethod();
+
+ ConnectionTuneParameters params = protocolSession.getConnectionTuneParameters();
+ if (params == null)
+ {
+ params = new ConnectionTuneParameters();
+ }
+
+ params.setFrameMax(frame.frameMax);
+ params.setChannelMax(frame.channelMax);
+ params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.heartbeat));
+ protocolSession.setConnectionTuneParameters(params);
+
+ stateManager.changeState(AMQState.CONNECTION_NOT_OPENED);
+ protocolSession.writeFrame(createTuneOkFrame(evt.getChannelId(), params, frame.getMajor(), frame.getMinor()));
+
+ String host = protocolSession.getAMQConnection().getVirtualHost();
+ AMQShortString virtualHost = new AMQShortString("/" + host);
+
+ protocolSession.writeFrame(createConnectionOpenFrame(evt.getChannelId(), virtualHost, null, true, frame.getMajor(),
+ frame.getMinor()));
+ }
+
+ protected AMQFrame createConnectionOpenFrame(int channel, AMQShortString path, AMQShortString capabilities,
+ boolean insist, byte major, byte minor)
+ {
+ // Be aware of possible changes to parameter order as versions change.
+ return ConnectionOpenBody.createAMQFrame(channel, major, minor, // AMQP version (major, minor)
+ capabilities, // capabilities
+ insist, // insist
+ path); // virtualHost
+ }
+
+ protected AMQFrame createTuneOkFrame(int channel, ConnectionTuneParameters params, byte major, byte minor)
+ {
+ // Be aware of possible changes to parameter order as versions change.
+ return ConnectionTuneOkBody.createAMQFrame(channel, major, minor, params.getChannelMax(), // channelMax
+ params.getFrameMax(), // frameMax
+ params.getHeartbeat()); // heartbeat
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java
new file mode 100644
index 0000000000..862a9be8d4
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.handler;
+
+import org.apache.qpid.AMQException;
+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.ExchangeBoundOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ExchangeBoundOkMethodHandler.class);
+ private static final ExchangeBoundOkMethodHandler _instance = new ExchangeBoundOkMethodHandler();
+
+ public static ExchangeBoundOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ExchangeBoundOkMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ ExchangeBoundOkBody body = (ExchangeBoundOkBody) evt.getMethod();
+ _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.replyCode + " text: "
+ + body.replyText);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java
new file mode 100644
index 0000000000..65060d44d2
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.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.client.handler;
+
+import org.apache.qpid.AMQException;
+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.QueueDeleteOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class QueueDeleteOkMethodHandler implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(QueueDeleteOkMethodHandler.class);
+ private static final QueueDeleteOkMethodHandler _instance = new QueueDeleteOkMethodHandler();
+
+ public static QueueDeleteOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private QueueDeleteOkMethodHandler()
+ { }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ QueueDeleteOkBody body = (QueueDeleteOkBody) evt.getMethod();
+ _logger.debug("Received Queue.Delete-Ok message, message count: " + body.messageCount);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.java
new file mode 100644
index 0000000000..8741a1cbb6
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.java
@@ -0,0 +1,135 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import javax.jms.JMSException;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.ContentHeaderProperties;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+import java.math.BigDecimal;
+
+public class AMQMessage
+{
+ protected ContentHeaderProperties _contentHeaderProperties;
+
+ /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */
+ protected AMQSession _session;
+
+ protected final long _deliveryTag;
+
+ public AMQMessage(ContentHeaderProperties properties, long deliveryTag)
+ {
+ _contentHeaderProperties = properties;
+ _deliveryTag = deliveryTag;
+ }
+
+ public AMQMessage(ContentHeaderProperties properties)
+ {
+ this(properties, -1);
+ }
+
+ /**
+ * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls
+ * acknowledge()
+ *
+ * @param s the AMQ session that delivered this message
+ */
+ public void setAMQSession(AMQSession s)
+ {
+ _session = s;
+ }
+
+ public AMQSession getAMQSession()
+ {
+ return _session;
+ }
+
+ /**
+ * Get the AMQ message number assigned to this message
+ *
+ * @return the message number
+ */
+ public long getDeliveryTag()
+ {
+ return _deliveryTag;
+ }
+
+ /** Invoked prior to sending the message. Allows the message to be modified if necessary before sending. */
+ public void prepareForSending() throws JMSException
+ {
+ }
+
+ public FieldTable getPropertyHeaders()
+ {
+ return ((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders();
+ }
+
+ public void setDecimalProperty(AMQShortString propertyName, BigDecimal bd) throws JMSException
+ {
+ getPropertyHeaders().setDecimal(propertyName, bd);
+ }
+
+ public void setIntProperty(AMQShortString propertyName, int i) throws JMSException
+ {
+ getPropertyHeaders().setInteger(propertyName, new Integer(i));
+ }
+
+ public void setLongStringProperty(AMQShortString propertyName, String value)
+ {
+ getPropertyHeaders().setString(propertyName, value);
+ }
+
+ public void setTimestampProperty(AMQShortString propertyName, long value)
+ {
+ getPropertyHeaders().setTimestamp(propertyName, value);
+ }
+
+ public void setVoidProperty(AMQShortString propertyName)
+ {
+ getPropertyHeaders().setVoid(propertyName);
+ }
+
+ //** Getters
+
+ public BigDecimal getDecimalProperty(AMQShortString propertyName) throws JMSException
+ {
+ return getPropertyHeaders().getDecimal(propertyName);
+ }
+
+ public int getIntegerProperty(AMQShortString propertyName) throws JMSException
+ {
+ return getPropertyHeaders().getInteger(propertyName);
+ }
+
+ public String getLongStringProperty(AMQShortString propertyName)
+ {
+ return getPropertyHeaders().getString(propertyName);
+ }
+
+ public Long getTimestampProperty(AMQShortString propertyName)
+ {
+ return getPropertyHeaders().getTimestamp(propertyName);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java
new file mode 100644
index 0000000000..af254fbbaf
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java
@@ -0,0 +1,151 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.MessageEOFException;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+/**
+ * @author Apache Software Foundation
+ */
+public abstract class AbstractBytesMessage extends AbstractJMSMessage
+{
+
+ /**
+ * The default initial size of the buffer. The buffer expands automatically.
+ */
+ private static final int DEFAULT_BUFFER_INITIAL_SIZE = 1024;
+
+ AbstractBytesMessage()
+ {
+ this(null);
+ }
+
+ /**
+ * Construct a bytes message with existing data.
+ *
+ * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is
+ * set to auto expand
+ */
+ AbstractBytesMessage(ByteBuffer data)
+ {
+ super(data); // this instanties a content header
+ getContentHeaderProperties().setContentType(getMimeTypeAsShortString());
+
+ if (_data == null)
+ {
+ allocateInitialBuffer();
+ }
+ }
+
+ protected void allocateInitialBuffer()
+ {
+ _data = ByteBuffer.allocate(DEFAULT_BUFFER_INITIAL_SIZE);
+ _data.setAutoExpand(true);
+ }
+
+ AbstractBytesMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange,
+ AMQShortString routingKey, ByteBuffer data) throws AMQException
+ {
+ // TODO: this casting is ugly. Need to review whole ContentHeaderBody idea
+ super(messageNbr, (BasicContentHeaderProperties) contentHeader.properties, exchange, routingKey, data);
+ getContentHeaderProperties().setContentType(getMimeTypeAsShortString());
+ }
+
+ public void clearBodyImpl() throws JMSException
+ {
+ allocateInitialBuffer();
+ }
+
+ public String toBodyString() throws JMSException
+ {
+ checkReadable();
+ try
+ {
+ return getText();
+ }
+ catch (IOException e)
+ {
+ JMSException jmse = new JMSException(e.toString());
+ jmse.setLinkedException(e);
+ throw jmse;
+ }
+ }
+
+ /**
+ * We reset the stream before and after reading the data. This means that toString() will always output
+ * the entire message and also that the caller can then immediately start reading as if toString() had
+ * never been called.
+ *
+ * @return
+ * @throws IOException
+ */
+ private String getText() throws IOException
+ {
+ // this will use the default platform encoding
+ if (_data == null)
+ {
+ return null;
+ }
+
+ int pos = _data.position();
+ _data.rewind();
+ // one byte left is for the end of frame marker
+ if (_data.remaining() == 0)
+ {
+ // this is really redundant since pos must be zero
+ _data.position(pos);
+
+ return null;
+ }
+ else
+ {
+ String data = _data.getString(Charset.forName("UTF8").newDecoder());
+ _data.position(pos);
+
+ return data;
+ }
+ }
+
+ /**
+ * Check that there is at least a certain number of bytes available to read
+ *
+ * @param len the number of bytes
+ * @throws javax.jms.MessageEOFException if there are less than len bytes available to read
+ */
+ protected void checkAvailable(int len) throws MessageEOFException
+ {
+ if (_data.remaining() < len)
+ {
+ throw new MessageEOFException("Unable to read " + len + " bytes");
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java
new file mode 100644
index 0000000000..3b8ce9a98a
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java
@@ -0,0 +1,801 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.client.message;
+
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.MessageEOFException;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageNotReadableException;
+import javax.jms.MessageNotWriteableException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+/**
+ * @author Apache Software Foundation
+ */
+public abstract class AbstractBytesTypedMessage extends AbstractBytesMessage
+{
+
+ protected static final byte BOOLEAN_TYPE = (byte) 1;
+
+ protected static final byte BYTE_TYPE = (byte) 2;
+
+ protected static final byte BYTEARRAY_TYPE = (byte) 3;
+
+ protected static final byte SHORT_TYPE = (byte) 4;
+
+ protected static final byte CHAR_TYPE = (byte) 5;
+
+ protected static final byte INT_TYPE = (byte) 6;
+
+ protected static final byte LONG_TYPE = (byte) 7;
+
+ protected static final byte FLOAT_TYPE = (byte) 8;
+
+ protected static final byte DOUBLE_TYPE = (byte) 9;
+
+ protected static final byte STRING_TYPE = (byte) 10;
+
+ protected static final byte NULL_STRING_TYPE = (byte) 11;
+
+ /**
+ * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read
+ * a byte array in multiple chunks, hence this is used to track how much is left to be read
+ */
+ private int _byteArrayRemaining = -1;
+
+ AbstractBytesTypedMessage()
+ {
+ this(null);
+ }
+
+ /**
+ * Construct a stream message with existing data.
+ *
+ * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is
+ * set to auto expand
+ */
+ AbstractBytesTypedMessage(ByteBuffer data)
+ {
+ super(data); // this instanties a content header
+ }
+
+
+ AbstractBytesTypedMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange,
+ AMQShortString routingKey, ByteBuffer data) throws AMQException
+ {
+ super(messageNbr, contentHeader, exchange, routingKey, data);
+ }
+
+
+ protected byte readWireType() throws MessageFormatException, MessageEOFException,
+ MessageNotReadableException
+ {
+ checkReadable();
+ checkAvailable(1);
+ return _data.get();
+ }
+
+ protected void writeTypeDiscriminator(byte type) throws MessageNotWriteableException
+ {
+ checkWritable();
+ _data.put(type);
+ _changedData = true;
+ }
+
+ protected boolean readBoolean() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ boolean result;
+ try
+ {
+ switch (wireType)
+ {
+ case BOOLEAN_TYPE:
+ checkAvailable(1);
+ result = readBooleanImpl();
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = Boolean.parseBoolean(readStringImpl());
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a boolean");
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ private boolean readBooleanImpl()
+ {
+ return _data.get() != 0;
+ }
+
+ protected byte readByte() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ byte result;
+ try
+ {
+ switch (wireType)
+ {
+ case BYTE_TYPE:
+ checkAvailable(1);
+ result = readByteImpl();
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = Byte.parseByte(readStringImpl());
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a byte");
+ }
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ return result;
+ }
+
+ private byte readByteImpl()
+ {
+ return _data.get();
+ }
+
+ protected short readShort() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ short result;
+ try
+ {
+ switch (wireType)
+ {
+ case SHORT_TYPE:
+ checkAvailable(2);
+ result = readShortImpl();
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = Short.parseShort(readStringImpl());
+ break;
+ case BYTE_TYPE:
+ checkAvailable(1);
+ result = readByteImpl();
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a short");
+ }
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ return result;
+ }
+
+ private short readShortImpl()
+ {
+ return _data.getShort();
+ }
+
+ /**
+ * Note that this method reads a unicode character as two bytes from the stream
+ *
+ * @return the character read from the stream
+ * @throws javax.jms.JMSException
+ */
+ protected char readChar() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ try
+ {
+ if(wireType == NULL_STRING_TYPE){
+ throw new NullPointerException();
+ }
+
+ if (wireType != CHAR_TYPE)
+ {
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a char");
+ }
+ else
+ {
+ checkAvailable(2);
+ return readCharImpl();
+ }
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ private char readCharImpl()
+ {
+ return _data.getChar();
+ }
+
+ protected int readInt() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ int result;
+ try
+ {
+ switch (wireType)
+ {
+ case INT_TYPE:
+ checkAvailable(4);
+ result = readIntImpl();
+ break;
+ case SHORT_TYPE:
+ checkAvailable(2);
+ result = readShortImpl();
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = Integer.parseInt(readStringImpl());
+ break;
+ case BYTE_TYPE:
+ checkAvailable(1);
+ result = readByteImpl();
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to an int");
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ protected int readIntImpl()
+ {
+ return _data.getInt();
+ }
+
+ protected long readLong() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ long result;
+ try
+ {
+ switch (wireType)
+ {
+ case LONG_TYPE:
+ checkAvailable(8);
+ result = readLongImpl();
+ break;
+ case INT_TYPE:
+ checkAvailable(4);
+ result = readIntImpl();
+ break;
+ case SHORT_TYPE:
+ checkAvailable(2);
+ result = readShortImpl();
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = Long.parseLong(readStringImpl());
+ break;
+ case BYTE_TYPE:
+ checkAvailable(1);
+ result = readByteImpl();
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a long");
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ private long readLongImpl()
+ {
+ return _data.getLong();
+ }
+
+ protected float readFloat() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ float result;
+ try
+ {
+ switch (wireType)
+ {
+ case FLOAT_TYPE:
+ checkAvailable(4);
+ result = readFloatImpl();
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = Float.parseFloat(readStringImpl());
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a float");
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ private float readFloatImpl()
+ {
+ return _data.getFloat();
+ }
+
+ protected double readDouble() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ double result;
+ try
+ {
+ switch (wireType)
+ {
+ case DOUBLE_TYPE:
+ checkAvailable(8);
+ result = readDoubleImpl();
+ break;
+ case FLOAT_TYPE:
+ checkAvailable(4);
+ result = readFloatImpl();
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = Double.parseDouble(readStringImpl());
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a double");
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ private double readDoubleImpl()
+ {
+ return _data.getDouble();
+ }
+
+ protected String readString() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ String result;
+ try
+ {
+ switch (wireType)
+ {
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = readStringImpl();
+ break;
+ case NULL_STRING_TYPE:
+ result = null;
+ throw new NullPointerException("data is null");
+ case BOOLEAN_TYPE:
+ checkAvailable(1);
+ result = String.valueOf(readBooleanImpl());
+ break;
+ case LONG_TYPE:
+ checkAvailable(8);
+ result = String.valueOf(readLongImpl());
+ break;
+ case INT_TYPE:
+ checkAvailable(4);
+ result = String.valueOf(readIntImpl());
+ break;
+ case SHORT_TYPE:
+ checkAvailable(2);
+ result = String.valueOf(readShortImpl());
+ break;
+ case BYTE_TYPE:
+ checkAvailable(1);
+ result = String.valueOf(readByteImpl());
+ break;
+ case FLOAT_TYPE:
+ checkAvailable(4);
+ result = String.valueOf(readFloatImpl());
+ break;
+ case DOUBLE_TYPE:
+ checkAvailable(8);
+ result = String.valueOf(readDoubleImpl());
+ break;
+ case CHAR_TYPE:
+ checkAvailable(2);
+ result = String.valueOf(readCharImpl());
+ break;
+ default:
+ _data.position(position);
+ throw new MessageFormatException("Unable to convert " + wireType + " to a String");
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ protected String readStringImpl() throws JMSException
+ {
+ try
+ {
+ return _data.getString(Charset.forName("UTF-8").newDecoder());
+ }
+ catch (CharacterCodingException e)
+ {
+ JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e);
+ je.setLinkedException(e);
+ throw je;
+ }
+ }
+
+ protected int readBytes(byte[] bytes) throws JMSException
+ {
+ if (bytes == null)
+ {
+ throw new IllegalArgumentException("byte array must not be null");
+ }
+ checkReadable();
+ // first call
+ if (_byteArrayRemaining == -1)
+ {
+ // type discriminator checked separately so you get a MessageFormatException rather than
+ // an EOF even in the case where both would be applicable
+ checkAvailable(1);
+ byte wireType = readWireType();
+ if (wireType != BYTEARRAY_TYPE)
+ {
+ throw new MessageFormatException("Unable to convert " + wireType + " to a byte array");
+ }
+ checkAvailable(4);
+ int size = _data.getInt();
+ // length of -1 indicates null
+ if (size == -1)
+ {
+ return -1;
+ }
+ else
+ {
+ if (size > _data.remaining())
+ {
+ throw new MessageEOFException("Byte array has stated length " + size + " but message only contains " +
+ _data.remaining() + " bytes");
+ }
+ else
+ {
+ _byteArrayRemaining = size;
+ }
+ }
+ }
+ else if (_byteArrayRemaining == 0)
+ {
+ _byteArrayRemaining = -1;
+ return -1;
+ }
+
+ int returnedSize = readBytesImpl(bytes);
+ if (returnedSize < bytes.length)
+ {
+ _byteArrayRemaining = -1;
+ }
+ return returnedSize;
+ }
+
+ private int readBytesImpl(byte[] bytes)
+ {
+ int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining);
+ _byteArrayRemaining -= count;
+
+ if (count == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ _data.get(bytes, 0, count);
+ return count;
+ }
+ }
+
+ protected Object readObject() throws JMSException
+ {
+ int position = _data.position();
+ byte wireType = readWireType();
+ Object result = null;
+ try
+ {
+ switch (wireType)
+ {
+ case BOOLEAN_TYPE:
+ checkAvailable(1);
+ result = readBooleanImpl();
+ break;
+ case BYTE_TYPE:
+ checkAvailable(1);
+ result = readByteImpl();
+ break;
+ case BYTEARRAY_TYPE:
+ checkAvailable(4);
+ int size = _data.getInt();
+ if (size == -1)
+ {
+ result = null;
+ }
+ else
+ {
+ _byteArrayRemaining = size;
+ byte[] bytesResult = new byte[size];
+ readBytesImpl(bytesResult);
+ result = bytesResult;
+ }
+ break;
+ case SHORT_TYPE:
+ checkAvailable(2);
+ result = readShortImpl();
+ break;
+ case CHAR_TYPE:
+ checkAvailable(2);
+ result = readCharImpl();
+ break;
+ case INT_TYPE:
+ checkAvailable(4);
+ result = readIntImpl();
+ break;
+ case LONG_TYPE:
+ checkAvailable(8);
+ result = readLongImpl();
+ break;
+ case FLOAT_TYPE:
+ checkAvailable(4);
+ result = readFloatImpl();
+ break;
+ case DOUBLE_TYPE:
+ checkAvailable(8);
+ result = readDoubleImpl();
+ break;
+ case NULL_STRING_TYPE:
+ result = null;
+ break;
+ case STRING_TYPE:
+ checkAvailable(1);
+ result = readStringImpl();
+ break;
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ _data.position(position);
+ throw e;
+ }
+ }
+
+ protected void writeBoolean(boolean b) throws JMSException
+ {
+ writeTypeDiscriminator(BOOLEAN_TYPE);
+ _data.put(b ? (byte) 1 : (byte) 0);
+ }
+
+ protected void writeByte(byte b) throws JMSException
+ {
+ writeTypeDiscriminator(BYTE_TYPE);
+ _data.put(b);
+ }
+
+ protected void writeShort(short i) throws JMSException
+ {
+ writeTypeDiscriminator(SHORT_TYPE);
+ _data.putShort(i);
+ }
+
+ protected void writeChar(char c) throws JMSException
+ {
+ writeTypeDiscriminator(CHAR_TYPE);
+ _data.putChar(c);
+ }
+
+ protected void writeInt(int i) throws JMSException
+ {
+ writeTypeDiscriminator(INT_TYPE);
+ writeIntImpl(i);
+ }
+
+ protected void writeIntImpl(int i)
+ {
+ _data.putInt(i);
+ }
+
+ protected void writeLong(long l) throws JMSException
+ {
+ writeTypeDiscriminator(LONG_TYPE);
+ _data.putLong(l);
+ }
+
+ protected void writeFloat(float v) throws JMSException
+ {
+ writeTypeDiscriminator(FLOAT_TYPE);
+ _data.putFloat(v);
+ }
+
+ protected void writeDouble(double v) throws JMSException
+ {
+ writeTypeDiscriminator(DOUBLE_TYPE);
+ _data.putDouble(v);
+ }
+
+ protected void writeString(String string) throws JMSException
+ {
+ if (string == null)
+ {
+ writeTypeDiscriminator(NULL_STRING_TYPE);
+ }
+ else
+ {
+ writeTypeDiscriminator(STRING_TYPE);
+ try
+ {
+ writeStringImpl(string);
+ }
+ catch (CharacterCodingException e)
+ {
+ JMSException ex = new JMSException("Unable to encode string: " + e);
+ ex.setLinkedException(e);
+ throw ex;
+ }
+ }
+ }
+
+ protected void writeStringImpl(String string)
+ throws CharacterCodingException
+ {
+ _data.putString(string, Charset.forName("UTF-8").newEncoder());
+ // we must write the null terminator ourselves
+ _data.put((byte) 0);
+ }
+
+ protected void writeBytes(byte[] bytes) throws JMSException
+ {
+ writeBytes(bytes, 0, bytes == null ? 0 : bytes.length);
+ }
+
+ protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException
+ {
+ writeTypeDiscriminator(BYTEARRAY_TYPE);
+ if (bytes == null)
+ {
+ _data.putInt(-1);
+ }
+ else
+ {
+ _data.putInt(length);
+ _data.put(bytes, offset, length);
+ }
+ }
+
+ protected void writeObject(Object object) throws JMSException
+ {
+ checkWritable();
+ Class clazz;
+
+ if (object == null)
+ {
+ // string handles the output of null values
+ clazz = String.class;
+ }
+ else
+ {
+ clazz = object.getClass();
+ }
+
+ if (clazz == Byte.class)
+ {
+ writeByte((Byte) object);
+ }
+ else if (clazz == Boolean.class)
+ {
+ writeBoolean((Boolean) object);
+ }
+ else if (clazz == byte[].class)
+ {
+ writeBytes((byte[]) object);
+ }
+ else if (clazz == Short.class)
+ {
+ writeShort((Short) object);
+ }
+ else if (clazz == Character.class)
+ {
+ writeChar((Character) object);
+ }
+ else if (clazz == Integer.class)
+ {
+ writeInt((Integer) object);
+ }
+ else if (clazz == Long.class)
+ {
+ writeLong((Long) object);
+ }
+ else if (clazz == Float.class)
+ {
+ writeFloat((Float) object);
+ }
+ else if (clazz == Double.class)
+ {
+ writeDouble((Double) object);
+ }
+ else if (clazz == String.class)
+ {
+ writeString((String) object);
+ }
+ else
+ {
+ throw new MessageFormatException("Only primitives plus byte arrays and String are valid types");
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java
new file mode 100644
index 0000000000..2dfeb19268
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java
@@ -0,0 +1,685 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import org.apache.commons.collections.map.ReferenceMap;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.*;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.BindingURL;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MessageNotReadableException;
+import javax.jms.MessageNotWriteableException;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.UUID;
+
+public abstract class AbstractJMSMessage extends AMQMessage implements org.apache.qpid.jms.Message
+{
+ private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap());
+
+ protected boolean _redelivered;
+
+ protected ByteBuffer _data;
+ private boolean _readableProperties = false;
+ protected boolean _readableMessage = false;
+ protected boolean _changedData;
+ private Destination _destination;
+ private JMSHeaderAdapter _headerAdapter;
+ private BasicMessageConsumer _consumer;
+ private boolean _strictAMQP;
+
+ protected AbstractJMSMessage(ByteBuffer data)
+ {
+ super(new BasicContentHeaderProperties());
+ _data = data;
+ if (_data != null)
+ {
+ _data.acquire();
+ }
+
+ _readableProperties = false;
+ _readableMessage = (data != null);
+ _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,
+ AMQShortString routingKey, ByteBuffer data) throws AMQException
+ {
+ this(contentHeader, deliveryTag);
+
+ Integer type = contentHeader.getHeaders().getInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName());
+
+ AMQDestination dest;
+
+ if (AMQDestination.QUEUE_TYPE.equals(type))
+ {
+ dest = new AMQQueue(exchange, routingKey, routingKey);
+ }
+ else if (AMQDestination.TOPIC_TYPE.equals(type))
+ {
+ dest = new AMQTopic(exchange, routingKey, null);
+ }
+ else
+ {
+ dest = new AMQUndefinedDestination(exchange, routingKey, null);
+ }
+ // Destination dest = AMQDestination.createDestination(url);
+ setJMSDestination(dest);
+
+ _data = data;
+ if (_data != null)
+ {
+ _data.acquire();
+ }
+
+ _readableMessage = data != null;
+
+ }
+
+ protected AbstractJMSMessage(BasicContentHeaderProperties contentHeader, long deliveryTag)
+ {
+ super(contentHeader, deliveryTag);
+ _readableProperties = (_contentHeaderProperties != null);
+ _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders());
+ }
+
+ public String getJMSMessageID() throws JMSException
+ {
+ if (getContentHeaderProperties().getMessageIdAsString() == null)
+ {
+ getContentHeaderProperties().setMessageId("ID:" + UUID.randomUUID());
+ }
+
+ return getContentHeaderProperties().getMessageIdAsString();
+ }
+
+ public void setJMSMessageID(String messageId) throws JMSException
+ {
+ getContentHeaderProperties().setMessageId(messageId);
+ }
+
+ public long getJMSTimestamp() throws JMSException
+ {
+ return getContentHeaderProperties().getTimestamp();
+ }
+
+ public void setJMSTimestamp(long timestamp) throws JMSException
+ {
+ getContentHeaderProperties().setTimestamp(timestamp);
+ }
+
+ public byte[] getJMSCorrelationIDAsBytes() throws JMSException
+ {
+ return getContentHeaderProperties().getCorrelationIdAsString().getBytes();
+ }
+
+ public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException
+ {
+ getContentHeaderProperties().setCorrelationId(new String(bytes));
+ }
+
+ public void setJMSCorrelationID(String correlationId) throws JMSException
+ {
+ getContentHeaderProperties().setCorrelationId(correlationId);
+ }
+
+ public String getJMSCorrelationID() throws JMSException
+ {
+ return getContentHeaderProperties().getCorrelationIdAsString();
+ }
+
+ public Destination getJMSReplyTo() throws JMSException
+ {
+ String replyToEncoding = getContentHeaderProperties().getReplyToAsString();
+ if (replyToEncoding == null)
+ {
+ return null;
+ }
+ else
+ {
+ Destination dest = (Destination) _destinationCache.get(replyToEncoding);
+ if (dest == null)
+ {
+ try
+ {
+ BindingURL binding = new AMQBindingURL(replyToEncoding);
+ dest = AMQDestination.createDestination(binding);
+ }
+ catch (URLSyntaxException e)
+ {
+ throw new JMSAMQException("Illegal value in JMS_ReplyTo property: " + replyToEncoding, e);
+ }
+
+ _destinationCache.put(replyToEncoding, dest);
+ }
+
+ return dest;
+ }
+ }
+
+ public void setJMSReplyTo(Destination destination) throws JMSException
+ {
+ if (destination == null)
+ {
+ throw new IllegalArgumentException("Null destination not allowed");
+ }
+
+ if (!(destination instanceof AMQDestination))
+ {
+ throw new IllegalArgumentException(
+ "ReplyTo destination may only be an AMQDestination - passed argument was type " + destination.getClass());
+ }
+
+ final AMQDestination amqd = (AMQDestination) destination;
+
+ final AMQShortString encodedDestination = amqd.getEncodedName();
+ _destinationCache.put(encodedDestination, destination);
+ getContentHeaderProperties().setReplyTo(encodedDestination);
+ }
+
+ public Destination getJMSDestination() throws JMSException
+ {
+ return _destination;
+ }
+
+ public void setJMSDestination(Destination destination)
+ {
+ _destination = destination;
+ }
+
+ public int getJMSDeliveryMode() throws JMSException
+ {
+ return getContentHeaderProperties().getDeliveryMode();
+ }
+
+ public void setJMSDeliveryMode(int i) throws JMSException
+ {
+ getContentHeaderProperties().setDeliveryMode((byte) i);
+ }
+
+ public BasicContentHeaderProperties getContentHeaderProperties()
+ {
+ return (BasicContentHeaderProperties) _contentHeaderProperties;
+ }
+
+ public boolean getJMSRedelivered() throws JMSException
+ {
+ return _redelivered;
+ }
+
+ public void setJMSRedelivered(boolean b) throws JMSException
+ {
+ _redelivered = b;
+ }
+
+ public String getJMSType() throws JMSException
+ {
+ return getContentHeaderProperties().getTypeAsString();
+ }
+
+ public void setJMSType(String string) throws JMSException
+ {
+ getContentHeaderProperties().setType(string);
+ }
+
+ public long getJMSExpiration() throws JMSException
+ {
+ return getContentHeaderProperties().getExpiration();
+ }
+
+ public void setJMSExpiration(long l) throws JMSException
+ {
+ getContentHeaderProperties().setExpiration(l);
+ }
+
+ public int getJMSPriority() throws JMSException
+ {
+ return getContentHeaderProperties().getPriority();
+ }
+
+ public void setJMSPriority(int i) throws JMSException
+ {
+ getContentHeaderProperties().setPriority((byte) i);
+ }
+
+ public void clearProperties() throws JMSException
+ {
+ getJmsHeaders().clear();
+
+ _readableProperties = false;
+ }
+
+ public void clearBody() throws JMSException
+ {
+ clearBodyImpl();
+ _readableMessage = false;
+ }
+
+ public boolean propertyExists(AMQShortString propertyName) throws JMSException
+ {
+ return getJmsHeaders().propertyExists(propertyName);
+ }
+
+ public boolean propertyExists(String propertyName) throws JMSException
+ {
+ return getJmsHeaders().propertyExists(propertyName);
+ }
+
+ public boolean getBooleanProperty(AMQShortString propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getBoolean(propertyName);
+ }
+
+ public boolean getBooleanProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getBoolean(propertyName);
+ }
+
+ public byte getByteProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getByte(propertyName);
+ }
+
+ public byte[] getBytesProperty(AMQShortString propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getBytes(propertyName);
+ }
+
+ public short getShortProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getShort(propertyName);
+ }
+
+ public int getIntProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getInteger(propertyName);
+ }
+
+ public long getLongProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getLong(propertyName);
+ }
+
+ public float getFloatProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getFloat(propertyName);
+ }
+
+ public double getDoubleProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getDouble(propertyName);
+ }
+
+ public String getStringProperty(String propertyName) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ return getJmsHeaders().getString(propertyName);
+ }
+
+ public Object getObjectProperty(String propertyName) throws JMSException
+ {
+ return getJmsHeaders().getObject(propertyName);
+ }
+
+ public Enumeration getPropertyNames() throws JMSException
+ {
+ return getJmsHeaders().getPropertyNames();
+ }
+
+ public void setBooleanProperty(AMQShortString propertyName, boolean b) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setBoolean(propertyName, b);
+ }
+
+ public void setBooleanProperty(String propertyName, boolean b) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setBoolean(propertyName, b);
+ }
+
+ public void setByteProperty(String propertyName, byte b) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setByte(propertyName, new Byte(b));
+ }
+
+ public void setBytesProperty(AMQShortString propertyName, byte[] bytes) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setBytes(propertyName, bytes);
+ }
+
+ public void setShortProperty(String propertyName, short i) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setShort(propertyName, new Short(i));
+ }
+
+ public void setIntProperty(String propertyName, int i) throws JMSException
+ {
+ checkWritableProperties();
+ JMSHeaderAdapter.checkPropertyName(propertyName);
+ super.setIntProperty(new AMQShortString(propertyName), new Integer(i));
+ }
+
+ public void setLongProperty(String propertyName, long l) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setLong(propertyName, new Long(l));
+ }
+
+ public void setFloatProperty(String propertyName, float f) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setFloat(propertyName, new Float(f));
+ }
+
+ public void setDoubleProperty(String propertyName, double v) throws JMSException
+ {
+ if (_strictAMQP)
+ {
+ throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP");
+ }
+
+ checkWritableProperties();
+ getJmsHeaders().setDouble(propertyName, new Double(v));
+ }
+
+ public void setStringProperty(String propertyName, String value) throws JMSException
+ {
+ checkWritableProperties();
+ JMSHeaderAdapter.checkPropertyName(propertyName);
+ super.setLongStringProperty(new AMQShortString(propertyName), value);
+ }
+
+ public void setObjectProperty(String propertyName, Object object) throws JMSException
+ {
+ checkWritableProperties();
+ getJmsHeaders().setObject(propertyName, object);
+ }
+
+ protected void removeProperty(AMQShortString propertyName) throws JMSException
+ {
+ getJmsHeaders().remove(propertyName);
+ }
+
+ protected void removeProperty(String propertyName) throws JMSException
+ {
+ getJmsHeaders().remove(propertyName);
+ }
+
+ public void acknowledgeThis() throws JMSException
+ {
+ // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge
+ // is not specified. In our case, we only set the session field where client acknowledge mode is specified.
+ if (_session != null)
+ {
+ if (_session.getAMQConnection().isClosed())
+ {
+ throw new javax.jms.IllegalStateException("Connection is already closed");
+ }
+
+ // we set multiple to true here since acknowledgement implies acknowledge of all previous messages
+ // received on the session
+ _session.acknowledgeMessage(_deliveryTag, true);
+ }
+ }
+
+ public void acknowledge() throws JMSException
+ {
+ if (_session != null)
+ {
+ _session.acknowledge();
+ }
+ }
+
+ /**
+ * This forces concrete classes to implement clearBody()
+ *
+ * @throws JMSException
+ */
+ public abstract void clearBodyImpl() throws JMSException;
+
+ /**
+ * Get a String representation of the body of the message. Used in the toString() method which outputs this before
+ * message properties.
+ */
+ public abstract String toBodyString() throws JMSException;
+
+ public String getMimeType()
+ {
+ return getMimeTypeAsShortString().toString();
+ }
+
+ public abstract AMQShortString getMimeTypeAsShortString();
+
+ public String toString()
+ {
+ try
+ {
+ StringBuffer buf = new StringBuffer("Body:\n");
+ buf.append(toBodyString());
+ buf.append("\nJMS Correlation ID: ").append(getJMSCorrelationID());
+ buf.append("\nJMS timestamp: ").append(getJMSTimestamp());
+ buf.append("\nJMS expiration: ").append(getJMSExpiration());
+ buf.append("\nJMS priority: ").append(getJMSPriority());
+ buf.append("\nJMS delivery mode: ").append(getJMSDeliveryMode());
+ buf.append("\nJMS reply to: ").append(String.valueOf(getJMSReplyTo()));
+ buf.append("\nJMS Redelivered: ").append(_redelivered);
+ buf.append("\nJMS Destination: ").append(getJMSDestination());
+ buf.append("\nJMS Type: ").append(getJMSType());
+ buf.append("\nJMS MessageID: ").append(getJMSMessageID());
+ buf.append("\nAMQ message number: ").append(_deliveryTag);
+
+ buf.append("\nProperties:");
+ if (getJmsHeaders().isEmpty())
+ {
+ buf.append("<NONE>");
+ }
+ else
+ {
+ buf.append('\n').append(getJmsHeaders().getHeaders());
+ }
+
+ return buf.toString();
+ }
+ catch (JMSException e)
+ {
+ return e.toString();
+ }
+ }
+
+ public void setUnderlyingMessagePropertiesMap(FieldTable messageProperties)
+ {
+ getContentHeaderProperties().setHeaders(messageProperties);
+ }
+
+ public JMSHeaderAdapter getJmsHeaders()
+ {
+ return _headerAdapter;
+ }
+
+ public ByteBuffer getData()
+ {
+ // make sure we rewind the data just in case any method has moved the
+ // position beyond the start
+ if (_data != null)
+ {
+ reset();
+ }
+
+ return _data;
+ }
+
+ protected void checkReadable() throws MessageNotReadableException
+ {
+ if (!_readableMessage)
+ {
+ throw new MessageNotReadableException("You need to call reset() to make the message readable");
+ }
+ }
+
+ protected void checkWritable() throws MessageNotWriteableException
+ {
+ if (_readableMessage)
+ {
+ throw new MessageNotWriteableException("You need to call clearBody() to make the message writable");
+ }
+ }
+
+ protected void checkWritableProperties() throws MessageNotWriteableException
+ {
+ if (_readableProperties)
+ {
+ throw new MessageNotWriteableException("You need to call clearProperties() to make the message writable");
+ }
+ }
+
+ public boolean isReadable()
+ {
+ return _readableMessage;
+ }
+
+ public boolean isWritable()
+ {
+ return !_readableMessage;
+ }
+
+ public void reset()
+ {
+ if (!_changedData)
+ {
+ _data.rewind();
+ }
+ else
+ {
+ _data.flip();
+ _changedData = false;
+ }
+ }
+
+ public void setConsumer(BasicMessageConsumer basicMessageConsumer)
+ {
+ _consumer = basicMessageConsumer;
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java
new file mode 100644
index 0000000000..87df7e1337
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class AbstractJMSMessageFactory implements MessageFactory
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AbstractJMSMessageFactory.class);
+
+ protected abstract AbstractJMSMessage createMessage(long messageNbr, ByteBuffer data, AMQShortString exchange,
+ AMQShortString routingKey, ContentHeaderBody contentHeader) throws AMQException;
+
+ protected AbstractJMSMessage createMessageWithBody(long messageNbr, ContentHeaderBody contentHeader,
+ AMQShortString exchange, AMQShortString routingKey, List bodies) throws AMQException
+ {
+ ByteBuffer data;
+ final boolean debug = _logger.isDebugEnabled();
+
+ // we optimise the non-fragmented case to avoid copying
+ if ((bodies != null) && (bodies.size() == 1))
+ {
+ if (debug)
+ {
+ _logger.debug("Non-fragmented message body (bodySize=" + contentHeader.bodySize + ")");
+ }
+
+ data = ((ContentBody) bodies.get(0)).payload;
+ }
+ else if (bodies != null)
+ {
+ if (debug)
+ {
+ _logger.debug("Fragmented message body (" + bodies.size() + " frames, bodySize=" + contentHeader.bodySize
+ + ")");
+ }
+
+ data = ByteBuffer.allocate((int) contentHeader.bodySize); // XXX: Is cast a problem?
+ final Iterator it = bodies.iterator();
+ while (it.hasNext())
+ {
+ ContentBody cb = (ContentBody) it.next();
+ data.put(cb.payload);
+ cb.payload.release();
+ }
+
+ data.flip();
+ }
+ else // bodies == null
+ {
+ data = ByteBuffer.allocate(0);
+ }
+
+ if (debug)
+ {
+ _logger.debug("Creating message from buffer with position=" + data.position() + " and remaining="
+ + data.remaining());
+ }
+
+ return createMessage(messageNbr, data, exchange, routingKey, contentHeader);
+ }
+
+ public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, ContentHeaderBody contentHeader,
+ AMQShortString exchange, AMQShortString routingKey, List bodies) throws JMSException, AMQException
+ {
+ final AbstractJMSMessage msg = createMessageWithBody(messageNbr, contentHeader, exchange, routingKey, bodies);
+ msg.setJMSRedelivered(redelivered);
+
+ return msg;
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java
new file mode 100644
index 0000000000..19382b58c3
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java
@@ -0,0 +1,388 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+import javax.jms.MessageFormatException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessage
+{
+ public static final String MIME_TYPE = "application/octet-stream";
+ private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE);
+
+
+ public JMSBytesMessage()
+ {
+ this(null);
+ }
+
+ /**
+ * Construct a bytes message with existing data.
+ *
+ * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is
+ * set to auto expand
+ */
+ JMSBytesMessage(ByteBuffer data)
+ {
+ super(data); // this instanties a content header
+ }
+
+ JMSBytesMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange,
+ AMQShortString routingKey, ByteBuffer data) throws AMQException
+ {
+ super(messageNbr, contentHeader, exchange, routingKey, data);
+ }
+
+ public void reset()
+ {
+ super.reset();
+ _readableMessage = true;
+ }
+
+ public AMQShortString getMimeTypeAsShortString()
+ {
+ return MIME_TYPE_SHORT_STRING;
+ }
+
+ public long getBodyLength() throws JMSException
+ {
+ checkReadable();
+ return _data.limit();
+ }
+
+ public boolean readBoolean() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(1);
+ return _data.get() != 0;
+ }
+
+ public byte readByte() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(1);
+ return _data.get();
+ }
+
+ public int readUnsignedByte() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(1);
+ return _data.getUnsigned();
+ }
+
+ public short readShort() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(2);
+ return _data.getShort();
+ }
+
+ public int readUnsignedShort() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(2);
+ return _data.getUnsignedShort();
+ }
+
+ /**
+ * Note that this method reads a unicode character as two bytes from the stream
+ *
+ * @return the character read from the stream
+ * @throws JMSException
+ */
+ public char readChar() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(2);
+ return _data.getChar();
+ }
+
+ public int readInt() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(4);
+ return _data.getInt();
+ }
+
+ public long readLong() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(8);
+ return _data.getLong();
+ }
+
+ public float readFloat() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(4);
+ return _data.getFloat();
+ }
+
+ public double readDouble() throws JMSException
+ {
+ checkReadable();
+ checkAvailable(8);
+ return _data.getDouble();
+ }
+
+ public String readUTF() throws JMSException
+ {
+ checkReadable();
+ // we check only for one byte since theoretically the string could be only a
+ // single byte when using UTF-8 encoding
+
+ try
+ {
+ short length = readShort();
+ if(length == 0)
+ {
+ return "";
+ }
+ else
+ {
+ CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
+ ByteBuffer encodedString = _data.slice();
+ encodedString.limit(length);
+ _data.position(_data.position()+length);
+ CharBuffer string = decoder.decode(encodedString.buf());
+
+ return string.toString();
+ }
+
+
+
+ }
+ catch (CharacterCodingException e)
+ {
+ JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e);
+ je.setLinkedException(e);
+ throw je;
+ }
+ }
+
+ public int readBytes(byte[] bytes) throws JMSException
+ {
+ if (bytes == null)
+ {
+ throw new IllegalArgumentException("byte array must not be null");
+ }
+ checkReadable();
+ int count = (_data.remaining() >= bytes.length ? bytes.length : _data.remaining());
+ if (count == 0)
+ {
+ return -1;
+ }
+ else
+ {
+ _data.get(bytes, 0, count);
+ return count;
+ }
+ }
+
+ public int readBytes(byte[] bytes, int maxLength) throws JMSException
+ {
+ if (bytes == null)
+ {
+ throw new IllegalArgumentException("byte array must not be null");
+ }
+ if (maxLength > bytes.length)
+ {
+ throw new IllegalArgumentException("maxLength must be <= bytes.length");
+ }
+ checkReadable();
+ int count = (_data.remaining() >= maxLength ? maxLength : _data.remaining());
+ if (count == 0)
+ {
+ return -1;
+ }
+ else
+ {
+ _data.get(bytes, 0, count);
+ return count;
+ }
+ }
+
+ public void writeBoolean(boolean b) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.put(b ? (byte) 1 : (byte) 0);
+ }
+
+ public void writeByte(byte b) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.put(b);
+ }
+
+ public void writeShort(short i) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.putShort(i);
+ }
+
+ public void writeChar(char c) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.putChar(c);
+ }
+
+ public void writeInt(int i) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.putInt(i);
+ }
+
+ public void writeLong(long l) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.putLong(l);
+ }
+
+ public void writeFloat(float v) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.putFloat(v);
+ }
+
+ public void writeDouble(double v) throws JMSException
+ {
+ checkWritable();
+ _changedData = true;
+ _data.putDouble(v);
+ }
+
+ public void writeUTF(String string) throws JMSException
+ {
+ checkWritable();
+ try
+ {
+ CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
+ java.nio.ByteBuffer encodedString = encoder.encode(CharBuffer.wrap(string));
+
+ _data.putShort((short)encodedString.limit());
+ _data.put(encodedString);
+ _changedData = true;
+ //_data.putString(string, Charset.forName("UTF-8").newEncoder());
+ // we must add the null terminator manually
+ //_data.put((byte)0);
+ }
+ catch (CharacterCodingException e)
+ {
+ JMSException ex = new JMSException("Unable to encode string: " + e);
+ ex.setLinkedException(e);
+ throw ex;
+ }
+ }
+
+ public void writeBytes(byte[] bytes) throws JMSException
+ {
+ checkWritable();
+ _data.put(bytes);
+ _changedData = true;
+ }
+
+ public void writeBytes(byte[] bytes, int offset, int length) throws JMSException
+ {
+ checkWritable();
+ _data.put(bytes, offset, length);
+ _changedData = true;
+ }
+
+ public void writeObject(Object object) throws JMSException
+ {
+ checkWritable();
+ if (object == null)
+ {
+ throw new NullPointerException("Argument must not be null");
+ }
+ Class clazz = object.getClass();
+ if (clazz == Byte.class)
+ {
+ writeByte((Byte) object);
+ }
+ else if (clazz == Boolean.class)
+ {
+ writeBoolean((Boolean) object);
+ }
+ else if (clazz == byte[].class)
+ {
+ writeBytes((byte[]) object);
+ }
+ else if (clazz == Short.class)
+ {
+ writeShort((Short) object);
+ }
+ else if (clazz == Character.class)
+ {
+ writeChar((Character) object);
+ }
+ else if (clazz == Integer.class)
+ {
+ writeInt((Integer) object);
+ }
+ else if (clazz == Long.class)
+ {
+ writeLong((Long) object);
+ }
+ else if (clazz == Float.class)
+ {
+ writeFloat((Float) object);
+ }
+ else if (clazz == Double.class)
+ {
+ writeDouble((Double) object);
+ }
+ else if (clazz == String.class)
+ {
+ writeUTF((String) object);
+ }
+ else
+ {
+ throw new MessageFormatException("Only primitives plus byte arrays and String are valid types");
+ }
+ }
+
+ public String toString()
+ {
+ return String.valueOf(System.identityHashCode(this));
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java
new file mode 100644
index 0000000000..fd2aae9feb
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.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.client.message;
+
+import javax.jms.JMSException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class JMSBytesMessageFactory extends AbstractJMSMessageFactory
+{
+ protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data,
+ AMQShortString exchange, AMQShortString routingKey,
+ ContentHeaderBody contentHeader) throws AMQException
+ {
+ return new JMSBytesMessage(deliveryTag, contentHeader, exchange, routingKey, data);
+ }
+
+ public AbstractJMSMessage createMessage() throws JMSException
+ {
+ return new JMSBytesMessage();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java
new file mode 100644
index 0000000000..39b4e1e27b
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java
@@ -0,0 +1,552 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.util.Enumeration;
+
+import javax.jms.JMSException;
+import javax.jms.MessageFormatException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQPInvalidClassException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+
+public final class JMSHeaderAdapter
+{
+ private final FieldTable _headers;
+
+ public JMSHeaderAdapter(FieldTable headers)
+ {
+ _headers = headers;
+ }
+
+
+ public FieldTable getHeaders()
+ {
+ return _headers;
+ }
+
+ public boolean getBoolean(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Boolean b = getHeaders().getBoolean(string);
+
+ if (b == null)
+ {
+ if (getHeaders().containsKey(string))
+ {
+ Object str = getHeaders().getObject(string);
+
+ if (str == null || !(str instanceof String))
+ {
+ throw new MessageFormatException("getBoolean can't use " + string + " item.");
+ }
+ else
+ {
+ return Boolean.valueOf((String) str);
+ }
+ }
+ else
+ {
+ b = Boolean.valueOf(null);
+ }
+ }
+
+ return b;
+ }
+
+ public boolean getBoolean(AMQShortString string) throws JMSException
+ {
+ checkPropertyName(string);
+ Boolean b = getHeaders().getBoolean(string);
+
+ if (b == null)
+ {
+ if (getHeaders().containsKey(string))
+ {
+ Object str = getHeaders().getObject(string);
+
+ if (str == null || !(str instanceof String))
+ {
+ throw new MessageFormatException("getBoolean can't use " + string + " item.");
+ }
+ else
+ {
+ return Boolean.valueOf((String) str);
+ }
+ }
+ else
+ {
+ b = Boolean.valueOf(null);
+ }
+ }
+
+ return b;
+ }
+
+ public char getCharacter(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Character c = getHeaders().getCharacter(string);
+
+ if (c == null)
+ {
+ if (getHeaders().isNullStringValue(string))
+ {
+ throw new NullPointerException("Cannot convert null char");
+ }
+ else
+ {
+ throw new MessageFormatException("getChar can't use " + string + " item.");
+ }
+ }
+ else
+ {
+ return (char) c;
+ }
+ }
+
+ public byte[] getBytes(String string) throws JMSException
+ {
+ return getBytes(new AMQShortString(string));
+ }
+
+ public byte[] getBytes(AMQShortString string) throws JMSException
+ {
+ checkPropertyName(string);
+
+ byte[] bs = getHeaders().getBytes(string);
+
+ if (bs == null)
+ {
+ throw new MessageFormatException("getBytes can't use " + string + " item.");
+ }
+ else
+ {
+ return bs;
+ }
+ }
+
+ public byte getByte(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Byte b = getHeaders().getByte(string);
+ if (b == null)
+ {
+ if (getHeaders().containsKey(string))
+ {
+ Object str = getHeaders().getObject(string);
+
+ if (str == null || !(str instanceof String))
+ {
+ throw new MessageFormatException("getByte can't use " + string + " item.");
+ }
+ else
+ {
+ return Byte.valueOf((String) str);
+ }
+ }
+ else
+ {
+ b = Byte.valueOf(null);
+ }
+ }
+
+ return b;
+ }
+
+ public short getShort(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Short s = getHeaders().getShort(string);
+
+ if (s == null)
+ {
+ s = Short.valueOf(getByte(string));
+ }
+
+ return s;
+ }
+
+ public int getInteger(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Integer i = getHeaders().getInteger(string);
+
+ if (i == null)
+ {
+ i = Integer.valueOf(getShort(string));
+ }
+
+ return i;
+ }
+
+ public long getLong(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Long l = getHeaders().getLong(string);
+
+ if (l == null)
+ {
+ l = Long.valueOf(getInteger(string));
+ }
+
+ return l;
+ }
+
+ public float getFloat(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Float f = getHeaders().getFloat(string);
+
+ if (f == null)
+ {
+ if (getHeaders().containsKey(string))
+ {
+ Object str = getHeaders().getObject(string);
+
+ if (str == null || !(str instanceof String))
+ {
+ throw new MessageFormatException("getFloat can't use " + string + " item.");
+ }
+ else
+ {
+ return Float.valueOf((String) str);
+ }
+ }
+ else
+ {
+ f = Float.valueOf(null);
+ }
+
+ }
+
+ return f;
+ }
+
+ public double getDouble(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ Double d = getHeaders().getDouble(string);
+
+ if (d == null)
+ {
+ d = Double.valueOf(getFloat(string));
+ }
+
+ return d;
+ }
+
+ public String getString(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ String s = getHeaders().getString(string);
+
+ if (s == null)
+ {
+ if (getHeaders().containsKey(string))
+ {
+ Object o = getHeaders().getObject(string);
+ if (o instanceof byte[])
+ {
+ throw new MessageFormatException("getObject couldn't find " + string + " item.");
+ }
+ else
+ {
+ if (o == null)
+ {
+ return null;
+ }
+ else
+ {
+ s = String.valueOf(o);
+ }
+ }
+ }//else return s // null;
+ }
+
+ return s;
+ }
+
+ public Object getObject(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ return getHeaders().getObject(string);
+ }
+
+ public void setBoolean(AMQShortString string, boolean b) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setBoolean(string, b);
+ }
+
+ public void setBoolean(String string, boolean b) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setBoolean(string, b);
+ }
+
+ public void setChar(String string, char c) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setChar(string, c);
+ }
+
+ public Object setBytes(AMQShortString string, byte[] bytes)
+ {
+ checkPropertyName(string);
+ return getHeaders().setBytes(string, bytes);
+ }
+
+ public Object setBytes(String string, byte[] bytes)
+ {
+ checkPropertyName(string);
+ return getHeaders().setBytes(string, bytes);
+ }
+
+ public Object setBytes(String string, byte[] bytes, int start, int length)
+ {
+ checkPropertyName(string);
+ return getHeaders().setBytes(string, bytes, start, length);
+ }
+
+ public void setByte(String string, byte b) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setByte(string, b);
+ }
+
+ public void setByte(AMQShortString string, byte b) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setByte(string, b);
+ }
+
+
+ public void setShort(String string, short i) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setShort(string, i);
+ }
+
+ public void setInteger(String string, int i) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setInteger(string, i);
+ }
+
+ public void setInteger(AMQShortString string, int i) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setInteger(string, i);
+ }
+
+ public void setLong(String string, long l) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setLong(string, l);
+ }
+
+ public void setFloat(String string, float v) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setFloat(string, v);
+ }
+
+ public void setDouble(String string, double v) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setDouble(string, v);
+ }
+
+ public void setString(String string, String string1) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setString(string, string1);
+ }
+
+ public void setString(AMQShortString string, String string1) throws JMSException
+ {
+ checkPropertyName(string);
+ getHeaders().setString(string, string1);
+ }
+
+ public void setObject(String string, Object object) throws JMSException
+ {
+ checkPropertyName(string);
+ try
+ {
+ getHeaders().setObject(string, object);
+ }
+ catch (AMQPInvalidClassException aice)
+ {
+ MessageFormatException mfe = new MessageFormatException("Only primatives are allowed object is:" + object.getClass());
+ mfe.setLinkedException(aice);
+ throw mfe;
+ }
+ }
+
+ public boolean itemExists(String string) throws JMSException
+ {
+ checkPropertyName(string);
+ return getHeaders().containsKey(string);
+ }
+
+ public Enumeration getPropertyNames()
+ {
+ return getHeaders().getPropertyNames();
+ }
+
+ public void clear()
+ {
+ getHeaders().clear();
+ }
+
+ public boolean propertyExists(AMQShortString propertyName)
+ {
+ checkPropertyName(propertyName);
+ return getHeaders().propertyExists(propertyName);
+ }
+
+ public boolean propertyExists(String propertyName)
+ {
+ checkPropertyName(propertyName);
+ return getHeaders().propertyExists(propertyName);
+ }
+
+ public Object put(Object key, Object value)
+ {
+ checkPropertyName(key.toString());
+ return getHeaders().setObject(key.toString(), value);
+ }
+
+ public Object remove(AMQShortString propertyName)
+ {
+ checkPropertyName(propertyName);
+ return getHeaders().remove(propertyName);
+ }
+
+ public Object remove(String propertyName)
+ {
+ checkPropertyName(propertyName);
+ return getHeaders().remove(propertyName);
+ }
+
+ public boolean isEmpty()
+ {
+ return getHeaders().isEmpty();
+ }
+
+ public void writeToBuffer(ByteBuffer data)
+ {
+ getHeaders().writeToBuffer(data);
+ }
+
+ public Enumeration getMapNames()
+ {
+ return getPropertyNames();
+ }
+
+ protected static void checkPropertyName(CharSequence propertyName)
+ {
+ if (propertyName == null)
+ {
+ throw new IllegalArgumentException("Property name must not be null");
+ }
+ else if (propertyName.length() == 0)
+ {
+ throw new IllegalArgumentException("Property name must not be the empty string");
+ }
+
+ checkIdentiferFormat(propertyName);
+ }
+
+ protected static void checkIdentiferFormat(CharSequence propertyName)
+ {
+// JMS requirements 3.5.1 Property Names
+// Identifiers:
+// - An identifier is an unlimited-length character sequence that must begin
+// with a Java identifier start character; all following characters must be Java
+// identifier part characters. An identifier start character is any character for
+// which the method Character.isJavaIdentifierStart returns true. This includes
+// '_' and '$'. An identifier part character is any character for which the
+// method Character.isJavaIdentifierPart returns true.
+// - Identifiers cannot be the names NULL, TRUE, or FALSE.
+// � Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or
+// ESCAPE.
+// � Identifiers are either header field references or property references. The
+// type of a property value in a message selector corresponds to the type
+// used to set the property. If a property that does not exist in a message is
+// referenced, its value is NULL. The semantics of evaluating NULL values
+// in a selector are described in Section 3.8.1.2, �Null Values.�
+// � The conversions that apply to the get methods for properties do not
+// apply when a property is used in a message selector expression. For
+// example, suppose you set a property as a string value, as in the
+// following:
+// myMessage.setStringProperty("NumberOfOrders", "2");
+// The following expression in a message selector would evaluate to false,
+// because a string cannot be used in an arithmetic expression:
+// "NumberOfOrders > 1"
+// � Identifiers are case sensitive.
+// � Message header field references are restricted to JMSDeliveryMode,
+// JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and
+// JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be
+// null and if so are treated as a NULL value.
+
+ if (Boolean.getBoolean("strict-jms"))
+ {
+ // JMS start character
+ if (!(Character.isJavaIdentifierStart(propertyName.charAt(0))))
+ {
+ throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid JMS identifier start character");
+ }
+
+ // JMS part character
+ int length = propertyName.length();
+ for (int c = 1; c < length; c++)
+ {
+ if (!(Character.isJavaIdentifierPart(propertyName.charAt(c))))
+ {
+ throw new IllegalArgumentException("Identifier '" + propertyName + "' contains an invalid JMS identifier character");
+ }
+ }
+
+ // JMS invalid names
+ if ((propertyName.equals("NULL")
+ || propertyName.equals("TRUE")
+ || propertyName.equals("FALSE")
+ || propertyName.equals("NOT")
+ || propertyName.equals("AND")
+ || propertyName.equals("OR")
+ || propertyName.equals("BETWEEN")
+ || propertyName.equals("LIKE")
+ || propertyName.equals("IN")
+ || propertyName.equals("IS")
+ || propertyName.equals("ESCAPE")))
+ {
+ throw new IllegalArgumentException("Identifier '" + propertyName + "' is not allowed in JMS");
+ }
+ }
+
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java
new file mode 100644
index 0000000000..a70acbabbe
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java
@@ -0,0 +1,507 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client.message;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.MessageFormatException;
+
+import java.nio.charset.CharacterCodingException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jms.MapMessage
+{
+ private static final Logger _logger = LoggerFactory.getLogger(JMSMapMessage.class);
+
+ public static final String MIME_TYPE = "jms/map-message";
+ private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE);
+
+ private Map<String, Object> _map = new HashMap<String, Object>();
+
+ public JMSMapMessage() throws JMSException
+ {
+ this(null);
+ }
+
+ JMSMapMessage(ByteBuffer data) throws JMSException
+ {
+ super(data); // this instantiates a content header
+ populateMapFromData();
+ }
+
+ JMSMapMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, AMQShortString routingKey,
+ ByteBuffer data) throws AMQException
+ {
+ super(messageNbr, contentHeader, exchange, routingKey, data);
+ try
+ {
+ populateMapFromData();
+ }
+ catch (JMSException je)
+ {
+ throw new AMQException("Error populating MapMessage from ByteBuffer", je);
+
+ }
+
+ }
+
+ public String toBodyString() throws JMSException
+ {
+ return _map.toString();
+ }
+
+ public AMQShortString getMimeTypeAsShortString()
+ {
+ return MIME_TYPE_SHORT_STRING;
+ }
+
+ public ByteBuffer getData()
+ {
+ // What if _data is null?
+ writeMapToData();
+
+ return super.getData();
+ }
+
+ @Override
+ public void clearBodyImpl() throws JMSException
+ {
+ super.clearBodyImpl();
+ _map.clear();
+ }
+
+ public boolean getBoolean(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (value instanceof Boolean)
+ {
+ return ((Boolean) value).booleanValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Boolean.valueOf((String) value);
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to boolean.");
+ }
+
+ }
+
+ public byte getByte(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (value instanceof Byte)
+ {
+ return ((Byte) value).byteValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Byte.valueOf((String) value).byteValue();
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to byte.");
+ }
+ }
+
+ public short getShort(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (value instanceof Short)
+ {
+ return ((Short) value).shortValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).shortValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Short.valueOf((String) value).shortValue();
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to short.");
+ }
+
+ }
+
+ public int getInt(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (value instanceof Integer)
+ {
+ return ((Integer) value).intValue();
+ }
+ else if (value instanceof Short)
+ {
+ return ((Short) value).intValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).intValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Integer.valueOf((String) value).intValue();
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to int.");
+ }
+
+ }
+
+ public long getLong(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (value instanceof Long)
+ {
+ return ((Long) value).longValue();
+ }
+ else if (value instanceof Integer)
+ {
+ return ((Integer) value).longValue();
+ }
+
+ if (value instanceof Short)
+ {
+ return ((Short) value).longValue();
+ }
+
+ if (value instanceof Byte)
+ {
+ return ((Byte) value).longValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Long.valueOf((String) value).longValue();
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to long.");
+ }
+
+ }
+
+ public char getChar(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (!_map.containsKey(propName))
+ {
+ throw new MessageFormatException("Property " + propName + " not present");
+ }
+ else if (value instanceof Character)
+ {
+ return ((Character) value).charValue();
+ }
+ else if (value == null)
+ {
+ throw new NullPointerException("Property " + propName + " has null value and therefore cannot "
+ + "be converted to char.");
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to boolan.");
+ }
+
+ }
+
+ public float getFloat(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (value instanceof Float)
+ {
+ return ((Float) value).floatValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Float.valueOf((String) value).floatValue();
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to float.");
+ }
+ }
+
+ public double getDouble(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (value instanceof Double)
+ {
+ return ((Double) value).doubleValue();
+ }
+ else if (value instanceof Float)
+ {
+ return ((Float) value).doubleValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Double.valueOf((String) value).doubleValue();
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to double.");
+ }
+ }
+
+ public String getString(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if ((value instanceof String) || (value == null))
+ {
+ return (String) value;
+ }
+ else if (value instanceof byte[])
+ {
+ throw new MessageFormatException("Property " + propName + " of type byte[] " + "cannot be converted to String.");
+ }
+ else
+ {
+ return value.toString();
+ }
+
+ }
+
+ public byte[] getBytes(String propName) throws JMSException
+ {
+ Object value = _map.get(propName);
+
+ if (!_map.containsKey(propName))
+ {
+ throw new MessageFormatException("Property " + propName + " not present");
+ }
+ else if ((value instanceof byte[]) || (value == null))
+ {
+ return (byte[]) value;
+ }
+ else
+ {
+ throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName()
+ + " cannot be converted to byte[].");
+ }
+ }
+
+ public Object getObject(String propName) throws JMSException
+ {
+ return _map.get(propName);
+ }
+
+ public Enumeration getMapNames() throws JMSException
+ {
+ return Collections.enumeration(_map.keySet());
+ }
+
+ public void setBoolean(String propName, boolean b) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, b);
+ }
+
+ public void setByte(String propName, byte b) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, b);
+ }
+
+ public void setShort(String propName, short i) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, i);
+ }
+
+ public void setChar(String propName, char c) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, c);
+ }
+
+ public void setInt(String propName, int i) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, i);
+ }
+
+ public void setLong(String propName, long l) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, l);
+ }
+
+ public void setFloat(String propName, float v) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, v);
+ }
+
+ public void setDouble(String propName, double v) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, v);
+ }
+
+ public void setString(String propName, String string1) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, string1);
+ }
+
+ public void setBytes(String propName, byte[] bytes) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ _map.put(propName, bytes);
+ }
+
+ public void setBytes(String propName, byte[] bytes, int offset, int length) throws JMSException
+ {
+ if ((offset == 0) && (length == bytes.length))
+ {
+ setBytes(propName, bytes);
+ }
+ else
+ {
+ byte[] newBytes = new byte[length];
+ System.arraycopy(bytes, offset, newBytes, 0, length);
+ setBytes(propName, newBytes);
+ }
+ }
+
+ public void setObject(String propName, Object value) throws JMSException
+ {
+ checkWritable();
+ checkPropertyName(propName);
+ if ((value instanceof Boolean) || (value instanceof Byte) || (value instanceof Short) || (value instanceof Integer)
+ || (value instanceof Long) || (value instanceof Character) || (value instanceof Float)
+ || (value instanceof Double) || (value instanceof String) || (value instanceof byte[]) || (value == null))
+ {
+ _map.put(propName, value);
+ }
+ else
+ {
+ throw new MessageFormatException("Cannot set property " + propName + " to value " + value + "of type "
+ + value.getClass().getName() + ".");
+ }
+ }
+
+ private void checkPropertyName(String propName)
+ {
+ if ((propName == null) || propName.equals(""))
+ {
+ throw new IllegalArgumentException("Property name cannot be null, or the empty String.");
+ }
+ }
+
+ public boolean itemExists(String propName) throws JMSException
+ {
+ return _map.containsKey(propName);
+ }
+
+ private void populateMapFromData() throws JMSException
+ {
+ if (_data != null)
+ {
+ _data.rewind();
+
+ final int entries = readIntImpl();
+ for (int i = 0; i < entries; i++)
+ {
+ String propName = readStringImpl();
+ Object value = readObject();
+ _map.put(propName, value);
+ }
+ }
+ else
+ {
+ _map.clear();
+ }
+ }
+
+ private void writeMapToData()
+ {
+ allocateInitialBuffer();
+ final int size = _map.size();
+ writeIntImpl(size);
+ for (Map.Entry<String, Object> entry : _map.entrySet())
+ {
+ try
+ {
+ writeStringImpl(entry.getKey());
+ }
+ catch (CharacterCodingException e)
+ {
+ throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey(), e);
+
+ }
+
+ try
+ {
+ writeObject(entry.getValue());
+ }
+ catch (JMSException e)
+ {
+ Object value = entry.getValue();
+ throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey() + " value : " + value
+ + " (type: " + value.getClass().getName() + ").", e);
+ }
+ }
+
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java
new file mode 100644
index 0000000000..a6b9bb29a4
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.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.client.message;
+
+import javax.jms.JMSException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class JMSMapMessageFactory extends AbstractJMSMessageFactory
+{
+ public AbstractJMSMessage createMessage() throws JMSException
+ {
+ return new JMSMapMessage();
+ }
+
+ protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data,
+ AMQShortString exchange, AMQShortString routingKey,
+ ContentHeaderBody contentHeader) throws AMQException
+ {
+ return new JMSMapMessage(deliveryTag, contentHeader, exchange, routingKey, data);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java
new file mode 100644
index 0000000000..caf8741280
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java
@@ -0,0 +1,197 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.MessageFormatException;
+import javax.jms.ObjectMessage;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessage
+{
+ public static final String MIME_TYPE = "application/java-object-stream";
+ private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE);
+
+ private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+ /**
+ * Creates empty, writable message for use by producers
+ */
+ public JMSObjectMessage()
+ {
+ this(null);
+ }
+
+ private JMSObjectMessage(ByteBuffer data)
+ {
+ super(data);
+ if (data == null)
+ {
+ _data = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
+ _data.setAutoExpand(true);
+ }
+
+ getContentHeaderProperties().setContentType(MIME_TYPE_SHORT_STRING);
+ }
+
+ /**
+ * Creates read only message for delivery to consumers
+ */
+ JMSObjectMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, AMQShortString routingKey,
+ ByteBuffer data) throws AMQException
+ {
+ super(messageNbr, (BasicContentHeaderProperties) contentHeader.properties, exchange, routingKey, data);
+ }
+
+ public void clearBodyImpl() throws JMSException
+ {
+ if (_data != null)
+ {
+ _data.release();
+ }
+
+ _data = null;
+
+ }
+
+ public String toBodyString() throws JMSException
+ {
+ return toString(_data);
+ }
+
+ public AMQShortString getMimeTypeAsShortString()
+ {
+ return MIME_TYPE_SHORT_STRING;
+ }
+
+ public void setObject(Serializable serializable) throws JMSException
+ {
+ checkWritable();
+
+ if (_data == null)
+ {
+ _data = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
+ _data.setAutoExpand(true);
+ }
+ else
+ {
+ _data.rewind();
+ }
+
+ try
+ {
+ ObjectOutputStream out = new ObjectOutputStream(_data.asOutputStream());
+ out.writeObject(serializable);
+ out.flush();
+ out.close();
+ }
+ catch (IOException e)
+ {
+ MessageFormatException mfe = new MessageFormatException("Message not serializable: " + e);
+ mfe.setLinkedException(e);
+ throw mfe;
+ }
+
+ }
+
+ public Serializable getObject() throws JMSException
+ {
+ ObjectInputStream in = null;
+ if (_data == null)
+ {
+ return null;
+ }
+
+ try
+ {
+ _data.rewind();
+ in = new ObjectInputStream(_data.asInputStream());
+
+ return (Serializable) in.readObject();
+ }
+ catch (IOException e)
+ {
+ MessageFormatException mfe = new MessageFormatException("Could not deserialize message: " + e);
+ mfe.setLinkedException(e);
+ throw mfe;
+ }
+ catch (ClassNotFoundException e)
+ {
+ MessageFormatException mfe = new MessageFormatException("Could not deserialize message: " + e);
+ mfe.setLinkedException(e);
+ throw mfe;
+ }
+ finally
+ {
+ _data.rewind();
+ close(in);
+ }
+ }
+
+ private static void close(InputStream in)
+ {
+ try
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (IOException ignore)
+ { }
+ }
+
+ private static String toString(ByteBuffer data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ int pos = data.position();
+ try
+ {
+ return data.getString(Charset.forName("UTF8").newDecoder());
+ }
+ catch (CharacterCodingException e)
+ {
+ return null;
+ }
+ finally
+ {
+ data.position(pos);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java
new file mode 100644
index 0000000000..57ac4fb006
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.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.client.message;
+
+import javax.jms.JMSException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class JMSObjectMessageFactory extends AbstractJMSMessageFactory
+{
+ protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data,
+ AMQShortString exchange, AMQShortString routingKey,
+ ContentHeaderBody contentHeader) throws AMQException
+ {
+ return new JMSObjectMessage(deliveryTag, contentHeader, exchange, routingKey, data);
+ }
+
+ public AbstractJMSMessage createMessage() throws JMSException
+ {
+ return new JMSObjectMessage();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java
new file mode 100644
index 0000000000..b4350c7a98
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java
@@ -0,0 +1,204 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import javax.jms.JMSException;
+import javax.jms.StreamMessage;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class JMSStreamMessage extends AbstractBytesTypedMessage implements StreamMessage
+{
+ public static final String MIME_TYPE="jms/stream-message";
+ private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE);
+
+
+ /**
+ * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read
+ * a byte array in multiple chunks, hence this is used to track how much is left to be read
+ */
+ private int _byteArrayRemaining = -1;
+
+ public JMSStreamMessage()
+ {
+ this(null);
+ }
+
+ /**
+ * Construct a stream message with existing data.
+ *
+ * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is
+ * set to auto expand
+ */
+ JMSStreamMessage(ByteBuffer data)
+ {
+ super(data); // this instanties a content header
+ }
+
+
+ JMSStreamMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange,
+ AMQShortString routingKey, ByteBuffer data) throws AMQException
+ {
+ super(messageNbr, contentHeader, exchange, routingKey, data);
+ }
+
+ public void reset()
+ {
+ super.reset();
+ _readableMessage = true;
+ }
+
+ public AMQShortString getMimeTypeAsShortString()
+ {
+ return MIME_TYPE_SHORT_STRING;
+ }
+
+
+
+ public boolean readBoolean() throws JMSException
+ {
+ return super.readBoolean();
+ }
+
+
+ public byte readByte() throws JMSException
+ {
+ return super.readByte();
+ }
+
+ public short readShort() throws JMSException
+ {
+ return super.readShort();
+ }
+
+ /**
+ * Note that this method reads a unicode character as two bytes from the stream
+ *
+ * @return the character read from the stream
+ * @throws JMSException
+ */
+ public char readChar() throws JMSException
+ {
+ return super.readChar();
+ }
+
+ public int readInt() throws JMSException
+ {
+ return super.readInt();
+ }
+
+ public long readLong() throws JMSException
+ {
+ return super.readLong();
+ }
+
+ public float readFloat() throws JMSException
+ {
+ return super.readFloat();
+ }
+
+ public double readDouble() throws JMSException
+ {
+ return super.readDouble();
+ }
+
+ public String readString() throws JMSException
+ {
+ return super.readString();
+ }
+
+ public int readBytes(byte[] bytes) throws JMSException
+ {
+ return super.readBytes(bytes);
+ }
+
+
+ public Object readObject() throws JMSException
+ {
+ return super.readObject();
+ }
+
+ public void writeBoolean(boolean b) throws JMSException
+ {
+ super.writeBoolean(b);
+ }
+
+ public void writeByte(byte b) throws JMSException
+ {
+ super.writeByte(b);
+ }
+
+ public void writeShort(short i) throws JMSException
+ {
+ super.writeShort(i);
+ }
+
+ public void writeChar(char c) throws JMSException
+ {
+ super.writeChar(c);
+ }
+
+ public void writeInt(int i) throws JMSException
+ {
+ super.writeInt(i);
+ }
+
+ public void writeLong(long l) throws JMSException
+ {
+ super.writeLong(l);
+ }
+
+ public void writeFloat(float v) throws JMSException
+ {
+ super.writeFloat(v);
+ }
+
+ public void writeDouble(double v) throws JMSException
+ {
+ super.writeDouble(v);
+ }
+
+ public void writeString(String string) throws JMSException
+ {
+ super.writeString(string);
+ }
+
+ public void writeBytes(byte[] bytes) throws JMSException
+ {
+ super.writeBytes(bytes);
+ }
+
+ public void writeBytes(byte[] bytes, int offset, int length) throws JMSException
+ {
+ super.writeBytes(bytes,offset,length);
+ }
+
+ public void writeObject(Object object) throws JMSException
+ {
+ super.writeObject(object);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java
new file mode 100644
index 0000000000..c34ee7175d
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.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.client.message;
+
+import javax.jms.JMSException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class JMSStreamMessageFactory extends AbstractJMSMessageFactory
+{
+ protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data,
+ AMQShortString exchange, AMQShortString routingKey,
+ ContentHeaderBody contentHeader) throws AMQException
+ {
+ return new JMSStreamMessage(deliveryTag, contentHeader, exchange, routingKey, data);
+ }
+
+ public AbstractJMSMessage createMessage() throws JMSException
+ {
+ return new JMSStreamMessage();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java
new file mode 100644
index 0000000000..87cc80f21d
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java
@@ -0,0 +1,201 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.CustomJMSXProperty;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+
+public class JMSTextMessage extends AbstractJMSMessage implements javax.jms.TextMessage
+{
+ private static final String MIME_TYPE = "text/plain";
+ private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE);
+
+
+ private String _decodedValue;
+
+ /**
+ * This constant represents the name of a property that is set when the message payload is null.
+ */
+ private static final AMQShortString PAYLOAD_NULL_PROPERTY = CustomJMSXProperty.JMS_AMQP_NULL.getShortStringName();
+ private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
+
+ public JMSTextMessage() throws JMSException
+ {
+ this(null, null);
+ }
+
+ JMSTextMessage(ByteBuffer data, String encoding) throws JMSException
+ {
+ super(data); // this instantiates a content header
+ getContentHeaderProperties().setContentType(MIME_TYPE_SHORT_STRING);
+ getContentHeaderProperties().setEncoding(encoding);
+ }
+
+ JMSTextMessage(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange,
+ AMQShortString routingKey, ByteBuffer data)
+ throws AMQException
+ {
+ super(deliveryTag, contentHeader, exchange, routingKey, data);
+ contentHeader.setContentType(MIME_TYPE_SHORT_STRING);
+ _data = data;
+ }
+
+ JMSTextMessage(ByteBuffer data) throws JMSException
+ {
+ this(data, null);
+ }
+
+ JMSTextMessage(String text) throws JMSException
+ {
+ super((ByteBuffer) null);
+ setText(text);
+ }
+
+ public void clearBodyImpl() throws JMSException
+ {
+ if (_data != null)
+ {
+ _data.release();
+ }
+ _data = null;
+ _decodedValue = null;
+ }
+
+ public String toBodyString() throws JMSException
+ {
+ return getText();
+ }
+
+ public void setData(ByteBuffer data)
+ {
+ _data = data;
+ }
+
+ public AMQShortString getMimeTypeAsShortString()
+ {
+ return MIME_TYPE_SHORT_STRING;
+ }
+
+ public void setText(String text) throws JMSException
+ {
+ checkWritable();
+
+ clearBody();
+ try
+ {
+ if (text != null)
+ {
+ _data = ByteBuffer.allocate(text.length());
+ _data.limit(text.length()) ;
+ //_data.sweep();
+ _data.setAutoExpand(true);
+ final String encoding = getContentHeaderProperties().getEncodingAsString();
+ if (encoding == null)
+ {
+ _data.put(text.getBytes(DEFAULT_CHARSET.name()));
+ }
+ else
+ {
+ _data.put(text.getBytes(encoding));
+ }
+ _changedData=true;
+ }
+ _decodedValue = text;
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ // should never occur
+ JMSException jmse = new JMSException("Unable to decode text data");
+ jmse.setLinkedException(e);
+ }
+ }
+
+ public String getText() throws JMSException
+ {
+ if (_data == null && _decodedValue == null)
+ {
+ return null;
+ }
+ else if (_decodedValue != null)
+ {
+ return _decodedValue;
+ }
+ else
+ {
+ _data.rewind();
+
+ if (propertyExists(PAYLOAD_NULL_PROPERTY) && getBooleanProperty(PAYLOAD_NULL_PROPERTY))
+ {
+ return null;
+ }
+ if (getContentHeaderProperties().getEncodingAsString() != null)
+ {
+ try
+ {
+ _decodedValue = _data.getString(Charset.forName(getContentHeaderProperties().getEncodingAsString()).newDecoder());
+ }
+ catch (CharacterCodingException e)
+ {
+ JMSException je = new JMSException("Could not decode string data: " + e);
+ je.setLinkedException(e);
+ throw je;
+ }
+ }
+ else
+ {
+ try
+ {
+ _decodedValue = _data.getString(DEFAULT_CHARSET.newDecoder());
+ }
+ catch (CharacterCodingException e)
+ {
+ JMSException je = new JMSException("Could not decode string data: " + e);
+ je.setLinkedException(e);
+ throw je;
+ }
+ }
+ return _decodedValue;
+ }
+ }
+
+ @Override
+ public void prepareForSending() throws JMSException
+ {
+ super.prepareForSending();
+ if (_data == null)
+ {
+ setBooleanProperty(PAYLOAD_NULL_PROPERTY, true);
+ }
+ else
+ {
+ removeProperty(PAYLOAD_NULL_PROPERTY);
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java
new file mode 100644
index 0000000000..c5942dbe2a
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import javax.jms.JMSException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class JMSTextMessageFactory extends AbstractJMSMessageFactory
+{
+
+ public AbstractJMSMessage createMessage() throws JMSException
+ {
+ return new JMSTextMessage();
+ }
+
+ protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data,
+ AMQShortString exchange, AMQShortString routingKey,
+ ContentHeaderBody contentHeader) throws AMQException
+ {
+ return new JMSTextMessage(deliveryTag, (BasicContentHeaderProperties) contentHeader.properties,
+ exchange, routingKey, data);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java
new file mode 100644
index 0000000000..f6b11c6f6c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.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.client.message;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.MessageEOFException;
+import javax.jms.ObjectMessage;
+import javax.jms.StreamMessage;
+import javax.jms.TextMessage;
+
+import java.util.Enumeration;
+
+public class MessageConverter
+{
+
+ /**
+ * Log4J logger
+ */
+ protected final Logger _logger = LoggerFactory.getLogger(getClass());
+
+ /**
+ * AbstractJMSMessage which will hold the converted message
+ */
+ private AbstractJMSMessage _newMessage;
+
+ public MessageConverter(AbstractJMSMessage message) throws JMSException
+ {
+ _newMessage = message;
+ }
+
+ public MessageConverter(BytesMessage message) throws JMSException
+ {
+ BytesMessage bytesMessage = (BytesMessage) message;
+ bytesMessage.reset();
+
+ JMSBytesMessage nativeMsg = new JMSBytesMessage();
+
+ byte[] buf = new byte[1024];
+
+ int len;
+
+ while ((len = bytesMessage.readBytes(buf)) != -1)
+ {
+ nativeMsg.writeBytes(buf, 0, len);
+ }
+
+ _newMessage = nativeMsg;
+ setMessageProperties(message);
+ }
+
+ public MessageConverter(MapMessage message) throws JMSException
+ {
+ MapMessage nativeMessage = new JMSMapMessage();
+
+ Enumeration mapNames = message.getMapNames();
+ while (mapNames.hasMoreElements())
+ {
+ String name = (String) mapNames.nextElement();
+ nativeMessage.setObject(name, message.getObject(name));
+ }
+
+ _newMessage = (AbstractJMSMessage) nativeMessage;
+ setMessageProperties(message);
+ }
+
+ public MessageConverter(ObjectMessage message) throws JMSException
+ {
+ ObjectMessage origMessage = (ObjectMessage) message;
+ ObjectMessage nativeMessage = new JMSObjectMessage();
+
+ nativeMessage.setObject(origMessage.getObject());
+
+ _newMessage = (AbstractJMSMessage) nativeMessage;
+ setMessageProperties(message);
+
+ }
+
+ public MessageConverter(TextMessage message) throws JMSException
+ {
+ TextMessage nativeMessage = new JMSTextMessage();
+
+ nativeMessage.setText(message.getText());
+
+ _newMessage = (AbstractJMSMessage) nativeMessage;
+ setMessageProperties(message);
+ }
+
+ public MessageConverter(StreamMessage message) throws JMSException
+ {
+ StreamMessage nativeMessage = new JMSStreamMessage();
+
+ try
+ {
+ message.reset();
+ while (true)
+ {
+ nativeMessage.writeObject(message.readObject());
+ }
+ }
+ catch (MessageEOFException e)
+ {
+ // we're at the end so don't mind the exception
+ }
+
+ _newMessage = (AbstractJMSMessage) nativeMessage;
+ setMessageProperties(message);
+ }
+
+ public MessageConverter(Message message) throws JMSException
+ {
+ // Send a message with just properties.
+ // Throwing away content
+ BytesMessage nativeMessage = new JMSBytesMessage();
+
+ _newMessage = (AbstractJMSMessage) nativeMessage;
+ setMessageProperties(message);
+ }
+
+ public AbstractJMSMessage getConvertedMessage()
+ {
+ return _newMessage;
+ }
+
+ /**
+ * Sets all message properties
+ */
+ protected void setMessageProperties(Message message) throws JMSException
+ {
+ setNonJMSProperties(message);
+ setJMSProperties(message);
+ }
+
+ /**
+ * Sets all non-JMS defined properties on converted message
+ */
+ protected void setNonJMSProperties(Message message) throws JMSException
+ {
+ Enumeration propertyNames = message.getPropertyNames();
+ while (propertyNames.hasMoreElements())
+ {
+ String propertyName = String.valueOf(propertyNames.nextElement());
+ // TODO: Shouldn't need to check for JMS properties here as don't think getPropertyNames() should return them
+ if (!propertyName.startsWith("JMSX_"))
+ {
+ Object value = message.getObjectProperty(propertyName);
+ _newMessage.setObjectProperty(propertyName, value);
+ }
+ }
+ }
+
+ /**
+ * Exposed JMS defined properties on converted message:
+ * JMSDestination - we don't set here
+ * JMSDeliveryMode - set
+ * JMSExpiration - we don't set here
+ * JMSPriority - we don't set here
+ * JMSMessageID - we don't set here
+ * JMSTimestamp - we don't set here
+ * JMSCorrelationID - set
+ * JMSReplyTo - set
+ * JMSType - set
+ * JMSRedlivered - we don't set here
+ */
+ protected void setJMSProperties(Message message) throws JMSException
+ {
+ _newMessage.setJMSDeliveryMode(message.getJMSDeliveryMode());
+
+ if (message.getJMSReplyTo() != null)
+ {
+ _newMessage.setJMSReplyTo(message.getJMSReplyTo());
+ }
+
+ _newMessage.setJMSType(message.getJMSType());
+
+ _newMessage.setJMSCorrelationID(message.getJMSCorrelationID());
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java
new file mode 100644
index 0000000000..0fe4af715d
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.util.List;
+
+import javax.jms.JMSException;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+
+public interface MessageFactory
+{
+ AbstractJMSMessage createMessage(long deliveryTag, boolean redelivered,
+ ContentHeaderBody contentHeader,
+ AMQShortString exchange, AMQShortString routingKey,
+ List bodies)
+ throws JMSException, AMQException;
+
+ AbstractJMSMessage createMessage() throws JMSException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java
new file mode 100644
index 0000000000..c2015f9e7c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java
@@ -0,0 +1,127 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.JMSException;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+public class MessageFactoryRegistry
+{
+ private final Map<String, MessageFactory> _mimeStringToFactoryMap = new HashMap<String, MessageFactory>();
+ private final Map<AMQShortString, MessageFactory> _mimeShortStringToFactoryMap =
+ new HashMap<AMQShortString, MessageFactory>();
+
+ /**
+ * Construct a new registry with the default message factories registered
+ * @return a message factory registry
+ */
+ public static MessageFactoryRegistry newDefaultRegistry()
+ {
+ MessageFactoryRegistry mf = new MessageFactoryRegistry();
+ mf.registerFactory(JMSMapMessage.MIME_TYPE, new JMSMapMessageFactory());
+ mf.registerFactory("text/plain", new JMSTextMessageFactory());
+ mf.registerFactory("text/xml", new JMSTextMessageFactory());
+ mf.registerFactory(JMSBytesMessage.MIME_TYPE, new JMSBytesMessageFactory());
+ mf.registerFactory(JMSObjectMessage.MIME_TYPE, new JMSObjectMessageFactory());
+ mf.registerFactory(JMSStreamMessage.MIME_TYPE, new JMSStreamMessageFactory());
+ mf.registerFactory(null, new JMSBytesMessageFactory());
+
+ return mf;
+ }
+
+ public void registerFactory(String mimeType, MessageFactory mf)
+ {
+ if (mf == null)
+ {
+ throw new IllegalArgumentException("Message factory must not be null");
+ }
+
+ _mimeStringToFactoryMap.put(mimeType, mf);
+ _mimeShortStringToFactoryMap.put(new AMQShortString(mimeType), mf);
+ }
+
+ public MessageFactory deregisterFactory(String mimeType)
+ {
+ _mimeShortStringToFactoryMap.remove(new AMQShortString(mimeType));
+
+ return _mimeStringToFactoryMap.remove(mimeType);
+ }
+
+ /**
+ * Create a message. This looks up the MIME type from the content header and instantiates the appropriate
+ * concrete message type.
+ * @param deliveryTag the AMQ message id
+ * @param redelivered true if redelivered
+ * @param contentHeader the content header that was received
+ * @param bodies a list of ContentBody instances
+ * @return the message.
+ * @throws AMQException
+ * @throws JMSException
+ */
+ public AbstractJMSMessage createMessage(long deliveryTag, boolean redelivered, AMQShortString exchange,
+ AMQShortString routingKey, ContentHeaderBody contentHeader, List bodies)
+ throws AMQException, JMSException
+ {
+ BasicContentHeaderProperties properties = (BasicContentHeaderProperties) contentHeader.properties;
+
+ // Get the message content type. This may be null for pure AMQP messages, but will always be set for JMS over
+ // AMQP. When the type is null, it can only be assumed that the message is a byte message.
+ AMQShortString contentTypeShortString = properties.getContentType();
+ contentTypeShortString = (contentTypeShortString == null) ? new AMQShortString(JMSBytesMessage.MIME_TYPE)
+ : contentTypeShortString;
+
+ MessageFactory mf = _mimeShortStringToFactoryMap.get(contentTypeShortString);
+ if (mf == null)
+ {
+ throw new AMQException("Unsupport MIME type of " + properties.getContentTypeAsString());
+ }
+ else
+ {
+ return mf.createMessage(deliveryTag, redelivered, contentHeader, exchange, routingKey, bodies);
+ }
+ }
+
+ public AbstractJMSMessage createMessage(String mimeType) throws AMQException, JMSException
+ {
+ if (mimeType == null)
+ {
+ throw new IllegalArgumentException("Mime type must not be null");
+ }
+
+ MessageFactory mf = _mimeStringToFactoryMap.get(mimeType);
+ if (mf == null)
+ {
+ throw new AMQException("Unsupport MIME type of " + mimeType);
+ }
+ else
+ {
+ return mf.createMessage();
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.java
new file mode 100644
index 0000000000..1f61a661d4
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * @todo Not used! Delete!
+ */
+public class UnexpectedBodyReceivedException extends AMQException
+{
+ public UnexpectedBodyReceivedException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+
+ public UnexpectedBodyReceivedException(String msg)
+ {
+ super(msg);
+ }
+
+ public UnexpectedBodyReceivedException(AMQConstant errorCode, String msg)
+ {
+ super(errorCode, msg);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java b/Final/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java
new file mode 100644
index 0000000000..5b199f2478
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.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.client.message;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.framing.BasicReturnBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+
+/**
+ * This class contains everything needed to process a JMS message. It assembles the deliver body, the content header and
+ * the content body/ies.
+ *
+ * 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
+{
+ private long _bytesReceived = 0;
+
+ 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
+ {
+
+ if (body.payload != null)
+ {
+ final long payloadSize = body.payload.remaining();
+
+ if (_bodies == null)
+ {
+ if (payloadSize == getContentHeader().bodySize)
+ {
+ _bodies = Collections.singletonList(body);
+ }
+ else
+ {
+ _bodies = new ArrayList<ContentBody>();
+ _bodies.add(body);
+ }
+
+ }
+ else
+ {
+ _bodies.add(body);
+ }
+ _bytesReceived += payloadSize;
+ }
+ }
+
+ public boolean isAllBodyDataReceived()
+ {
+ return _bytesReceived == getContentHeader().bodySize;
+ }
+
+ public BasicDeliverBody getDeliverBody()
+ {
+ return _deliverBody;
+ }
+
+ public BasicReturnBody getBounceBody()
+ {
+ return _bounceBody;
+ }
+
+
+ public int getChannelId()
+ {
+ return _channelId;
+ }
+
+
+ public ContentHeaderBody getContentHeader()
+ {
+ return _contentHeader;
+ }
+
+ public void setContentHeader(ContentHeaderBody contentHeader)
+ {
+ this._contentHeader = contentHeader;
+ }
+
+ public List<ContentBody> getBodies()
+ {
+ return _bodies;
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
new file mode 100644
index 0000000000..e7ff5afceb
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java
@@ -0,0 +1,732 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.protocol;
+
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.SSLFilter;
+import org.apache.mina.filter.codec.ProtocolCodecException;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+
+import org.apache.qpid.AMQConnectionClosedException;
+import org.apache.qpid.AMQDisconnectedException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQTimeoutException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.SSLConfiguration;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.client.failover.FailoverHandler;
+import org.apache.qpid.client.failover.FailoverState;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.listener.SpecificMethodFrameListener;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.framing.AMQBody;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.ConnectionCloseOkBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.HeartbeatBody;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQMethodListener;
+import org.apache.qpid.ssl.SSLContextFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * AMQProtocolHandler is the client side protocol handler for AMQP, it handles all protocol events received from the
+ * network by MINA. The primary purpose of AMQProtocolHandler is to translate the generic event model of MINA into the
+ * specific event model of AMQP, by revealing the type of the received events (from decoded data), and passing the
+ * event on to more specific handlers for the type. In this sense, it channels the richer event model of AMQP,
+ * expressed in terms of methods and so on, through the cruder, general purpose event model of MINA, expressed in
+ * terms of "message received" and so on.
+ *
+ * <p/>There is a 1:1 mapping between an AMQProtocolHandler and an {@link AMQConnection}. The connection class is
+ * exposed to the end user of the AMQP client API, and also implements the JMS Connection API, so provides the public
+ * API calls through which an individual connection can be manipulated. This protocol handler talks to the network
+ * through MINA, in a behind the scenes role; it is not an exposed part of the client API.
+ *
+ * <p/>There is a 1:many mapping between an AMQProtocolHandler and a set of {@link AMQSession}s. At the MINA level,
+ * there is one session per connection. At the AMQP level there can be many channels which are also called sessions in
+ * JMS parlance. The {@link AMQSession}s are managed through an {@link AMQProtocolSession} instance. The protocol
+ * session is similar to the MINA per-connection session, except that it can span the lifecycle of multiple MINA sessions
+ * in the event of failover. See below for more information about this.
+ *
+ * <p/>Mina provides a session container that can be used to store/retrieve arbitrary objects as String named
+ * attributes. A more convenient, type-safe, container for session data is provided in the form of
+ * {@link AMQProtocolSession}.
+ *
+ * <p/>A common way to use MINA is to have a single instance of the event handler, and for MINA to pass in its session
+ * object with every event, and for per-connection data to be held in the MINA session (perhaps using a type-safe wrapper
+ * as described above). This event handler is different, because dealing with failover complicates things. To the
+ * end client of an AMQConnection, a failed over connection is still handled through the same connection instance, but
+ * behind the scenes a new transport connection, and MINA session will have been created. The MINA session object cannot
+ * be used to track the state of the fail-over process, because it is destroyed and a new one is created, as the old
+ * connection is shutdown and a new one created. For this reason, an AMQProtocolHandler is created per AMQConnection
+ * and the protocol session data is held outside of the MINA IOSession.
+ *
+ * <p/>This handler is responsibile for setting up the filter chain to filter all events for this handler through.
+ * The filter chain is set up as a stack of event handers that perform the following functions (working upwards from
+ * the network traffic at the bottom), handing off incoming events to an asynchronous thread pool to do the work,
+ * optionally handling secure sockets encoding/decoding, encoding/decoding the AMQP format itself.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create the filter chain to filter this handlers events.
+ * <td> {@link ProtocolCodecFilter}, {@link SSLContextFactory}, {@link SSLFilter}, {@link ReadWriteThreadModel}.
+ *
+ * <tr><td> Maintain fail-over state.
+ * <tr><td>
+ * </table>
+ *
+ * @todo Explain the system property: amqj.shared_read_write_pool. How does putting the protocol codec filter before the
+ * async write filter make it a shared pool? The pooling filter uses the same thread pool for reading and writing
+ * anyway, see {@link org.apache.qpid.pool.PoolingFilter}, docs for comments. Will putting the protocol codec
+ * filter before it mean not doing the read/write asynchronously but in the main filter thread?
+ *
+ * @todo Use a single handler instance, by shifting everything to do with the 'protocol session' state, including
+ * failover state, into AMQProtocolSession, and tracking that from AMQConnection? The lifecycles of
+ * AMQProtocolSesssion and AMQConnection will be the same, so if there is high cohesion between them, they could
+ * be merged, although there is sense in keeping the session model seperate. Will clarify things by having data
+ * held per protocol handler, per protocol session, per network connection, per channel, in seperate classes, so
+ * that lifecycles of the fields match lifecycles of their containing objects.
+ */
+public class AMQProtocolHandler extends IoHandlerAdapter
+{
+ /** Used for debugging. */
+ private static final Logger _logger = LoggerFactory.getLogger(AMQProtocolHandler.class);
+
+ /**
+ * The connection that this protocol handler is associated with. There is a 1-1 mapping between connection
+ * instances and protocol handler instances.
+ */
+ private AMQConnection _connection;
+
+ /** Our wrapper for a protocol session that provides access to session values in a typesafe manner. */
+ private volatile AMQProtocolSession _protocolSession;
+
+ /** Holds the state of the protocol session. */
+ private AMQStateManager _stateManager = new AMQStateManager();
+
+ /** Holds the method listeners, */
+ private final CopyOnWriteArraySet _frameListeners = new CopyOnWriteArraySet();
+
+ /**
+ * We create the failover handler when the session is created since it needs a reference to the IoSession in order
+ * to be able to send errors during failover back to the client application. The session won't be available in the
+ * case where we failing over due to a Connection.Redirect message from the broker.
+ */
+ private FailoverHandler _failoverHandler;
+
+ /**
+ * This flag is used to track whether failover is being attempted. It is used to prevent the application constantly
+ * attempting failover where it is failing.
+ */
+ private FailoverState _failoverState = FailoverState.NOT_STARTED;
+
+ /** Used to provide a condition to wait upon for operations that are required to wait for failover to complete. */
+ private CountDownLatch _failoverLatch;
+
+ /** Defines the default timeout to use for synchronous protocol commands. */
+ private final long DEFAULT_SYNC_TIMEOUT = 1000 * 30;
+
+ /**
+ * Creates a new protocol handler, associated with the specified client connection instance.
+ *
+ * @param con The client connection that this is the event handler for.
+ */
+ public AMQProtocolHandler(AMQConnection con)
+ {
+ _connection = con;
+ }
+
+ /**
+ * Invoked by MINA when a MINA session for a new connection is created. This method sets up the filter chain on the
+ * session, which filters the events handled by this handler. The filter chain consists of, handing off events
+ * to an asynchronous thread pool, optionally encoding/decoding ssl, encoding/decoding AMQP.
+ *
+ * @param session The MINA session.
+ *
+ * @throws Exception Any underlying exceptions are allowed to fall through to MINA.
+ */
+ public void sessionCreated(IoSession session) throws Exception
+ {
+ _logger.debug("Protocol session created for session " + System.identityHashCode(session));
+ _failoverHandler = new FailoverHandler(this, session);
+
+ final ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false));
+
+ if (Boolean.getBoolean("amqj.shared_read_write_pool"))
+ {
+ session.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf);
+ }
+ else
+ {
+ session.getFilterChain().addLast("protocolFilter", pcf);
+ }
+ // we only add the SSL filter where we have an SSL connection
+ if (_connection.getSSLConfiguration() != null)
+ {
+ SSLConfiguration sslConfig = _connection.getSSLConfiguration();
+ SSLContextFactory sslFactory =
+ new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType());
+ SSLFilter sslFilter = new SSLFilter(sslFactory.buildClientContext());
+ sslFilter.setUseClientMode(true);
+ session.getFilterChain().addBefore("protocolFilter", "ssl", sslFilter);
+ }
+
+ try
+ {
+ ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance();
+ threadModel.getAsynchronousReadFilter().createNewJobForSession(session);
+ threadModel.getAsynchronousWriteFilter().createNewJobForSession(session);
+ }
+ catch (RuntimeException e)
+ {
+ e.printStackTrace();
+ }
+
+ _protocolSession = new AMQProtocolSession(this, session, _connection, getStateManager());
+ _protocolSession.init();
+ }
+
+ /**
+ * Called when the network connection is closed. This can happen, either because the client explicitly requested
+ * that the connection be closed, in which case nothing is done, or because the connection died. In the case
+ * where the connection died, an attempt to failover automatically to a new connection may be started. The failover
+ * process will be started, provided that it is the clients policy to allow failover, and provided that a failover
+ * has not already been started or failed.
+ *
+ * <p/>It is important to note that when the connection dies this method may be called or {@link #exceptionCaught}
+ * may be called first followed by this method. This depends on whether the client was trying to send data at the
+ * time of the failure.
+ *
+ * @param session The MINA session.
+ *
+ * @todo Clarify: presumably exceptionCaught is called when the client is sending during a connection failure and
+ * not otherwise? The above comment doesn't make that clear.
+ */
+ public void sessionClosed(IoSession session)
+ {
+ if (_connection.isClosed())
+ {
+ _logger.debug("Session closed called by client");
+ }
+ else
+ {
+ _logger.debug("Session closed called with failover state currently " + _failoverState);
+
+ // reconnetablility was introduced here so as not to disturb the client as they have made their intentions
+ // known through the policy settings.
+
+ if ((_failoverState != FailoverState.IN_PROGRESS) && _connection.failoverAllowed())
+ {
+ _logger.debug("FAILOVER STARTING");
+ if (_failoverState == FailoverState.NOT_STARTED)
+ {
+ _failoverState = FailoverState.IN_PROGRESS;
+ startFailoverThread();
+ }
+ else
+ {
+ _logger.debug("Not starting failover as state currently " + _failoverState);
+ }
+ }
+ else
+ {
+ _logger.debug("Failover not allowed by policy."); // or already in progress?
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(_connection.getFailoverPolicy().toString());
+ }
+
+ if (_failoverState != FailoverState.IN_PROGRESS)
+ {
+ _logger.debug("sessionClose() not allowed to failover");
+ _connection.exceptionReceived(new AMQDisconnectedException(
+ "Server closed connection and reconnection " + "not permitted."));
+ }
+ else
+ {
+ _logger.debug("sessionClose() failover in progress");
+ }
+ }
+ }
+
+ _logger.debug("Protocol Session [" + this + "] closed");
+ }
+
+ /** See {@link FailoverHandler} to see rationale for separate thread. */
+ private void startFailoverThread()
+ {
+ Thread failoverThread = new Thread(_failoverHandler);
+ failoverThread.setName("Failover");
+ // Do not inherit daemon-ness from current thread as this can be a daemon
+ // thread such as a AnonymousIoService thread.
+ failoverThread.setDaemon(false);
+ failoverThread.start();
+ }
+
+ public void sessionIdle(IoSession session, IdleStatus status) throws Exception
+ {
+ _logger.debug("Protocol Session [" + this + ":" + session + "] idle: " + status);
+ if (IdleStatus.WRITER_IDLE.equals(status))
+ {
+ // write heartbeat frame:
+ _logger.debug("Sent heartbeat");
+ session.write(HeartbeatBody.FRAME);
+ HeartbeatDiagnostics.sent();
+ }
+ else if (IdleStatus.READER_IDLE.equals(status))
+ {
+ // failover:
+ HeartbeatDiagnostics.timeout();
+ _logger.warn("Timed out while waiting for heartbeat from peer.");
+ session.close();
+ }
+ }
+
+ /**
+ * Invoked when any exception is thrown by a user IoHandler implementation or by MINA. If the cause is an
+ * IOException, MINA will close the connection automatically.
+ *
+ * @param session The MINA session.
+ * @param cause The exception that triggered this event.
+ */
+ public void exceptionCaught(IoSession session, Throwable cause)
+ {
+ if (_failoverState == FailoverState.NOT_STARTED)
+ {
+ // if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException)))
+ if (cause instanceof AMQConnectionClosedException)
+ {
+ _logger.info("Exception caught therefore going to attempt failover: " + cause, cause);
+ // this will attemp failover
+
+ sessionClosed(session);
+ }
+ else
+ {
+
+ if (cause instanceof ProtocolCodecException)
+ {
+ _logger.info("Protocol Exception caught NOT going to attempt failover as " +
+ "cause isn't AMQConnectionClosedException: " + cause, cause);
+
+ AMQException amqe = new AMQException("Protocol handler error: " + cause, cause);
+ propagateExceptionToWaiters(amqe);
+ _connection.exceptionReceived(cause);
+ }
+
+ }
+
+ // FIXME Need to correctly handle other exceptions. Things like ...
+ // if (cause instanceof AMQChannelClosedException)
+ // which will cause the JMSSession to end due to a channel close and so that Session needs
+ // to be removed from the map so we can correctly still call close without an exception when trying to close
+ // the server closed session. See also CloseChannelMethodHandler as the sessionClose is never called on exception
+ }
+ // we reach this point if failover was attempted and failed therefore we need to let the calling app
+ // know since we cannot recover the situation
+ else if (_failoverState == FailoverState.FAILED)
+ {
+ _logger.error("Exception caught by protocol handler: " + cause, cause);
+
+ // we notify the state manager of the error in case we have any clients waiting on a state
+ // change. Those "waiters" will be interrupted and can handle the exception
+ AMQException amqe = new AMQException("Protocol handler error: " + cause, cause);
+ propagateExceptionToWaiters(amqe);
+ _connection.exceptionReceived(cause);
+ }
+ }
+
+ /**
+ * There are two cases where we have other threads potentially blocking for events to be handled by this class.
+ * These are for the state manager (waiting for a state change) or a frame listener (waiting for a particular type
+ * of frame to arrive). When an error occurs we need to notify these waiters so that they can react appropriately.
+ *
+ * @param e the exception to propagate
+ */
+ public void propagateExceptionToWaiters(Exception e)
+ {
+ getStateManager().error(e);
+ if (!_frameListeners.isEmpty())
+ {
+ final Iterator it = _frameListeners.iterator();
+ while (it.hasNext())
+ {
+ final AMQMethodListener ml = (AMQMethodListener) it.next();
+ ml.error(e);
+ }
+ }
+ }
+
+ private static int _messageReceivedCount;
+
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ final boolean debug = _logger.isDebugEnabled();
+ final long msgNumber = ++_messageReceivedCount;
+
+ if (debug && ((msgNumber % 1000) == 0))
+ {
+ _logger.debug("Received " + _messageReceivedCount + " protocol messages");
+ }
+
+ AMQFrame frame = (AMQFrame) message;
+
+ final AMQBody bodyFrame = frame.getBodyFrame();
+
+ 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:
+
+ }
+
+ _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes());
+ }
+
+ private static int _messagesOut;
+
+ public void messageSent(IoSession session, Object message) throws Exception
+ {
+ final long sentMessages = _messagesOut++;
+
+ final boolean debug = _logger.isDebugEnabled();
+
+ if (debug && ((sentMessages % 1000) == 0))
+ {
+ _logger.debug("Sent " + _messagesOut + " protocol messages");
+ }
+
+ _connection.bytesSent(session.getWrittenBytes());
+ if (debug)
+ {
+ _logger.debug("Sent frame " + message);
+ }
+ }
+
+ /*
+ public void addFrameListener(AMQMethodListener listener)
+ {
+ _frameListeners.add(listener);
+ }
+
+ public void removeFrameListener(AMQMethodListener listener)
+ {
+ _frameListeners.remove(listener);
+ }
+ */
+ public void attainState(AMQState s) throws AMQException
+ {
+ getStateManager().attainState(s);
+ }
+
+ /**
+ * Convenience method that writes a frame to the protocol session. Equivalent to calling
+ * getProtocolSession().write().
+ *
+ * @param frame the frame to write
+ */
+ public void writeFrame(AMQDataBlock frame)
+ {
+ _protocolSession.writeFrame(frame);
+ }
+
+ public void writeFrame(AMQDataBlock frame, boolean wait)
+ {
+ _protocolSession.writeFrame(frame, wait);
+ }
+
+ /**
+ * Convenience method that writes a frame to the protocol session and waits for a particular response. Equivalent to
+ * calling getProtocolSession().write() then waiting for the response.
+ *
+ * @param frame
+ * @param listener the blocking listener. Note the calling thread will block.
+ */
+ public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, BlockingMethodFrameListener listener)
+ throws AMQException, FailoverException
+ {
+ return writeCommandFrameAndWaitForReply(frame, listener, DEFAULT_SYNC_TIMEOUT);
+ }
+
+ /**
+ * Convenience method that writes a frame to the protocol session and waits for a particular response. Equivalent to
+ * calling getProtocolSession().write() then waiting for the response.
+ *
+ * @param frame
+ * @param listener the blocking listener. Note the calling thread will block.
+ */
+ public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, BlockingMethodFrameListener listener,
+ long timeout) throws AMQException, FailoverException
+ {
+ try
+ {
+ _frameListeners.add(listener);
+ _protocolSession.writeFrame(frame);
+
+ AMQMethodEvent e = listener.blockForFrame(timeout);
+
+ return e;
+ // 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
+ _frameListeners.remove(listener);
+ }
+
+ }
+
+ /** More convenient method to write a frame and wait for it's response. */
+ public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass) throws AMQException, FailoverException
+ {
+ return syncWrite(frame, responseClass, DEFAULT_SYNC_TIMEOUT);
+ }
+
+ /** More convenient method to write a frame and wait for it's response. */
+ public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass, long timeout) throws AMQException, FailoverException
+ {
+ return writeCommandFrameAndWaitForReply(frame, new SpecificMethodFrameListener(frame.getChannel(), responseClass),
+ timeout);
+ }
+
+ public void closeSession(AMQSession session) throws AMQException
+ {
+ _protocolSession.closeSession(session);
+ }
+
+ /**
+ * Closes the connection.
+ *
+ * <p/>If a failover exception occurs whilst closing the connection it is ignored, as the connection is closed
+ * anyway.
+ *
+ * @param timeout The timeout to wait for an acknowledgement to the close request.
+ *
+ * @throws AMQException If the close fails for any reason.
+ */
+ public void closeConnection(long timeout) throws AMQException
+ {
+ getStateManager().changeState(AMQState.CONNECTION_CLOSING);
+
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Connect this to the session version obtained from ProtocolInitiation for this session.
+ // Be aware of possible changes to parameter order as versions change.
+ final AMQFrame frame =
+ ConnectionCloseBody.createAMQFrame(0, _protocolSession.getProtocolMajorVersion(),
+ _protocolSession.getProtocolMinorVersion(), // AMQP version (major, minor)
+ 0, // classId
+ 0, // methodId
+ AMQConstant.REPLY_SUCCESS.getCode(), // replyCode
+ new AMQShortString("JMS client is closing the connection.")); // replyText
+
+ try
+ {
+ syncWrite(frame, ConnectionCloseOkBody.class, timeout);
+ _protocolSession.closeProtocolSession();
+ }
+ catch (AMQTimeoutException e)
+ {
+ _protocolSession.closeProtocolSession(false);
+ }
+ catch (FailoverException e)
+ {
+ _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway.");
+ }
+ }
+
+ /** @return the number of bytes read from this protocol session */
+ public long getReadBytes()
+ {
+ return _protocolSession.getIoSession().getReadBytes();
+ }
+
+ /** @return the number of bytes written to this protocol session */
+ public long getWrittenBytes()
+ {
+ return _protocolSession.getIoSession().getWrittenBytes();
+ }
+
+ public void failover(String host, int port)
+ {
+ _failoverHandler.setHost(host);
+ _failoverHandler.setPort(port);
+ // see javadoc for FailoverHandler to see rationale for separate thread
+ startFailoverThread();
+ }
+
+ public void blockUntilNotFailingOver() throws InterruptedException
+ {
+ if (_failoverLatch != null)
+ {
+ _failoverLatch.await();
+ }
+ }
+
+ public AMQShortString generateQueueName()
+ {
+ return _protocolSession.generateQueueName();
+ }
+
+ public CountDownLatch getFailoverLatch()
+ {
+ return _failoverLatch;
+ }
+
+ public void setFailoverLatch(CountDownLatch failoverLatch)
+ {
+ _failoverLatch = failoverLatch;
+ }
+
+ public AMQConnection getConnection()
+ {
+ return _connection;
+ }
+
+ public AMQStateManager getStateManager()
+ {
+ return _stateManager;
+ }
+
+ public void setStateManager(AMQStateManager stateManager)
+ {
+ _stateManager = stateManager;
+ if (_protocolSession != null)
+ {
+ _protocolSession.setStateManager(stateManager);
+ }
+ }
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+
+ FailoverState getFailoverState()
+ {
+ return _failoverState;
+ }
+
+ public void setFailoverState(FailoverState failoverState)
+ {
+ _failoverState = failoverState;
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return _protocolSession.getProtocolMajorVersion();
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return _protocolSession.getProtocolMinorVersion();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
new file mode 100644
index 0000000000..5fe6ffe6c6
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
@@ -0,0 +1,459 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.protocol;
+
+import org.apache.commons.lang.StringUtils;
+
+import org.apache.mina.common.CloseFuture;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.ConnectionTuneParameters;
+// import org.apache.qpid.client.message.UnexpectedBodyReceivedException;
+import org.apache.qpid.client.message.UnprocessedMessage;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.MainRegistry;
+import org.apache.qpid.framing.ProtocolInitiation;
+import org.apache.qpid.framing.ProtocolVersion;
+import org.apache.qpid.framing.VersionSpecificRegistry;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.security.sasl.SaslClient;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.UUID;
+
+/**
+ * Wrapper for protocol session that provides type-safe access to session attributes. <p/> The underlying protocol
+ * session is still available but clients should not use it to obtain session attributes.
+ */
+public class AMQProtocolSession implements AMQVersionAwareProtocolSession
+{
+ protected static final int LAST_WRITE_FUTURE_JOIN_TIMEOUT = 1000 * 60 * 2;
+
+ protected static final Logger _logger = LoggerFactory.getLogger(AMQProtocolSession.class);
+
+ public static final String PROTOCOL_INITIATION_RECEIVED = "ProtocolInitiatiionReceived";
+
+ protected static final String CONNECTION_TUNE_PARAMETERS = "ConnectionTuneParameters";
+
+ protected static final String AMQ_CONNECTION = "AMQConnection";
+
+ protected static final String SASL_CLIENT = "SASLClient";
+
+ protected final IoSession _minaProtocolSession;
+
+ private AMQStateManager _stateManager;
+
+ protected WriteFuture _lastWriteFuture;
+
+ /**
+ * The handler from which this session was created and which is used to handle protocol events. We send failover
+ * events to the handler.
+ */
+ 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();
+
+ /**
+ * 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();
+
+ /** Counter to ensure unique queue names */
+ protected int _queueId = 1;
+ protected final Object _queueIdLock = new Object();
+
+ private byte _protocolMinorVersion;
+ private byte _protocolMajorVersion;
+ private VersionSpecificRegistry _registry =
+ MainRegistry.getVersionSpecificRegistry(ProtocolVersion.getLatestSupportedVersion());
+
+ private final AMQConnection _connection;
+
+ public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection)
+ {
+ this(protocolHandler, protocolSession, connection, new AMQStateManager());
+
+ }
+
+ public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection,
+ AMQStateManager stateManager)
+ {
+ _protocolHandler = protocolHandler;
+ _minaProtocolSession = protocolSession;
+ _minaProtocolSession.setAttachment(this);
+ // properties of the connection are made available to the event handlers
+ _minaProtocolSession.setAttribute(AMQ_CONNECTION, connection);
+ // fixme - real value needed
+ _minaProtocolSession.setWriteTimeout(LAST_WRITE_FUTURE_JOIN_TIMEOUT);
+ _stateManager = stateManager;
+ _stateManager.setProtocolSession(this);
+ _connection = connection;
+
+ }
+
+ public void init()
+ {
+ // start the process of setting up the connection. This is the first place that
+ // data is written to the server.
+
+ _minaProtocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()));
+ }
+
+ public String getClientID()
+ {
+ try
+ {
+ return getAMQConnection().getClientID();
+ }
+ catch (JMSException e)
+ {
+ // we never throw a JMSException here
+ return null;
+ }
+ }
+
+ public void setClientID(String clientID) throws JMSException
+ {
+ getAMQConnection().setClientID(clientID);
+ }
+
+ public AMQStateManager getStateManager()
+ {
+ return _stateManager;
+ }
+
+ public void setStateManager(AMQStateManager stateManager)
+ {
+ _stateManager = stateManager;
+ }
+
+ public String getVirtualHost()
+ {
+ return getAMQConnection().getVirtualHost();
+ }
+
+ public String getUsername()
+ {
+ return getAMQConnection().getUsername();
+ }
+
+ public String getPassword()
+ {
+ return getAMQConnection().getPassword();
+ }
+
+ public IoSession getIoSession()
+ {
+ return _minaProtocolSession;
+ }
+
+ public SaslClient getSaslClient()
+ {
+ return (SaslClient) _minaProtocolSession.getAttribute(SASL_CLIENT);
+ }
+
+ /**
+ * Store the SASL client currently being used for the authentication handshake
+ *
+ * @param client if non-null, stores this in the session. if null clears any existing client being stored
+ */
+ public void setSaslClient(SaslClient client)
+ {
+ if (client == null)
+ {
+ _minaProtocolSession.removeAttribute(SASL_CLIENT);
+ }
+ else
+ {
+ _minaProtocolSession.setAttribute(SASL_CLIENT, client);
+ }
+ }
+
+ public ConnectionTuneParameters getConnectionTuneParameters()
+ {
+ return (ConnectionTuneParameters) _minaProtocolSession.getAttribute(CONNECTION_TUNE_PARAMETERS);
+ }
+
+ public void setConnectionTuneParameters(ConnectionTuneParameters params)
+ {
+ _minaProtocolSession.setAttribute(CONNECTION_TUNE_PARAMETERS, params);
+ AMQConnection con = getAMQConnection();
+ con.setMaximumChannelCount(params.getChannelMax());
+ con.setMaximumFrameSize(params.getFrameMax());
+ initHeartbeats((int) params.getHeartbeat());
+ }
+
+ /**
+ * Callback invoked from the BasicDeliverMethodHandler when a message has been received. This is invoked on the MINA
+ * dispatcher thread.
+ *
+ * @param message
+ *
+ * @throws AMQException if this was not expected
+ */
+ public void unprocessedMessageReceived(UnprocessedMessage message) throws AMQException
+ {
+ _channelId2UnprocessedMsgMap.put(message.getChannelId(), message);
+ }
+
+ public void messageContentHeaderReceived(int channelId, ContentHeaderBody contentHeader) throws AMQException
+ {
+ UnprocessedMessage msg = (UnprocessedMessage) _channelId2UnprocessedMsgMap.get(channelId);
+ if (msg == null)
+ {
+ throw new AMQException("Error: received content header without having received a BasicDeliver frame first");
+ }
+
+ if (msg.getContentHeader() != null)
+ {
+ throw new AMQException(
+ "Error: received duplicate content header or did not receive correct number of content body frames");
+ }
+
+ msg.setContentHeader(contentHeader);
+ if (contentHeader.bodySize == 0)
+ {
+ deliverMessageToAMQSession(channelId, msg);
+ }
+ }
+
+ public void messageContentBodyReceived(int channelId, ContentBody contentBody) throws AMQException
+ {
+ UnprocessedMessage msg = (UnprocessedMessage) _channelId2UnprocessedMsgMap.get(channelId);
+ if (msg == null)
+ {
+ throw new AMQException("Error: received content body without having received a JMSDeliver frame first");
+ }
+
+ if (msg.getContentHeader() == null)
+ {
+ _channelId2UnprocessedMsgMap.remove(channelId);
+ throw new AMQException("Error: received content body without having received a ContentHeader frame first");
+ }
+
+ /*try
+ {*/
+ msg.receiveBody(contentBody);
+ /*}
+ catch (UnexpectedBodyReceivedException e)
+ {
+ _channelId2UnprocessedMsgMap.remove(channelId);
+ throw e;
+ }*/
+
+ if (msg.isAllBodyDataReceived())
+ {
+ deliverMessageToAMQSession(channelId, msg);
+ }
+ }
+
+ /**
+ * Deliver a message to the appropriate session, removing the unprocessed message from our map
+ *
+ * @param channelId the channel id the message should be delivered to
+ * @param msg the message
+ */
+ private void deliverMessageToAMQSession(int channelId, UnprocessedMessage msg)
+ {
+ AMQSession session = getSession(channelId);
+ session.messageReceived(msg);
+ _channelId2UnprocessedMsgMap.remove(channelId);
+ }
+
+ protected AMQSession getSession(int channelId)
+ {
+ return _connection.getSession(channelId);
+ }
+
+ /**
+ * Convenience method that writes a frame to the protocol session. Equivalent to calling
+ * getProtocolSession().write().
+ *
+ * @param frame the frame to write
+ */
+ public void writeFrame(AMQDataBlock frame)
+ {
+ writeFrame(frame, false);
+ }
+
+ public void writeFrame(AMQDataBlock frame, boolean wait)
+ {
+ WriteFuture f = _minaProtocolSession.write(frame);
+ if (wait)
+ {
+ // fixme -- time out?
+ f.join();
+ }
+ else
+ {
+ _lastWriteFuture = f;
+ }
+ }
+
+ /**
+ * Starts the process of closing a session
+ *
+ * @param session the AMQSession being closed
+ */
+ public void closeSession(AMQSession session)
+ {
+ _logger.debug("closeSession called on protocol session for session " + session.getChannelId());
+ final int channelId = session.getChannelId();
+ if (channelId <= 0)
+ {
+ throw new IllegalArgumentException("Attempt to close a channel with id < 0");
+ }
+ // we need to know when a channel is closing so that we can respond
+ // with a channel.close frame when we receive any other type of frame
+ // on that channel
+ _closingChannels.putIfAbsent(channelId, session);
+ }
+
+ /**
+ * Called from the ChannelClose handler when a channel close frame is received. This method decides whether this is
+ * a response or an initiation. The latter case causes the AMQSession to be closed and an exception to be thrown if
+ * appropriate.
+ *
+ * @param channelId the id of the channel (session)
+ *
+ * @return true if the client must respond to the server, i.e. if the server initiated the channel close, false if
+ * the channel close is just the server responding to the client's earlier request to close the channel.
+ */
+ public boolean channelClosed(int channelId, AMQConstant code, String text) throws AMQException
+ {
+
+ // if this is not a response to an earlier request to close the channel
+ if (_closingChannels.remove(channelId) == null)
+ {
+ final AMQSession session = getSession(channelId);
+ try
+ {
+ session.closed(new AMQException(code, text));
+ }
+ catch (JMSException e)
+ {
+ throw new AMQException("JMSException received while closing session", e);
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public AMQConnection getAMQConnection()
+ {
+ return (AMQConnection) _minaProtocolSession.getAttribute(AMQ_CONNECTION);
+ }
+
+ public void closeProtocolSession()
+ {
+ closeProtocolSession(true);
+ }
+
+ public void closeProtocolSession(boolean waitLast)
+ {
+ _logger.debug("Waiting for last write to join.");
+ if (waitLast && (_lastWriteFuture != null))
+ {
+ _lastWriteFuture.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT);
+ }
+
+ _logger.debug("Closing protocol session");
+ final CloseFuture future = _minaProtocolSession.close();
+ future.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT);
+ }
+
+ public void failover(String host, int port)
+ {
+ _protocolHandler.failover(host, port);
+ }
+
+ protected AMQShortString generateQueueName()
+ {
+ int id;
+
+ return new AMQShortString("tmp_" + UUID.randomUUID());
+ }
+
+ /** @param delay delay in seconds (not ms) */
+ void initHeartbeats(int delay)
+ {
+ if (delay > 0)
+ {
+ _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay);
+ _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, HeartbeatConfig.CONFIG.getTimeout(delay));
+ HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay));
+ }
+ }
+
+ public void confirmConsumerCancelled(int channelId, AMQShortString consumerTag)
+ {
+ final AMQSession session = getSession(channelId);
+
+ session.confirmConsumerCancelled(consumerTag);
+ }
+
+ public void setProtocolVersion(final byte versionMajor, final byte versionMinor)
+ {
+ _protocolMajorVersion = versionMajor;
+ _protocolMinorVersion = versionMinor;
+ _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor);
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return _protocolMinorVersion;
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return _protocolMajorVersion;
+ }
+
+ public VersionSpecificRegistry getRegistry()
+ {
+ return _registry;
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java
new file mode 100644
index 0000000000..1badbb601c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java
@@ -0,0 +1,311 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.protocol;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQTimeoutException;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQMethodListener;
+
+/**
+ * BlockingMethodFrameListener is a 'rendezvous' which acts as a {@link AMQMethodListener} that delegates handling of
+ * incoming methods to a method listener implemented as a sub-class of this and hands off the processed method or
+ * error to a consumer. The producer of the event does not have to wait for the consumer to take the event, so this
+ * differs from a 'rendezvous' in that sense.
+ *
+ * <p/>BlockingMethodFrameListeners are used to coordinate waiting for replies to method calls that expect a response.
+ * They are always used in a 'one-shot' manner, that is, to recieve just one response. Usually the caller has to register
+ * them as method listeners with an event dispatcher and remember to de-register them (in a finally block) once they
+ * have been completed.
+ *
+ * <p/>The {@link #processMethod} must return <tt>true</tt> on any incoming method that it handles. This indicates to
+ * this listeners that the method it is waiting for has arrived. Incoming methods are also filtered by channel prior to
+ * being passed to the {@link #processMethod} method, so responses are only received for a particular channel. The
+ * channel id must be passed to the constructor.
+ *
+ * <p/>Errors from the producer are rethrown to the consumer.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept notification of AMQP method events. <td> {@link AMQMethodEvent}
+ * <tr><td> Delegate handling of the method to another method listener. <td> {@link AMQMethodBody}
+ * <tr><td> Block until a method is handled by the delegated to handler.
+ * <tr><td> Propagate the most recent exception to the consumer.
+ * </table>
+ *
+ * @todo Might be neater if this method listener simply wrapped another that provided the method handling using a
+ * methodRecevied method. The processMethod takes an additional channelId, however none of the implementations
+ * seem to use it. So wrapping the listeners is possible.
+ *
+ * @todo What is to stop a blocking method listener, receiving a second method whilst it is registered as a listener,
+ * overwriting the first one before the caller of the block method has had a chance to examine it? If one-shot
+ * behaviour is to be intended it should be enforced, perhaps by always returning false once the blocked for
+ * method has been received.
+ *
+ * @todo Interuption is caught but not handled. This could be allowed to fall through. This might actually be usefull
+ * for fail-over where a thread is blocking when failure happens, it could be interrupted to abandon or retry
+ * when this happens. At the very least, restore the interrupted status flag.
+ *
+ * @todo If the retrotranslator can handle it, could use a SynchronousQueue to implement this rendezvous. Need to
+ * check that SynchronousQueue has a non-blocking put method available.
+ */
+public abstract class BlockingMethodFrameListener implements AMQMethodListener
+{
+ /** This flag is used to indicate that the blocked for method has been received. */
+ private volatile boolean _ready = false;
+
+ /** This flag is used to indicate that the received error has been processed. */
+ private volatile boolean _errorAck = false;
+
+ /** Used to protect the shared event and ready flag between the producer and consumer. */
+ private final ReentrantLock _lock = new ReentrantLock();
+
+ /**
+ * Used to signal that a method has been received
+ */
+ private final Condition _receivedCondition = _lock.newCondition();
+
+ /**
+ * Used to signal that a error has been processed
+ */
+ private final Condition _errorConditionAck = _lock.newCondition();
+
+ /** Used to hold the most recent exception that is passed to the {@link #error(Exception)} method. */
+ private volatile Exception _error;
+
+ /** Holds the channel id for the channel upon which this listener is waiting for a response. */
+ protected int _channelId;
+
+ /** Holds the incoming method. */
+ protected AMQMethodEvent _doneEvt = null;
+
+ /**
+ * Creates a new method listener, that filters incoming method to just those that match the specified channel id.
+ *
+ * @param channelId The channel id to filter incoming methods with.
+ */
+ public BlockingMethodFrameListener(int channelId)
+ {
+ _channelId = channelId;
+ }
+
+ /**
+ * Delegates any additional handling of the incoming methods to another handler.
+ *
+ * @param channelId The channel id of the incoming method.
+ * @param frame The method body.
+ *
+ * @return <tt>true</tt> if the method was handled, <tt>false</tt> otherwise.
+ */
+ public abstract boolean processMethod(int channelId, AMQMethodBody frame); // throws AMQException;
+
+ /**
+ * Informs this listener that an AMQP method has been received.
+ *
+ * @param evt The AMQP method.
+ *
+ * @return <tt>true</tt> if this listener has handled the method, <tt>false</tt> otherwise.
+ */
+ public boolean methodReceived(AMQMethodEvent evt) // throws AMQException
+ {
+ AMQMethodBody method = evt.getMethod();
+
+ /*try
+ {*/
+ boolean ready = (evt.getChannelId() == _channelId) && processMethod(evt.getChannelId(), method);
+
+ if (ready)
+ {
+ // we only update the flag from inside the synchronized block
+ // so that the blockForFrame method cannot "miss" an update - it
+ // will only ever read the flag from within the synchronized block
+ _lock.lock();
+ try
+ {
+ _doneEvt = evt;
+ _ready = ready;
+ _receivedCondition.signal();
+ }
+ finally
+ {
+ _lock.unlock();
+ }
+ }
+
+ return ready;
+
+ /*}
+ catch (AMQException e)
+ {
+ error(e);
+ // we rethrow the error here, and the code in the frame dispatcher will go round
+ // each listener informing them that an exception has been thrown
+ throw e;
+ }*/
+ }
+
+ /**
+ * Blocks until a method is received that is handled by the delegated to method listener, or the specified timeout
+ * has passed.
+ *
+ * @param timeout The timeout in milliseconds.
+ *
+ * @return The AMQP method that was received.
+ *
+ * @throws AMQException
+ * @throws FailoverException
+ */
+ public AMQMethodEvent blockForFrame(long timeout) throws AMQException, FailoverException
+ {
+ long nanoTimeout = TimeUnit.MILLISECONDS.toNanos(timeout);
+
+ _lock.lock();
+
+ try
+ {
+ while (!_ready)
+ {
+ try
+ {
+ if (timeout == -1)
+ {
+ _receivedCondition.await();
+ }
+ else
+ {
+ nanoTimeout = _receivedCondition.awaitNanos(nanoTimeout);
+
+ if (nanoTimeout <= 0 && !_ready && _error == null)
+ {
+ _error = new AMQTimeoutException("Server did not respond in a timely fashion");
+ _ready = true;
+ }
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // IGNORE -- //fixme this isn't ideal as being interrupted isn't equivellant to sucess
+ // if (!_ready && timeout != -1)
+ // {
+ // _error = new AMQException("Server did not respond timely");
+ // _ready = true;
+ // }
+ }
+ }
+
+
+ if (_error != null)
+ {
+ if (_error instanceof AMQException)
+ {
+ throw (AMQException) _error;
+ }
+ else if (_error instanceof FailoverException)
+ {
+ // This should ensure that FailoverException is not wrapped and can be caught.
+ throw (FailoverException) _error; // needed to expose FailoverException.
+ }
+ else
+ {
+ throw new AMQException("Woken up due to " + _error.getClass(), _error);
+ }
+ }
+
+ }
+ finally
+ {
+ _errorAck = true;
+ _errorConditionAck.signal();
+ _error = null;
+ _lock.unlock();
+ }
+
+ return _doneEvt;
+ }
+
+ /**
+ * This is a callback, called by the MINA dispatcher thread only. It is also called from within this
+ * class to avoid code repetition but again is only called by the MINA dispatcher thread.
+ *
+ * @param e
+ */
+ public void error(Exception e)
+ {
+ // set the error so that the thread that is blocking (against blockForFrame())
+ // can pick up the exception and rethrow to the caller
+
+
+ _lock.lock();
+
+ if (_error == null)
+ {
+ _error = e;
+ }
+ else
+ {
+ System.err.println("WARNING: new error arrived while old one not yet processed");
+ }
+
+ try
+ {
+ _ready = true;
+ _receivedCondition.signal();
+
+ while (!_errorAck)
+ {
+ try
+ {
+ _errorConditionAck.await();
+ }
+ catch (InterruptedException e1)
+ {
+ //
+ }
+ }
+ _errorAck = false;
+ }
+ finally
+ {
+ _lock.unlock();
+ }
+ }
+
+ public boolean equals(Object o)
+ {
+
+ if (o instanceof BlockingMethodFrameListener)
+ {
+ BlockingMethodFrameListener other = (BlockingMethodFrameListener) o;
+
+ return _channelId == other._channelId;
+ }
+
+ return false;
+ }
+
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java
new file mode 100644
index 0000000000..35ea44a331
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.protocol;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class HeartbeatConfig
+{
+ private static final Logger _logger = LoggerFactory.getLogger(HeartbeatConfig.class);
+ static final HeartbeatConfig CONFIG = new HeartbeatConfig();
+
+ /**
+ * The factor used to get the timeout from the delay between heartbeats.
+ */
+ private float timeoutFactor = 2;
+
+ HeartbeatConfig()
+ {
+ String property = System.getProperty("amqj.heartbeat.timeoutFactor");
+ if (property != null)
+ {
+ try
+ {
+ timeoutFactor = Float.parseFloat(property);
+ }
+ catch (NumberFormatException e)
+ {
+ _logger.warn("Invalid timeout factor (amqj.heartbeat.timeoutFactor): " + property);
+ }
+ }
+ }
+
+ float getTimeoutFactor()
+ {
+ return timeoutFactor;
+ }
+
+ int getTimeout(int writeDelay)
+ {
+ return (int) (timeoutFactor * writeDelay);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java
new file mode 100644
index 0000000000..d44faeab04
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java
@@ -0,0 +1,121 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.protocol;
+
+class HeartbeatDiagnostics
+{
+ private static final Diagnostics _impl = init();
+
+ private static Diagnostics init()
+ {
+ return Boolean.getBoolean("amqj.heartbeat.diagnostics") ? new On() : new Off();
+ }
+
+ static void sent()
+ {
+ _impl.sent();
+ }
+
+ static void timeout()
+ {
+ _impl.timeout();
+ }
+
+ static void received(boolean heartbeat)
+ {
+ _impl.received(heartbeat);
+ }
+
+ static void init(int delay, int timeout)
+ {
+ _impl.init(delay, timeout);
+ }
+
+ private static interface Diagnostics
+ {
+ void sent();
+ void timeout();
+ void received(boolean heartbeat);
+ void init(int delay, int timeout);
+ }
+
+ private static class On implements Diagnostics
+ {
+ private final String[] messages = new String[50];
+ private int i;
+
+ private void save(String msg)
+ {
+ messages[i++] = msg;
+ if(i >= messages.length){
+ i = 0;//i.e. a circular buffer
+ }
+ }
+
+ public void sent()
+ {
+ save(System.currentTimeMillis() + ": sent heartbeat");
+ }
+
+ public void timeout()
+ {
+ for(int i = 0; i < messages.length; i++)
+ {
+ if(messages[i] != null)
+ {
+ System.out.println(messages[i]);
+ }
+ }
+ System.out.println(System.currentTimeMillis() + ": timed out");
+ }
+
+ public void received(boolean heartbeat)
+ {
+ save(System.currentTimeMillis() + ": received " + (heartbeat ? "heartbeat" : "data"));
+ }
+
+ public void init(int delay, int timeout)
+ {
+ System.out.println(System.currentTimeMillis() + ": initialised delay=" + delay + ", timeout=" + timeout);
+ }
+ }
+
+ private static class Off implements Diagnostics
+ {
+ public void sent()
+ {
+
+ }
+ public void timeout()
+ {
+
+ }
+ public void received(boolean heartbeat)
+ {
+
+ }
+
+ public void init(int delay, int timeout)
+ {
+
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java
new file mode 100644
index 0000000000..93cc5e7ec3
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.protocol;
+
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A MINA filter that monitors the numbers of messages pending to be sent by MINA. It outputs a message
+ * when a threshold has been exceeded, and has a frequency configuration so that messages are not output
+ * too often.
+ *
+ */
+public class ProtocolBufferMonitorFilter extends IoFilterAdapter
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ProtocolBufferMonitorFilter.class);
+
+ public static long DEFAULT_FREQUENCY = 5000;
+
+ public static int DEFAULT_THRESHOLD = 3000;
+
+ private int _bufferedMessages = 0;
+
+ private int _threshold;
+
+ private long _lastMessageOutputTime;
+
+ private long _outputFrequencyInMillis;
+
+ public ProtocolBufferMonitorFilter()
+ {
+ _threshold = DEFAULT_THRESHOLD;
+ _outputFrequencyInMillis = DEFAULT_FREQUENCY;
+ }
+
+ public ProtocolBufferMonitorFilter(int threshold, long frequency)
+ {
+ _threshold = threshold;
+ _outputFrequencyInMillis = frequency;
+ }
+
+ public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception
+ {
+ _bufferedMessages++;
+ if (_bufferedMessages > _threshold)
+ {
+ long now = System.currentTimeMillis();
+ if ((now - _lastMessageOutputTime) > _outputFrequencyInMillis)
+ {
+ _logger.warn("Protocol message buffer exceeded threshold of " + _threshold + ". Current backlog: "
+ + _bufferedMessages);
+ _lastMessageOutputTime = now;
+ }
+ }
+
+ nextFilter.messageReceived(session, message);
+ }
+
+ public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception
+ {
+ _bufferedMessages--;
+ nextFilter.messageSent(session, message);
+ }
+
+ public int getBufferedMessages()
+ {
+ return _bufferedMessages;
+ }
+
+ public int getThreshold()
+ {
+ return _threshold;
+ }
+
+ public void setThreshold(int threshold)
+ {
+ _threshold = threshold;
+ }
+
+ public long getOutputFrequencyInMillis()
+ {
+ return _outputFrequencyInMillis;
+ }
+
+ public void setOutputFrequencyInMillis(long outputFrequencyInMillis)
+ {
+ _outputFrequencyInMillis = outputFrequencyInMillis;
+ }
+
+ public long getLastMessageOutputTime()
+ {
+ return _lastMessageOutputTime;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java
new file mode 100644
index 0000000000..fbca444208
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+
+public interface AMQCallbackHandler extends CallbackHandler
+{
+ void initialise(AMQProtocolSession protocolSession);
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java
new file mode 100644
index 0000000000..140cbdeb75
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java
@@ -0,0 +1,231 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security;
+
+import org.apache.qpid.util.FileUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * CallbackHandlerRegistry is a registry for call back handlers for user authentication and interaction during user
+ * authentication. It is capable of reading its configuration from a properties file containing call back handler
+ * implementing class names for different SASL mechanism names. Instantiating this registry also has the effect of
+ * configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}.
+ *
+ * <p/>The callback configuration should be specified in a properties file, refered to by the System property
+ * "amp.callbackhandler.properties". The format of the properties file is:
+ *
+ * <p/><pre>
+ * CallbackHanlder.mechanism=fully.qualified.class.name
+ * </pre>
+ *
+ * <p/>Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a
+ * class that implements org.apache.qpid.client.security.AMQCallbackHanlder and provides a call back handler for the
+ * specified mechanism.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Parse callback properties.
+ * <tr><td> Provide mapping from SASL mechanisms to callback implementations.
+ * </table>
+ */
+public class CallbackHandlerRegistry
+{
+ private static final Logger _logger = LoggerFactory.getLogger(CallbackHandlerRegistry.class);
+
+ /** The name of the system property that holds the name of the callback handler properties file. */
+ private static final String FILE_PROPERTY = "amq.callbackhandler.properties";
+
+ /** The default name of the callback handler properties resource. */
+ public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/CallbackHandlerRegistry.properties";
+
+ /** A static reference to the singleton instance of this registry. */
+ private static CallbackHandlerRegistry _instance = new CallbackHandlerRegistry();
+
+ /** Holds a map from SASL mechanism names to call back handlers. */
+ private Map<String, Class> _mechanismToHandlerClassMap = new HashMap<String, Class>();
+
+ /** Holds a space delimited list of mechanisms that callback handlers exist for. */
+ private String _mechanisms;
+
+ /**
+ * Gets the singleton instance of this registry.
+ *
+ * @return The singleton instance of this registry.
+ */
+ public static CallbackHandlerRegistry getInstance()
+ {
+ return _instance;
+ }
+
+ /**
+ * Gets the callback handler class for a given SASL mechanism name.
+ *
+ * @param mechanism The SASL mechanism name.
+ *
+ * @return The callback handler class for the mechanism, or null if none is configured for that mechanism.
+ */
+ public Class getCallbackHandlerClass(String mechanism)
+ {
+ return (Class) _mechanismToHandlerClassMap.get(mechanism);
+ }
+
+ /**
+ * Gets a space delimited list of supported SASL mechanisms.
+ *
+ * @return A space delimited list of supported SASL mechanisms.
+ */
+ public String getMechanisms()
+ {
+ return _mechanisms;
+ }
+
+ /**
+ * Creates the call back handler registry from its configuration resource or file. This also has the side effect
+ * of configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}.
+ */
+ private CallbackHandlerRegistry()
+ {
+ // Register any configured SASL client factories.
+ DynamicSaslRegistrar.registerSaslProviders();
+
+ String filename = System.getProperty(FILE_PROPERTY);
+ InputStream is =
+ FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME,
+ CallbackHandlerRegistry.class.getClassLoader());
+
+ try
+ {
+ Properties props = new Properties();
+ props.load(is);
+ parseProperties(props);
+ _logger.info("Callback handlers available for SASL mechanisms: " + _mechanisms);
+ }
+ catch (IOException e)
+ {
+ _logger.error("Error reading properties: " + e, e);
+ }
+ finally
+ {
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+
+ }
+ catch (IOException e)
+ {
+ _logger.error("Unable to close properties stream: " + e, e);
+ }
+ }
+ }
+ }
+
+ /*private InputStream openPropertiesInputStream(String filename)
+ {
+ boolean useDefault = true;
+ InputStream is = null;
+ if (filename != null)
+ {
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(new File(filename)));
+ useDefault = false;
+ }
+ catch (FileNotFoundException e)
+ {
+ _logger.error("Unable to read from file " + filename + ": " + e, e);
+ }
+ }
+
+ if (useDefault)
+ {
+ is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME);
+ }
+
+ return is;
+ }*/
+
+ /**
+ * Scans the specified properties as a mapping from IANA registered SASL mechanism to call back handler
+ * implementations, that provide the necessary call back handling for obtaining user log in credentials
+ * during authentication for the specified mechanism, and builds a map from mechanism names to handler
+ * classes.
+ *
+ * @param props
+ */
+ private void parseProperties(Properties props)
+ {
+ Enumeration e = props.propertyNames();
+ while (e.hasMoreElements())
+ {
+ String propertyName = (String) e.nextElement();
+ int period = propertyName.indexOf(".");
+ if (period < 0)
+ {
+ _logger.warn("Unable to parse property " + propertyName + " when configuring SASL providers");
+
+ continue;
+ }
+
+ String mechanism = propertyName.substring(period + 1);
+ String className = props.getProperty(propertyName);
+ Class clazz = null;
+ try
+ {
+ clazz = Class.forName(className);
+ if (!AMQCallbackHandler.class.isAssignableFrom(clazz))
+ {
+ _logger.warn("SASL provider " + clazz + " does not implement " + AMQCallbackHandler.class
+ + ". Skipping");
+
+ continue;
+ }
+
+ _mechanismToHandlerClassMap.put(mechanism, clazz);
+ if (_mechanisms == null)
+ {
+ _mechanisms = mechanism;
+ }
+ else
+ {
+ // one time cost
+ _mechanisms = _mechanisms + " " + mechanism;
+ }
+ }
+ catch (ClassNotFoundException ex)
+ {
+ _logger.warn("Unable to load class " + className + ". Skipping that SASL provider");
+
+ continue;
+ }
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties b/Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties
new file mode 100644
index 0000000000..89ee8337f8
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties
@@ -0,0 +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
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+CallbackHandler.CRAM-MD5-HASHED=org.apache.qpid.client.security.UsernameHashedPasswordCallbackHandler
+CallbackHandler.CRAM-MD5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
+CallbackHandler.PLAIN=org.apache.qpid.client.security.UsernamePasswordCallbackHandler
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java
new file mode 100644
index 0000000000..803b34b7fa
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java
@@ -0,0 +1,198 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security;
+
+import org.apache.qpid.util.FileUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.sasl.SaslClientFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+/**
+ * DynamicSaslRegistrar provides a collection of helper methods for reading a configuration file that contains a mapping
+ * from SASL mechanism names to implementing client factory class names and registering a security provider with the
+ * Java runtime system, that uses the configured client factory implementations.
+ *
+ * <p/>The sasl configuration should be specified in a properties file, refered to by the System property
+ * "amp.dynamicsaslregistrar.properties". The format of the properties file is:
+ *
+ * <p/><pre>
+ * mechanism=fully.qualified.class.name
+ * </pre>
+ *
+ * <p/>Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a class that
+ * implements javax.security.sasl.SaslClientFactory and provides the specified mechanism.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Parse SASL
+ * mechanism properties. <tr><td> Create and register security provider for SASL mechanisms. </table>
+ */
+public class DynamicSaslRegistrar
+{
+ private static final Logger _logger = LoggerFactory.getLogger(DynamicSaslRegistrar.class);
+
+ /** The name of the system property that holds the name of the SASL configuration properties. */
+ private static final String FILE_PROPERTY = "amq.dynamicsaslregistrar.properties";
+
+ /** The default name of the SASL properties file resource. */
+ public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/DynamicSaslRegistrar.properties";
+
+ /** Reads the properties file, and creates a dynamic security provider to register the SASL implementations with. */
+ public static void registerSaslProviders()
+ {
+ _logger.debug("public static void registerSaslProviders(): called");
+
+ // Open the SASL properties file, using the default name is one is not specified.
+ String filename = System.getProperty(FILE_PROPERTY);
+ InputStream is =
+ FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME,
+ DynamicSaslRegistrar.class.getClassLoader());
+
+ try
+ {
+ Properties props = new Properties();
+ props.load(is);
+
+ _logger.debug("props = " + props);
+
+ Map<String, Class<? extends SaslClientFactory>> factories = parseProperties(props);
+
+ if (factories.size() > 0)
+ {
+ Security.insertProviderAt(new JCAProvider(factories), 0);
+ _logger.debug("Dynamic SASL provider added as a security provider");
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.error("Error reading properties: " + e, e);
+ }
+ finally
+ {
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+
+ }
+ catch (IOException e)
+ {
+ _logger.error("Unable to close properties stream: " + e, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Either attempts to open the specified filename as an input stream, or uses the default SASL configuration
+ * resource.
+ *
+ * @param filename The name of the file to get the SASL properties from, null to use the default.
+ *
+ * @return An input stream to read the dynamic SASL configuration from, or null if one could not be opened.
+ */
+ /*private static InputStream openPropertiesInputStream(String filename)
+ {
+ InputStream is = null;
+
+ // Flag to indicate whether the default resource should be used. By default this is true, so that the default
+ // is used when opening the file fails.
+ boolean useDefault = true;
+
+ // Try to open the file if one was specified.
+ if (filename != null)
+ {
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(new File(filename)));
+
+ // Clear the default flag because the file was succesfully opened.
+ useDefault = false;
+ }
+ catch (FileNotFoundException e)
+ {
+ _logger.error("Unable to read from file " + filename + ": " + e, e);
+ }
+ }
+
+ // Load the default resource if a file was not specified, or if opening the file failed.
+ if (useDefault)
+ {
+ is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME);
+ }
+
+ return is;
+ }*/
+
+ /**
+ * Parses the specified properties as a mapping from IANA registered SASL mechanism names to implementing client
+ * factories. If the client factories cannot be instantiated or do not implement SaslClientFactory then the
+ * properties refering to them are ignored.
+ *
+ * @param props The properties to scan for Sasl client factory implementations.
+ *
+ * @return A map from SASL mechanism names to implementing client factory classes.
+ *
+ * @todo Why tree map here? Do really want mechanisms in alphabetical order? Seems more likely that the declared
+ * order of the mechanisms is intended to be preserved, so that they are registered in the declared order of
+ * preference. Consider LinkedHashMap instead.
+ */
+ private static Map<String, Class<? extends SaslClientFactory>> parseProperties(Properties props)
+ {
+ Enumeration e = props.propertyNames();
+
+ TreeMap<String, Class<? extends SaslClientFactory>> factoriesToRegister =
+ new TreeMap<String, Class<? extends SaslClientFactory>>();
+
+ while (e.hasMoreElements())
+ {
+ String mechanism = (String) e.nextElement();
+ String className = props.getProperty(mechanism);
+ try
+ {
+ Class<?> clazz = Class.forName(className);
+ if (!(SaslClientFactory.class.isAssignableFrom(clazz)))
+ {
+ _logger.error("Class " + clazz + " does not implement " + SaslClientFactory.class + " - skipping");
+
+ continue;
+ }
+
+ factoriesToRegister.put(mechanism, (Class<? extends SaslClientFactory>) clazz);
+ }
+ catch (Exception ex)
+ {
+ _logger.error("Error instantiating SaslClientFactory calss " + className + " - skipping");
+ }
+ }
+
+ return factoriesToRegister;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties
new file mode 100644
index 0000000000..1bff43142b
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory
+CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java
new file mode 100644
index 0000000000..5a2c5ac5c1
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.sasl.SaslClientFactory;
+
+import java.security.Provider;
+import java.util.Map;
+
+/**
+ * JCAProvider is a security provider for SASL client factories that is configured from a map of SASL mechanism names
+ * to client factories implementation class names. It is intended that the map of client factories can be read from a
+ * configuration file or other application configuration mechanism.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Register SASL mechanism implementations.
+ * </table>
+ */
+public class JCAProvider extends Provider
+{
+ private static final Logger log = LoggerFactory.getLogger(JCAProvider.class);
+
+ /**
+ * Creates the security provider with a map from SASL mechanisms to implementing factories.
+ *
+ * @param providerMap The map from SASL mechanims to implementing factory classes.
+ */
+ public JCAProvider(Map<String, Class<? extends SaslClientFactory>> providerMap)
+ {
+ super("AMQSASLProvider", 1.0, "A JCA provider that registers all "
+ + "AMQ SASL providers that want to be registered");
+ register(providerMap);
+ // Security.addProvider(this);
+ }
+
+ /**
+ * Registers client factory classes for a map of mechanism names to client factory classes.
+ *
+ * @param providerMap The map from SASL mechanims to implementing factory classes.
+ */
+ private void register(Map<String, Class<? extends SaslClientFactory>> providerMap)
+ {
+ for (Map.Entry<String, Class<? extends SaslClientFactory>> me : providerMap.entrySet())
+ {
+ put("SaslClientFactory." + me.getKey(), me.getValue().getName());
+ log.debug("Registered SASL Client factory for " + me.getKey() + " as " + me.getValue().getName());
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java
new file mode 100644
index 0000000000..66176dac3c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security;
+
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler
+{
+ private static final Logger _logger = LoggerFactory.getLogger(UsernameHashedPasswordCallbackHandler.class);
+
+ private AMQProtocolSession _protocolSession;
+
+ public void initialise(AMQProtocolSession protocolSession)
+ {
+ _protocolSession = protocolSession;
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ Callback cb = callbacks[i];
+ if (cb instanceof NameCallback)
+ {
+ ((NameCallback) cb).setName(_protocolSession.getUsername());
+ }
+ else if (cb instanceof PasswordCallback)
+ {
+ try
+ {
+ ((PasswordCallback) cb).setPassword(getHash(_protocolSession.getPassword()));
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ UnsupportedCallbackException uce = new UnsupportedCallbackException(cb);
+ uce.initCause(e);
+ throw uce;
+ }
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(cb);
+ }
+ }
+ }
+
+ private char[] getHash(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException
+ {
+
+ byte[] data = text.getBytes("utf-8");
+
+ MessageDigest md = MessageDigest.getInstance("MD5");
+
+ for (byte b : data)
+ {
+ md.update(b);
+ }
+
+ byte[] digest = md.digest();
+
+ char[] hash = new char[digest.length];
+
+ int index = 0;
+ for (byte b : digest)
+ {
+ hash[index++] = (char) b;
+ }
+
+ return hash;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java
new file mode 100644
index 0000000000..c50c62710f
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+
+public class UsernamePasswordCallbackHandler implements AMQCallbackHandler
+{
+ private AMQProtocolSession _protocolSession;
+
+ public void initialise(AMQProtocolSession protocolSession)
+ {
+ _protocolSession = protocolSession;
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ Callback cb = callbacks[i];
+ if (cb instanceof NameCallback)
+ {
+ ((NameCallback)cb).setName(_protocolSession.getUsername());
+ }
+ else if (cb instanceof PasswordCallback)
+ {
+ ((PasswordCallback)cb).setPassword(_protocolSession.getPassword().toCharArray());
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(cb);
+ }
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java
new file mode 100644
index 0000000000..f8a25c630c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security.amqplain;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+
+/**
+ * Implements the "AMQPlain" authentication protocol that uses FieldTables to send username and pwd.
+ *
+ */
+public class AmqPlainSaslClient implements SaslClient
+{
+ /**
+ * The name of this mechanism
+ */
+ public static final String MECHANISM = "AMQPLAIN";
+
+ private CallbackHandler _cbh;
+
+ public AmqPlainSaslClient(CallbackHandler cbh)
+ {
+ _cbh = cbh;
+ }
+
+ public String getMechanismName()
+ {
+ return "AMQPLAIN";
+ }
+
+ public boolean hasInitialResponse()
+ {
+ return true;
+ }
+
+ public byte[] evaluateChallenge(byte[] challenge) throws SaslException
+ {
+ // we do not care about the prompt or the default name
+ NameCallback nameCallback = new NameCallback("prompt", "defaultName");
+ PasswordCallback pwdCallback = new PasswordCallback("prompt", false);
+ Callback[] callbacks = new Callback[]{nameCallback, pwdCallback};
+ try
+ {
+ _cbh.handle(callbacks);
+ }
+ catch (Exception e)
+ {
+ throw new SaslException("Error handling SASL callbacks: " + e, e);
+ }
+ FieldTable table = FieldTableFactory.newFieldTable();
+ table.setString("LOGIN", nameCallback.getName());
+ table.setString("PASSWORD", new String(pwdCallback.getPassword()));
+ return table.getDataAsBytes();
+ }
+
+ public boolean isComplete()
+ {
+ return true;
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Not supported");
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Not supported");
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ public void dispose() throws SaslException
+ {
+ _cbh = null;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java
new file mode 100644
index 0000000000..30cc786890
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.security.amqplain;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslException;
+
+public class AmqPlainSaslClientFactory implements SaslClientFactory
+{
+ public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, Map props, CallbackHandler cbh) throws SaslException
+ {
+ for (int i = 0; i < mechanisms.length; i++)
+ {
+ if (mechanisms[i].equals(AmqPlainSaslClient.MECHANISM))
+ {
+ if (cbh == null)
+ {
+ throw new SaslException("CallbackHandler must not be null");
+ }
+ return new AmqPlainSaslClient(cbh);
+ }
+ }
+ return null;
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ else
+ {
+ return new String[]{AmqPlainSaslClient.MECHANISM};
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java b/Final/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java
new file mode 100644
index 0000000000..22bb1ac156
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client.security.crammd5hashed;
+
+import org.apache.qpid.client.security.amqplain.AmqPlainSaslClient;
+
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.Sasl;
+import javax.security.auth.callback.CallbackHandler;
+import java.util.Map;
+import java.security.Security;
+
+public class CRAMMD5HashedSaslClientFactory implements SaslClientFactory
+{
+ /** The name of this mechanism */
+ public static final String MECHANISM = "CRAM-MD5-HASHED";
+
+
+ public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, Map<String, ?> props, CallbackHandler cbh) throws SaslException
+ {
+ for (int i = 0; i < mechanisms.length; i++)
+ {
+ if (mechanisms[i].equals(MECHANISM))
+ {
+ if (cbh == null)
+ {
+ throw new SaslException("CallbackHandler must not be null");
+ }
+
+ String[] mechs = {"CRAM-MD5"};
+ return Sasl.createSaslClient(mechs, authorizationId, protocol, serverName, props, cbh);
+ }
+ }
+ return null;
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props != null)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ }
+
+ return new String[]{MECHANISM};
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java
new file mode 100644
index 0000000000..4996f59345
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+/**
+ * States used in the AMQ protocol. Used by the finite state machine to determine
+ * valid responses.
+ */
+public class AMQState
+{
+ private final int _id;
+
+ private final String _name;
+
+ private AMQState(int id, String name)
+ {
+ _id = id;
+ _name = name;
+ }
+
+ public String toString()
+ {
+ return "AMQState: id = " + _id + " name: " + _name;
+ }
+
+ public static final AMQState CONNECTION_NOT_STARTED = new AMQState(1, "CONNECTION_NOT_STARTED");
+
+ public static final AMQState CONNECTION_NOT_TUNED = new AMQState(2, "CONNECTION_NOT_TUNED");
+
+ public static final AMQState CONNECTION_NOT_OPENED = new AMQState(3, "CONNECTION_NOT_OPENED");
+
+ public static final AMQState CONNECTION_OPEN = new AMQState(4, "CONNECTION_OPEN");
+
+ public static final AMQState CONNECTION_CLOSING = new AMQState(5, "CONNECTION_CLOSING");
+
+ public static final AMQState CONNECTION_CLOSED = new AMQState(6, "CONNECTION_CLOSED");
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java
new file mode 100644
index 0000000000..edef54ccd6
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+/**
+ * An event that is fired when the protocol state has changed.
+ *
+ */
+public class AMQStateChangedEvent
+{
+ private final AMQState _oldState;
+
+ private final AMQState _newState;
+
+ public AMQStateChangedEvent(AMQState oldState, AMQState newState)
+ {
+ _oldState = oldState;
+ _newState = newState;
+ }
+
+ public AMQState getOldState()
+ {
+ return _oldState;
+ }
+
+ public AMQState getNewState()
+ {
+ return _newState;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java
new file mode 100644
index 0000000000..110471aad0
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+public interface AMQStateListener
+{
+ void stateChanged(AMQStateChangedEvent evt);
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
new file mode 100644
index 0000000000..227f23b540
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java
@@ -0,0 +1,276 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.handler.BasicCancelOkMethodHandler;
+import org.apache.qpid.client.handler.BasicDeliverMethodHandler;
+import org.apache.qpid.client.handler.BasicReturnMethodHandler;
+import org.apache.qpid.client.handler.ChannelCloseMethodHandler;
+import org.apache.qpid.client.handler.ChannelCloseOkMethodHandler;
+import org.apache.qpid.client.handler.ChannelFlowOkMethodHandler;
+import org.apache.qpid.client.handler.ConnectionCloseMethodHandler;
+import org.apache.qpid.client.handler.ConnectionOpenOkMethodHandler;
+import org.apache.qpid.client.handler.ConnectionSecureMethodHandler;
+import org.apache.qpid.client.handler.ConnectionStartMethodHandler;
+import org.apache.qpid.client.handler.ConnectionTuneMethodHandler;
+import org.apache.qpid.client.handler.ExchangeBoundOkMethodHandler;
+import org.apache.qpid.client.handler.QueueDeleteOkMethodHandler;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.BasicCancelOkBody;
+import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.framing.BasicReturnBody;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.framing.ChannelFlowOkBody;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.ConnectionOpenOkBody;
+import org.apache.qpid.framing.ConnectionSecureBody;
+import org.apache.qpid.framing.ConnectionStartBody;
+import org.apache.qpid.framing.ConnectionTuneBody;
+import org.apache.qpid.framing.ExchangeBoundOkBody;
+import org.apache.qpid.framing.QueueDeleteOkBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+import org.apache.qpid.protocol.AMQMethodListener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.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
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AMQStateManager.class);
+
+ private AMQProtocolSession _protocolSession;
+
+ /** The current state */
+ private AMQState _currentState;
+
+ /**
+ * Maps from an AMQState instance to a Map from Class to StateTransitionHandler. The class must be a subclass of
+ * AMQFrame.
+ */
+ protected final Map _state2HandlersMap = new HashMap();
+
+ private final CopyOnWriteArraySet _stateListeners = new CopyOnWriteArraySet();
+ private final Object _stateLock = new Object();
+ private static final long MAXIMUM_STATE_WAIT_TIME = 30000L;
+
+ public AMQStateManager()
+ {
+ this(null);
+ }
+
+ public AMQStateManager(AMQProtocolSession protocolSession)
+ {
+ this(AMQState.CONNECTION_NOT_STARTED, true, protocolSession);
+ }
+
+ protected AMQStateManager(AMQState state, boolean register, AMQProtocolSession protocolSession)
+ {
+ _protocolSession = protocolSession;
+ _currentState = state;
+ if (register)
+ {
+ registerListeners();
+ }
+ }
+
+ protected void registerListeners()
+ {
+ Map frame2handlerMap = new HashMap();
+
+ // we need to register a map for the null (i.e. all state) handlers otherwise you get
+ // a stack overflow in the handler searching code when you present it with a frame for which
+ // no handlers are registered
+ //
+ _state2HandlersMap.put(null, frame2handlerMap);
+
+ frame2handlerMap = new HashMap();
+ frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap();
+ frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap();
+ frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap);
+
+ //
+ // ConnectionOpen handlers
+ //
+ frame2handlerMap = new HashMap();
+ frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandler.getInstance());
+ frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance());
+ frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance());
+ frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance());
+ frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance());
+ frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance());
+ frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap);
+ }
+
+ public AMQState getCurrentState()
+ {
+ return _currentState;
+ }
+
+ public void changeState(AMQState newState) throws AMQException
+ {
+ _logger.debug("State changing to " + newState + " from old state " + _currentState);
+
+ synchronized (_stateLock)
+ {
+ _currentState = newState;
+ _stateLock.notifyAll();
+ }
+ }
+
+ public void error(Exception e)
+ {
+ _logger.debug("State manager receive error notification: " + e);
+ synchronized (_stateListeners)
+ {
+ final Iterator it = _stateListeners.iterator();
+ while (it.hasNext())
+ {
+ final StateListener l = (StateListener) it.next();
+ l.error(e);
+ }
+ }
+ }
+
+ public <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws AMQException
+ {
+ StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod());
+ if (handler != null)
+ {
+ handler.methodReceived(this, _protocolSession, evt);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState, AMQMethodBody frame)
+ // throws IllegalStateTransitionException
+ {
+ final Class clazz = frame.getClass();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Looking for state[" + currentState + "] transition handler for frame " + clazz);
+ }
+
+ final Map classToHandlerMap = (Map) _state2HandlersMap.get(currentState);
+
+ if (classToHandlerMap == null)
+ {
+ // if no specialised per state handler is registered look for a
+ // handler registered for "all" states
+ return findStateTransitionHandler(null, frame);
+ }
+
+ final StateAwareMethodListener handler = (StateAwareMethodListener) classToHandlerMap.get(clazz);
+ if (handler == null)
+ {
+ if (currentState == null)
+ {
+ _logger.debug("No state transition handler defined for receiving frame " + frame);
+
+ return null;
+ }
+ else
+ {
+ // if no specialised per state handler is registered look for a
+ // handler registered for "all" states
+ return findStateTransitionHandler(null, frame);
+ }
+ }
+ else
+ {
+ return handler;
+ }
+ }
+
+ public void attainState(final AMQState s) throws AMQException
+ {
+ synchronized (_stateLock)
+ {
+ final long waitUntilTime = System.currentTimeMillis() + MAXIMUM_STATE_WAIT_TIME;
+ long waitTime = MAXIMUM_STATE_WAIT_TIME;
+
+ while ((_currentState != s) && (waitTime > 0))
+ {
+ try
+ {
+ _stateLock.wait(MAXIMUM_STATE_WAIT_TIME);
+ }
+ catch (InterruptedException e)
+ {
+ _logger.warn("Thread interrupted");
+ }
+
+ if (_currentState != s)
+ {
+ waitTime = waitUntilTime - System.currentTimeMillis();
+ }
+ }
+
+ if (_currentState != s)
+ {
+ _logger.warn("State not achieved within permitted time. Current state " + _currentState
+ + ", desired state: " + s);
+ throw new AMQException("State not achieved within permitted time. Current state " + _currentState
+ + ", desired state: " + s);
+ }
+ }
+
+ // at this point the state will have changed.
+ }
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+
+ public void setProtocolSession(AMQProtocolSession session)
+ {
+ _protocolSession = session;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.java
new file mode 100644
index 0000000000..41fa1ba704
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+import org.apache.qpid.AMQException;
+
+/**
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Not used! Delete.
+ */
+public class IllegalStateTransitionException extends AMQException
+{
+ private AMQState _originalState;
+
+ private Class _frame;
+
+ public IllegalStateTransitionException(AMQState originalState, Class frame)
+ {
+ super("No valid state transition defined for receiving frame " + frame +
+ " from state " + originalState);
+ _originalState = originalState;
+ _frame = frame;
+ }
+
+ public AMQState getOriginalState()
+ {
+ return _originalState;
+ }
+
+ public Class getFrameClass()
+ {
+ return _frame;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java
new file mode 100644
index 0000000000..b3932533ce
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+/**
+ * A frame listener that is informed of the protocl state when invoked and has
+ * the opportunity to update state.
+ *
+ */
+public interface StateAwareMethodListener
+{
+ void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession,
+ AMQMethodEvent evt) throws AMQException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/StateListener.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/StateListener.java
new file mode 100644
index 0000000000..df207a0a23
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/StateListener.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+import org.apache.qpid.AMQException;
+
+public interface StateListener
+{
+ void stateChanged(AMQState oldState, AMQState newState) throws AMQException;
+
+ void error(Throwable t);
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java
new file mode 100644
index 0000000000..73d5a8a3d6
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.state;
+
+import org.apache.qpid.AMQException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Waits for a particular state to be reached.
+ */
+public class StateWaiter implements StateListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(StateWaiter.class);
+
+ private final AMQState _state;
+
+ private volatile boolean _newStateAchieved;
+
+ private volatile Throwable _throwable;
+
+ private final Object _monitor = new Object();
+ private static final long TIME_OUT = 1000 * 60 * 2;
+
+ public StateWaiter(AMQState state)
+ {
+ _state = state;
+ }
+
+ public void waituntilStateHasChanged() throws AMQException
+ {
+ synchronized (_monitor)
+ {
+ //
+ // The guard is required in case we are woken up by a spurious
+ // notify().
+ //
+ while (!_newStateAchieved && (_throwable == null))
+ {
+ try
+ {
+ _logger.debug("State " + _state + " not achieved so waiting...");
+ _monitor.wait(TIME_OUT);
+ // fixme this won't cause the timeout to exit the loop. need to set _throwable
+ }
+ catch (InterruptedException e)
+ {
+ _logger.debug("Interrupted exception caught while waiting: " + e, e);
+ }
+ }
+ }
+
+ if (_throwable != null)
+ {
+ _logger.debug("Throwable reached state waiter: " + _throwable);
+ if (_throwable instanceof AMQException)
+ {
+ throw (AMQException) _throwable;
+ }
+ else
+ {
+ throw new AMQException("Error: " + _throwable, _throwable); // FIXME: this will wrap FailoverException in throwable which will prevent it being caught.
+ }
+ }
+ }
+
+ public void stateChanged(AMQState oldState, AMQState newState)
+ {
+ synchronized (_monitor)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("stateChanged called changing from :" + oldState + " to :" + newState);
+ }
+
+ if (_state == newState)
+ {
+ _newStateAchieved = true;
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("New state reached so notifying monitor");
+ }
+
+ _monitor.notifyAll();
+ }
+ }
+ }
+
+ public void error(Throwable t)
+ {
+ synchronized (_monitor)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("exceptionThrown called");
+ }
+
+ _throwable = t;
+ _monitor.notifyAll();
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java b/Final/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java
new file mode 100644
index 0000000000..4a4f4a0a38
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.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.client.state.listener;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.protocol.BlockingMethodFrameListener;
+import org.apache.qpid.framing.AMQMethodBody;
+
+public class SpecificMethodFrameListener extends BlockingMethodFrameListener
+{
+ private final Class _expectedClass;
+
+ public SpecificMethodFrameListener(int channelId, Class expectedClass)
+ {
+ super(channelId);
+ _expectedClass = expectedClass;
+ }
+
+ public boolean processMethod(int channelId, AMQMethodBody frame) //throws AMQException
+ {
+
+ //equiv to: (frame instanceof _expectedClass)
+ return _expectedClass.isInstance(frame);
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o instanceof SpecificMethodFrameListener)
+ {
+ SpecificMethodFrameListener other = (SpecificMethodFrameListener) o;
+
+ // here we need to check if the two classes are the same.
+ return (_channelId == other._channelId) && (_expectedClass.equals(other._expectedClass));
+ }
+
+ return false;
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java b/Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java
new file mode 100644
index 0000000000..da16baaad9
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.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.client.transport;
+
+import org.apache.qpid.jms.BrokerDetails;
+
+/**
+ * AMQNoTransportForProtocolException represents a connection failure where there is no transport medium to connect
+ * to the broker available. This may be the case if their is a error in the connection url, or an unsupported transport
+ * type is specified.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent absence of a transport medium.
+ * </table>
+ */
+public class AMQNoTransportForProtocolException extends AMQTransportConnectionException
+{
+ BrokerDetails _details;
+
+ public AMQNoTransportForProtocolException(BrokerDetails details)
+ {
+ this(details, "No Transport exists for specified broker protocol");
+ }
+
+ public AMQNoTransportForProtocolException(BrokerDetails details, String message)
+ {
+ super(null, message, null);
+
+ _details = details;
+ }
+
+ public String toString()
+ {
+ if (_details != null)
+ {
+ return super.toString() + _details.toString();
+ }
+ else
+ {
+ return super.toString();
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java b/Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java
new file mode 100644
index 0000000000..24b4e03b39
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.transport;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQTransportConnectionException indicates a failure to establish a connection through the transporting medium, to
+ * an AMQP broker.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to connect through the transport medium.
+ * </table>
+ */
+public class AMQTransportConnectionException extends AMQException
+{
+ public AMQTransportConnectionException(AMQConstant errorCode, String message, Throwable cause)
+ {
+ super(errorCode, message, cause);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java b/Final/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java
new file mode 100644
index 0000000000..7a24d6e15a
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.transport;
+
+import java.io.IOException;
+
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.jms.BrokerDetails;
+
+public interface ITransportConnection
+{
+ void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail)
+ throws IOException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/Final/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java
new file mode 100644
index 0000000000..5482e48699
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.transport;
+
+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.SocketConnectorConfig;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+public class SocketTransportConnection implements ITransportConnection
+{
+ private static final Logger _logger = LoggerFactory.getLogger(SocketTransportConnection.class);
+ private static final int DEFAULT_BUFFER_SIZE = 32 * 1024;
+
+ private SocketConnectorFactory _socketConnectorFactory;
+
+ static interface SocketConnectorFactory
+ {
+ IoConnector newSocketConnector();
+ }
+
+ public SocketTransportConnection(SocketConnectorFactory socketConnectorFactory)
+ {
+ _socketConnectorFactory = socketConnectorFactory;
+ }
+
+ public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException
+ {
+ ByteBuffer.setUseDirectBuffers(Boolean.getBoolean("amqj.enableDirectBuffers"));
+
+ // the MINA default is currently to use the pooled allocator although this may change in future
+ // once more testing of the performance of the simple allocator has been done
+ if (!Boolean.getBoolean("amqj.enablePooledAllocator"))
+ {
+ _logger.info("Using SimpleByteBufferAllocator");
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+ }
+
+ final IoConnector ioConnector = _socketConnectorFactory.newSocketConnector();
+ SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig();
+
+ // if we do not use our own thread model we get the MINA default which is to use
+ // its own leader-follower model
+ boolean readWriteThreading = Boolean.getBoolean("amqj.shared_read_write_pool");
+ if (readWriteThreading)
+ {
+ cfg.setThreadModel(ReadWriteThreadModel.getInstance());
+ }
+
+ SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig();
+ scfg.setTcpNoDelay("true".equalsIgnoreCase(System.getProperty("amqj.tcpNoDelay", "true")));
+ scfg.setSendBufferSize(Integer.getInteger("amqj.sendBufferSize", DEFAULT_BUFFER_SIZE));
+ _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);
+ ConnectFuture future = ioConnector.connect(address, protocolHandler);
+
+ // wait for connection to complete
+ if (future.join(brokerDetail.getTimeout()))
+ {
+ // we call getSession which throws an IOException if there has been an error connecting
+ future.getSession();
+ }
+ else
+ {
+ throw new IOException("Timeout waiting for connection.");
+ }
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/Final/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
new file mode 100644
index 0000000000..1d0d6a3491
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
@@ -0,0 +1,318 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.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.SocketConnector;
+import org.apache.mina.transport.vmpipe.VmPipeAcceptor;
+import org.apache.mina.transport.vmpipe.VmPipeAddress;
+
+import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * The TransportConnection is a helper class responsible for connecting to an AMQ server. It sets up the underlying
+ * connector, which currently always uses TCP/IP sockets. It creates the "protocol handler" which deals with MINA
+ * protocol events. <p/> Could be extended in future to support different transport types by turning this into concrete
+ * class/interface combo.
+ */
+public class TransportConnection
+{
+ private static ITransportConnection _instance;
+
+ private static Map _inVmPipeAddress = new HashMap();
+ private static VmPipeAcceptor _acceptor;
+ private static int _currentInstance = -1;
+ private static int _currentVMPort = -1;
+
+ private static final int TCP = 0;
+ private static final int VM = 1;
+
+ 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
+ {
+ int transport = getTransport(details.getTransport());
+
+ if (transport == -1)
+ {
+ throw new AMQNoTransportForProtocolException(details);
+ }
+
+ if (transport == _currentInstance)
+ {
+ if (transport == VM)
+ {
+ if (_currentVMPort == details.getPort())
+ {
+ return _instance;
+ }
+ }
+ else
+ {
+ return _instance;
+ }
+ }
+
+ _currentInstance = transport;
+
+ switch (transport)
+ {
+
+ case TCP:
+ _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 (Boolean.getBoolean("qpidnio"))
+ {
+ _logger.error("Using Qpid NIO - sysproperty 'qpidnio' is set.");
+ // result = new org.apache.qpid.nio.SocketConnector(); // non-blocking connector
+ }
+ // else
+
+ {
+ _logger.info("Using Mina NIO");
+ result = new SocketConnector(); // non-blocking connector
+ }
+
+ // Don't have the connector's worker thread wait around for other connections (we only use
+ // one SocketConnector per connection at the moment anyway). This allows short-running
+ // clients (like unit tests) to complete quickly.
+ result.setWorkerTimeout(0);
+
+ return result;
+ }
+ });
+ break;
+
+ case VM:
+ {
+ _instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker"));
+ break;
+ }
+ }
+
+ return _instance;
+ }
+
+ private static int getTransport(String transport)
+ {
+ if (transport.equals(BrokerDetails.TCP))
+ {
+ return TCP;
+ }
+
+ if (transport.equals(BrokerDetails.VM))
+ {
+ return VM;
+ }
+
+ return -1;
+ }
+
+ private static ITransportConnection getVMTransport(BrokerDetails details, boolean AutoCreate)
+ throws AMQVMBrokerCreationException
+ {
+ int port = details.getPort();
+
+ synchronized (_inVmPipeAddress)
+ {
+ if (!_inVmPipeAddress.containsKey(port))
+ {
+ if (AutoCreate)
+ {
+ createVMBroker(port);
+ }
+ else
+ {
+ throw new AMQVMBrokerCreationException(null, port, "VM Broker on port " + port
+ + " does not exist. Auto create disabled.", null);
+ }
+ }
+ }
+ return new VmPipeTransportConnection(port);
+ }
+
+ public static void createVMBroker(int port) throws AMQVMBrokerCreationException
+ {
+ if (_acceptor == null)
+ {
+ _acceptor = new VmPipeAcceptor();
+
+ IoServiceConfig config = _acceptor.getDefaultConfig();
+
+ config.setThreadModel(ReadWriteThreadModel.getInstance());
+ }
+
+ synchronized (_inVmPipeAddress)
+ {
+ if (!_inVmPipeAddress.containsKey(port))
+ {
+ _logger.info("Creating InVM Qpid.AMQP listening on port " + port);
+ IoHandlerAdapter provider = null;
+ try
+ {
+ VmPipeAddress pipe = new VmPipeAddress(port);
+
+ provider = createBrokerInstance(port);
+
+ _acceptor.bind(pipe, provider);
+
+ _inVmPipeAddress.put(port, pipe);
+ _logger.info("Created InVM Qpid.AMQP listening on port " + port);
+ }
+ catch (IOException e)
+ {
+ _logger.error("Got IOException.", e);
+
+ // Try and unbind provider
+ try
+ {
+ VmPipeAddress pipe = new VmPipeAddress(port);
+
+ try
+ {
+ _acceptor.unbind(pipe);
+ }
+ catch (Exception ignore)
+ {
+ // ignore
+ }
+
+ if (provider == null)
+ {
+ provider = createBrokerInstance(port);
+ }
+
+ _acceptor.bind(pipe, provider);
+ _inVmPipeAddress.put(port, pipe);
+ _logger.info("Created InVM Qpid.AMQP listening on port " + port);
+ }
+ catch (IOException justUseFirstException)
+ {
+ String because;
+ if (e.getCause() == null)
+ {
+ because = e.toString();
+ }
+ else
+ {
+ because = e.getCause().toString();
+ }
+
+ throw new AMQVMBrokerCreationException(null, port, because + " Stopped binding of InVM Qpid.AMQP", e);
+ }
+ }
+ }
+ else
+ {
+ _logger.info("InVM Qpid.AMQP on port " + port + " already exits.");
+ }
+ }
+ }
+
+ private static IoHandlerAdapter createBrokerInstance(int port) throws AMQVMBrokerCreationException
+ {
+ String protocolProviderClass = System.getProperty("amqj.protocolprovider.class", DEFAULT_QPID_SERVER);
+ _logger.info("Creating Qpid protocol provider: " + protocolProviderClass);
+
+ // can't use introspection to get Provider as it is a server class.
+ // need to go straight to IoHandlerAdapter but that requries the queues and exchange from the ApplicationRegistry which we can't access.
+
+ // get right constructor and pass in instancec ID - "port"
+ IoHandlerAdapter provider;
+ try
+ {
+ Class[] cnstr = { Integer.class };
+ Object[] params = { port };
+ provider = (IoHandlerAdapter) Class.forName(protocolProviderClass).getConstructor(cnstr).newInstance(params);
+ // Give the broker a second to create
+ _logger.info("Created VMBroker Instance:" + port);
+ }
+ catch (Exception e)
+ {
+ _logger.info("Unable to create InVM Qpid.AMQP on port " + port + ". Because: " + e.getCause());
+ String because;
+ if (e.getCause() == null)
+ {
+ because = e.toString();
+ }
+ else
+ {
+ because = e.getCause().toString();
+ }
+
+ AMQVMBrokerCreationException amqbce =
+ new AMQVMBrokerCreationException(null, port, because + " Stopped InVM Qpid.AMQP creation", null);
+ amqbce.initCause(e);
+ throw amqbce;
+ }
+
+ return provider;
+ }
+
+ public static void killAllVMBrokers()
+ {
+ _logger.info("Killing all VM Brokers");
+ _acceptor.unbindAll();
+ synchronized (_inVmPipeAddress)
+ {
+ Iterator keys = _inVmPipeAddress.keySet().iterator();
+
+ while (keys.hasNext())
+ {
+ int id = (Integer) keys.next();
+ _inVmPipeAddress.remove(id);
+ }
+ }
+ }
+
+ public static void killVMBroker(int port)
+ {
+ synchronized (_inVmPipeAddress)
+ {
+ VmPipeAddress pipe = (VmPipeAddress) _inVmPipeAddress.get(port);
+ if (pipe != null)
+ {
+ _logger.info("Killing VM Broker:" + port);
+ _inVmPipeAddress.remove(port);
+ _acceptor.unbind(pipe);
+ }
+ }
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/Final/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java
new file mode 100644
index 0000000000..d9137dc8b1
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.transport;
+
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.transport.vmpipe.VmPipeAddress;
+import org.apache.mina.transport.vmpipe.VmPipeConnector;
+
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.pool.PoolingFilter;
+import org.apache.qpid.pool.ReferenceCountingExecutorService;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class VmPipeTransportConnection implements ITransportConnection
+{
+ private static final Logger _logger = LoggerFactory.getLogger(VmPipeTransportConnection.class);
+
+ private static int _port;
+
+ public VmPipeTransportConnection(int port)
+ {
+ _port = port;
+ }
+
+ public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException
+ {
+ final VmPipeConnector ioConnector = new VmPipeConnector();
+ final IoServiceConfig cfg = ioConnector.getDefaultConfig();
+
+ cfg.setThreadModel(ReadWriteThreadModel.getInstance());
+
+ final VmPipeAddress address = new VmPipeAddress(_port);
+ _logger.info("Attempting connection to " + address);
+ ConnectFuture future = ioConnector.connect(address, protocolHandler);
+ // wait for connection to complete
+ future.join();
+ // we call getSession which throws an IOException if there has been an error connecting
+ future.getSession();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java b/Final/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java
new file mode 100644
index 0000000000..579b0d9e90
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.util;
+
+import java.util.Iterator;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A blocking queue that emits events above a user specified threshold allowing the caller to take action (e.g. flow
+ * control) to try to prevent the queue growing (much) further. The underlying queue itself is not bounded therefore the
+ * caller is not obliged to react to the events. <p/> This implementation is <b>only</b> safe where we have a single
+ * thread adding items and a single (different) thread removing items.
+ *
+ * @todo Make this implement java.util.Queue and hide the implementation. Then different queue types can be substituted.
+ */
+public class FlowControllingBlockingQueue
+{
+ /** This queue is bounded and is used to store messages before being dispatched to the consumer */
+ private final BlockingQueue _queue = new LinkedBlockingQueue();
+
+ private final int _flowControlHighThreshold;
+ private final int _flowControlLowThreshold;
+
+ private final ThresholdListener _listener;
+
+ /** We require a separate count so we can track whether we have reached the threshold */
+ private int _count;
+
+ public boolean isEmpty()
+ {
+ return _queue.isEmpty();
+ }
+
+ public interface ThresholdListener
+ {
+ void aboveThreshold(int currentValue);
+
+ void underThreshold(int currentValue);
+ }
+
+ public FlowControllingBlockingQueue(int threshold, ThresholdListener listener)
+ {
+ this(threshold, threshold, listener);
+ }
+
+ public FlowControllingBlockingQueue(int highThreshold, int lowThreshold, ThresholdListener listener)
+ {
+ _flowControlHighThreshold = highThreshold;
+ _flowControlLowThreshold = lowThreshold;
+ _listener = listener;
+ }
+
+ public Object poll(long time, TimeUnit unit) throws InterruptedException
+ {
+ Object o = _queue.poll(time, unit);
+ if (o != null && _listener != null)
+ {
+ synchronized (_listener)
+ {
+ if (_count-- == _flowControlLowThreshold)
+ {
+ _listener.underThreshold(_count);
+ }
+ }
+ }
+
+ return o;
+ }
+
+ public void add(Object o)
+ {
+ _queue.add(o);
+ if (_listener != null)
+ {
+ synchronized (_listener)
+ {
+ if (++_count == _flowControlHighThreshold)
+ {
+ _listener.aboveThreshold(_count);
+ }
+ }
+ }
+ }
+
+ public Iterator iterator()
+ {
+ return _queue.iterator();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java b/Final/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java
new file mode 100644
index 0000000000..1791e7ede3
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.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.client.vmbroker;
+
+import org.apache.qpid.client.transport.AMQTransportConnectionException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQVMBrokerCreationException represents failure to create an in VM broker on the vm transport medium.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to create an in VM broker.
+ * </table>
+ */
+public class AMQVMBrokerCreationException extends AMQTransportConnectionException
+{
+ private int _port;
+
+ /**
+ * @param port
+ *
+ * @deprecated
+ */
+ public AMQVMBrokerCreationException(int port)
+ {
+ this(null, port, "Unable to create vm broker", null);
+ }
+
+ public AMQVMBrokerCreationException(AMQConstant errorCode, int port, String message, Throwable cause)
+ {
+ super(errorCode, message, cause);
+ _port = port;
+ }
+
+ public String toString()
+ {
+ return super.toString() + " on port " + _port;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java b/Final/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
new file mode 100644
index 0000000000..91f7710025
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java
@@ -0,0 +1,74 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import org.apache.qpid.client.SSLConfiguration;
+
+public interface BrokerDetails
+{
+
+ /*
+ * Known URL Options
+ * @see ConnectionURL
+ */
+ public static final String OPTIONS_RETRY = "retries";
+ public static final String OPTIONS_CONNECT_TIMEOUT = "connecttimeout";
+ public static final int DEFAULT_PORT = 5672;
+
+ public static final String TCP = "tcp";
+ public static final String VM = "vm";
+
+ public static final String DEFAULT_TRANSPORT = TCP;
+
+ public static final String URL_FORMAT_EXAMPLE =
+ "<transport>://<hostname>[:<port Default=\"" + DEFAULT_PORT + "\">][?<option>='<value>'[,<option>='<value>']]";
+
+ public static final long DEFAULT_CONNECT_TIMEOUT = 30000L;
+ public static final boolean USE_SSL_DEFAULT = false;
+
+ String getHost();
+
+ void setHost(String host);
+
+ int getPort();
+
+ void setPort(int port);
+
+ String getTransport();
+
+ void setTransport(String transport);
+
+ String getOption(String key);
+
+ void setOption(String key, String value);
+
+ long getTimeout();
+
+ void setTimeout(long timeout);
+
+ SSLConfiguration getSSLConfiguration();
+
+ void setSSLConfiguration(SSLConfiguration sslConfiguration);
+
+ String toString();
+
+ boolean equals(Object o);
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/ChannelLimitReachedException.java b/Final/java/client/src/main/java/org/apache/qpid/jms/ChannelLimitReachedException.java
new file mode 100644
index 0000000000..3d4a4573ed
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/ChannelLimitReachedException.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import javax.jms.ResourceAllocationException;
+
+/**
+ * Indicates that the maximum number of sessions per connection limit has been reached.
+ */
+public class ChannelLimitReachedException extends ResourceAllocationException
+{
+ private static final String ERROR_CODE = "1";
+
+ private long _limit;
+
+ public ChannelLimitReachedException(long limit)
+ {
+ super("Unable to create session since maximum number of sessions per connection is " +
+ limit + ". Either close one or more sessions or increase the " +
+ "maximum number of sessions per connection (or contact your AMQP administrator.", ERROR_CODE);
+ _limit = limit;
+ }
+
+ public long getLimit()
+ {
+ return _limit;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/Connection.java b/Final/java/client/src/main/java/org/apache/qpid/jms/Connection.java
new file mode 100644
index 0000000000..616c6dbbec
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/Connection.java
@@ -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.
+ *
+ */
+package org.apache.qpid.jms;
+
+import javax.jms.JMSException;
+
+public interface Connection extends javax.jms.Connection
+{
+ /**
+ * @return the maximum number of sessions supported by this Connection
+ */
+ long getMaximumChannelCount() throws JMSException;
+
+ void setConnectionListener(ConnectionListener listener);
+
+ /**
+ * Get the connection listener that has been registered with this connection, if any
+ *
+ * @return the listener or null if none has been set
+ */
+ ConnectionListener getConnectionListener();
+
+ /**
+ * Create a session specifying the prefetch limit of messages.
+ *
+ * @param transacted
+ * @param acknowledgeMode
+ * @param prefetch the maximum number of messages to buffer in the client. This
+ * applies as a total across all consumers
+ * @return
+ * @throws JMSException
+ */
+ org.apache.qpid.jms.Session createSession(boolean transacted, int acknowledgeMode,
+ int prefetch) throws JMSException;
+
+
+ /**
+ * Create a session specifying the prefetch limit of messages.
+ *
+ * @param transacted
+ * @param acknowledgeMode
+ * @param prefetchHigh the maximum number of messages to buffer in the client.
+ * This applies as a total across all consumers
+ * @param prefetchLow the number of messages that must be in the buffer in the client to renable message flow.
+ * This applies as a total across all consumers
+ * @return
+ * @throws JMSException
+ */
+ org.apache.qpid.jms.Session createSession(boolean transacted, int acknowledgeMode,
+ int prefetchHigh, int prefetchLow) throws JMSException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionListener.java b/Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionListener.java
new file mode 100644
index 0000000000..11c235901c
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionListener.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.jms;
+
+public interface ConnectionListener
+{
+ /**
+ * Called when bytes have been transmitted to the server
+ * @param count the number of bytes sent in total since the connection was opened
+ */
+ void bytesSent(long count);
+
+ /**
+ * Called when some bytes have been received on a connection
+ * @param count the number of bytes received in total since the connection was opened
+ */
+ void bytesReceived(long count);
+
+ /**
+ * Called after the infrastructure has detected that failover is required but before attempting failover.
+ * @param redirect true if the broker requested redirect. false if failover is occurring due to a connection error.
+ * @return true to continue failing over, false to veto failover and raise a connection exception
+ */
+ boolean preFailover(boolean redirect);
+
+ /**
+ * Called after connection has been made to another broker after failover has been started but before
+ * any resubscription has been done.
+ * @return true to continue with resubscription, false to prevent automatic resubscription. This is useful in
+ * cases where the application wants to handle resubscription. Note that in the latter case all sessions, producers
+ * and consumers are invalidated.
+ */
+ boolean preResubscribe();
+
+ /**
+ * Called once failover has completed successfully. This is called irrespective of whether the client has
+ * vetoed automatic resubscription.
+ */
+ void failoverComplete();
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java b/Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java
new file mode 100644
index 0000000000..2d91e290c4
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java
@@ -0,0 +1,86 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import java.util.List;
+
+/**
+ Connection URL format
+ amqp://[user:pass@][clientid]/virtualhost?brokerlist='tcp://host:port?option=\'value\'&option=\'value\';vm://:3/virtualpath?option=\'value\''&failover='method?option=\'value\'&option='value''"
+ Options are of course optional except for requiring a single broker in the broker list.
+ The option seperator is defined to be either '&' or ','
+ */
+public interface ConnectionURL
+{
+ public static final String AMQ_PROTOCOL = "amqp";
+ public static final String OPTIONS_BROKERLIST = "brokerlist";
+ public static final String OPTIONS_FAILOVER = "failover";
+ public static final String OPTIONS_FAILOVER_CYCLE = "cyclecount";
+ public static final String OPTIONS_SSL = "ssl";
+ public static final String OPTIONS_DEFAULT_TOPIC_EXCHANGE = "defaultTopicExchange";
+ public static final String OPTIONS_DEFAULT_QUEUE_EXCHANGE = "defaultQueueExchange";
+ public static final String OPTIONS_TEMPORARY_TOPIC_EXCHANGE = "temporaryTopicExchange";
+ public static final String OPTIONS_TEMPORARY_QUEUE_EXCHANGE = "temporaryQueueExchange";
+
+ String getURL();
+
+ String getFailoverMethod();
+
+ String getFailoverOption(String key);
+
+ int getBrokerCount();
+
+ BrokerDetails getBrokerDetails(int index);
+
+ void addBrokerDetails(BrokerDetails broker);
+
+ List<BrokerDetails> getAllBrokerDetails();
+
+ String getClientName();
+
+ void setClientName(String clientName);
+
+ String getUsername();
+
+ void setUsername(String username);
+
+ String getPassword();
+
+ void setPassword(String password);
+
+ String getVirtualHost();
+
+ void setVirtualHost(String virtualHost);
+
+ String getOption(String key);
+
+ void setOption(String key, String value);
+
+ AMQShortString getDefaultQueueExchangeName();
+
+ AMQShortString getDefaultTopicExchangeName();
+
+ AMQShortString getTemporaryQueueExchangeName();
+
+ AMQShortString getTemporaryTopicExchangeName();
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java b/Final/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java
new file mode 100644
index 0000000000..6ec883ff0b
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java
@@ -0,0 +1,324 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import org.apache.qpid.jms.failover.FailoverMethod;
+import org.apache.qpid.jms.failover.FailoverRoundRobinServers;
+import org.apache.qpid.jms.failover.FailoverSingleServer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FailoverPolicy
+{
+ private static final Logger _logger = LoggerFactory.getLogger(FailoverPolicy.class);
+
+ 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];
+
+ private int _currentMethod;
+
+ private int _methodsRetries;
+
+ private int _currentRetry;
+
+ private boolean _timing;
+
+ private long _lastMethodTime;
+ private long _lastFailTime;
+
+ public FailoverPolicy(ConnectionURL connectionDetails)
+ {
+ FailoverMethod method;
+
+ // todo This should be integrated in to the connection url when it supports
+ // multiple strategies.
+
+ _methodsRetries = 0;
+
+ if (connectionDetails.getFailoverMethod() == null)
+ {
+ if (connectionDetails.getBrokerCount() > 1)
+ {
+ method = new FailoverRoundRobinServers(connectionDetails);
+ }
+ else
+ {
+ method = new FailoverSingleServer(connectionDetails);
+ }
+ }
+ else
+ {
+ String failoverMethod = connectionDetails.getFailoverMethod();
+
+ /*
+ if (failoverMethod.equals(FailoverMethod.RANDOM))
+ {
+ //todo write a random connection Failover
+ }
+ */
+ if (failoverMethod.equals(FailoverMethod.SINGLE_BROKER))
+ {
+ method = new FailoverRoundRobinServers(connectionDetails);
+ }
+ else
+ {
+ if (failoverMethod.equals(FailoverMethod.ROUND_ROBIN))
+ {
+ method = new FailoverRoundRobinServers(connectionDetails);
+ }
+ else
+ {
+ try
+ {
+ Class[] constructorSpec = { ConnectionURL.class };
+ Object[] params = { connectionDetails };
+
+ method =
+ (FailoverMethod) ClassLoader.getSystemClassLoader().loadClass(failoverMethod)
+ .getConstructor(constructorSpec).newInstance(params);
+ }
+ catch (Exception cnfe)
+ {
+ throw new IllegalArgumentException("Unknown failover method:" + failoverMethod, cnfe);
+ }
+ }
+ }
+ }
+
+ if (method == null)
+ {
+ throw new IllegalArgumentException("Unknown failover method specified.");
+ }
+
+ reset();
+
+ _methods[_currentMethod] = method;
+ }
+
+ public FailoverPolicy(FailoverMethod method)
+ {
+ this(method, 0);
+ }
+
+ public FailoverPolicy(FailoverMethod method, int retries)
+ {
+ _methodsRetries = retries;
+
+ reset();
+
+ _methods[_currentMethod] = method;
+ }
+
+ private void reset()
+ {
+ _currentMethod = 0;
+ _currentRetry = 0;
+ _timing = false;
+
+ }
+
+ public boolean failoverAllowed()
+ {
+ boolean failoverAllowed;
+
+ if (_timing)
+ {
+ long now = System.currentTimeMillis();
+
+ if ((now - _lastMethodTime) >= DEFAULT_METHOD_TIMEOUT)
+ {
+ _logger.info("Failover method timeout");
+ _lastMethodTime = now;
+
+ if (!nextMethod())
+ {
+ return false;
+ }
+
+ }
+ else
+ {
+ if ((now - _lastFailTime) >= DEFAULT_FAILOVER_TIMEOUT)
+ {
+ _logger.info("Failover timeout");
+
+ return false;
+ }
+ else
+ {
+ _lastMethodTime = now;
+ }
+ }
+ }
+ else
+ {
+ _timing = true;
+ _lastMethodTime = System.currentTimeMillis();
+ _lastFailTime = _lastMethodTime;
+ }
+
+ if (_methods[_currentMethod].failoverAllowed())
+ {
+ failoverAllowed = true;
+ }
+ else
+ {
+ if (_currentMethod < (_methods.length - 1))
+ {
+ nextMethod();
+ _logger.info("Changing method to " + _methods[_currentMethod].methodName());
+
+ return failoverAllowed();
+ }
+ else
+ {
+ return cycleMethods();
+ }
+ }
+
+ return failoverAllowed;
+ }
+
+ private boolean nextMethod()
+ {
+ if (_currentMethod < (_methods.length - 1))
+ {
+ _currentMethod++;
+ _methods[_currentMethod].reset();
+
+ return true;
+ }
+ else
+ {
+ return cycleMethods();
+ }
+ }
+
+ private boolean cycleMethods()
+ {
+ if (_currentRetry < _methodsRetries)
+ {
+ _currentRetry++;
+
+ _currentMethod = 0;
+
+ _logger.info("Retrying methods starting with " + _methods[_currentMethod].methodName());
+ _methods[_currentMethod].reset();
+
+ return failoverAllowed();
+ }
+ else
+ {
+ _logger.debug("All failover methods exhausted");
+
+ return false;
+ }
+ }
+
+ /**
+ * Notification that connection was successful.
+ */
+ public void attainedConnection()
+ {
+ _currentRetry = 0;
+
+ _methods[_currentMethod].attainedConnection();
+
+ _timing = false;
+ }
+
+ public BrokerDetails getCurrentBrokerDetails()
+ {
+ return _methods[_currentMethod].getCurrentBrokerDetails();
+ }
+
+ public BrokerDetails getNextBrokerDetails()
+ {
+ return _methods[_currentMethod].getNextBrokerDetails();
+ }
+
+ public void setBroker(BrokerDetails broker)
+ {
+ _methods[_currentMethod].setBroker(broker);
+ }
+
+ public void addMethod(FailoverMethod method)
+ {
+ int len = _methods.length + 1;
+ FailoverMethod[] newMethods = new FailoverMethod[len];
+ System.arraycopy(_methods, 0, newMethods, 0, _methods.length);
+ int index = len - 1;
+ newMethods[index] = method;
+ _methods = newMethods;
+ }
+
+ public void setMethodRetries(int retries)
+ {
+ _methodsRetries = retries;
+ }
+
+ public FailoverMethod getCurrentMethod()
+ {
+ if ((_currentMethod >= 0) && (_currentMethod < (_methods.length - 1)))
+ {
+ return _methods[_currentMethod];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("Failover Policy:\n");
+
+ if (failoverAllowed())
+ {
+ sb.append("Failover allowed\n");
+ }
+ else
+ {
+ sb.append("Failover not allowed\n");
+ }
+
+ sb.append("Failover policy methods\n");
+ for (int i = 0; i < _methods.length; i++)
+ {
+
+ if (i == _currentMethod)
+ {
+ sb.append(">");
+ }
+
+ sb.append(_methods[i].toString());
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/Message.java b/Final/java/client/src/main/java/org/apache/qpid/jms/Message.java
new file mode 100644
index 0000000000..6752ee616f
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/Message.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import javax.jms.JMSException;
+
+public interface Message extends javax.jms.Message
+{
+ public void acknowledgeThis() throws JMSException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/MessageConsumer.java b/Final/java/client/src/main/java/org/apache/qpid/jms/MessageConsumer.java
new file mode 100644
index 0000000000..caac2b5c1f
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/MessageConsumer.java
@@ -0,0 +1,27 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+/**
+ */
+public interface MessageConsumer extends javax.jms.MessageConsumer
+{
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java b/Final/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
new file mode 100644
index 0000000000..b91fc2d960
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+/**
+ */
+public interface MessageProducer extends javax.jms.MessageProducer
+{
+ /**
+ * Set the default MIME type for messages produced by this producer. This reduces the overhead of each message.
+ * @param mimeType
+ */
+ void setMimeType(String mimeType) throws JMSException;
+
+ /**
+ * Set the default encoding for messages produced by this producer. This reduces the overhead of each message.
+ * @param encoding the encoding as understood by XXXX how do I specify this?? RG
+ * @throws UnsupportedEncodingException if the encoding is not understood
+ */
+ void setEncoding(String encoding) throws UnsupportedEncodingException, JMSException;
+
+ void send(Destination destination, Message message, int deliveryMode,
+ int priority, long timeToLive, boolean immediate)
+ throws JMSException;
+
+ void send(Destination destination, Message message, int deliveryMode,
+ int priority, long timeToLive, boolean mandatory, boolean immediate)
+ throws JMSException;
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/Session.java b/Final/java/client/src/main/java/org/apache/qpid/jms/Session.java
new file mode 100644
index 0000000000..5287381fae
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/Session.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+
+
+public interface Session extends javax.jms.Session
+{
+ /**
+ * Indicates that no client acknowledgements are required. Broker assumes that once it has delivered
+ * a message packet successfully it is acknowledged.
+ */
+ static final int NO_ACKNOWLEDGE = 257;
+
+ /**
+ * Pre acknowledge means that an ack is sent per message but sent before user code has processed
+ * the message (i.e. before the onMessage() call or the receive() method has returned).
+ */
+ static final int PRE_ACKNOWLEDGE = 258;
+
+ MessageConsumer createConsumer(Destination destination,
+ int prefetch,
+ boolean noLocal,
+ boolean exclusive,
+ String selector) throws JMSException;
+
+ MessageConsumer createConsumer(Destination destination,
+ int prefetchHigh,
+ int prefetchLow,
+ boolean noLocal,
+ boolean exclusive,
+ String selector) throws JMSException;
+
+ /**
+ * @return the prefetch value used by default for consumers created on this session.
+ */
+ int getDefaultPrefetch();
+
+ /**
+ * @return the High water prefetch value used by default for consumers created on this session.
+ */
+ int getDefaultPrefetchHigh();
+
+ /**
+ * @return the Low water prefetch value used by default for consumers created on this session.
+ */
+ int getDefaultPrefetchLow();
+
+ /**
+ * Create a producer
+ * @param destination
+ * @param mandatory the value of the mandatory flag used by default on the producer
+ * @param immediate the value of the immediate flag used by default on the producer
+ * @return
+ * @throws JMSException
+ */
+ MessageProducer createProducer(Destination destination, boolean mandatory, boolean immediate)
+ throws JMSException;
+
+ /**
+ * Create a producer
+ * @param destination
+ * @param immediate the value of the immediate flag used by default on the producer
+ * @return
+ * @throws JMSException
+ */
+ MessageProducer createProducer(Destination destination, boolean immediate)
+ throws JMSException;
+
+ AMQShortString getTemporaryTopicExchangeName();
+
+ AMQShortString getDefaultQueueExchangeName();
+
+ AMQShortString getDefaultTopicExchangeName();
+
+ AMQShortString getTemporaryQueueExchangeName();
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java b/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java
new file mode 100644
index 0000000000..d7ec46dea3
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.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.jms.failover;
+
+import org.apache.qpid.jms.BrokerDetails;
+
+public interface FailoverMethod
+{
+ public static final String SINGLE_BROKER = "singlebroker";
+ public static final String ROUND_ROBIN = "roundrobin";
+ public static final String RANDOM = "random";
+ /**
+ * Reset the Failover to initial conditions
+ */
+ void reset();
+
+ /**
+ * Check if failover is possible for this method
+ *
+ * @return true if failover is allowed
+ */
+ boolean failoverAllowed();
+
+ /**
+ * Notification to the Failover method that a connection has been attained.
+ */
+ void attainedConnection();
+
+ /**
+ * If there is no current BrokerDetails the null will be returned.
+ * @return The current BrokerDetail value to use
+ */
+ BrokerDetails getCurrentBrokerDetails();
+
+ /**
+ * Move to the next BrokerDetails if one is available.
+ * @return the next BrokerDetail or null if there is none.
+ */
+ BrokerDetails getNextBrokerDetails();
+
+ /**
+ * Set the currently active broker to be the new value.
+ * @param broker The new BrokerDetail value
+ */
+ void setBroker(BrokerDetails broker);
+
+ /**
+ * Set the retries for this method
+ * @param maxRetries the maximum number of time to retry this Method
+ */
+ void setRetries(int maxRetries);
+
+ /**
+ * @return The name of this method for display purposes.
+ */
+ String methodName();
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java b/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java
new file mode 100644
index 0000000000..4e0d0b79b5
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java
@@ -0,0 +1,261 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms.failover;
+
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.jms.ConnectionURL;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FailoverRoundRobinServers implements FailoverMethod
+{
+ private static final Logger _logger = LoggerFactory.getLogger(FailoverRoundRobinServers.class);
+
+ /** The default number of times to cycle through all servers */
+ public static final int DEFAULT_CYCLE_RETRIES = 0;
+ /** The default number of times to retry each server */
+ public static final int DEFAULT_SERVER_RETRIES = 0;
+
+ /**
+ * The index into the hostDetails array of the broker to which we are connected
+ */
+ private int _currentBrokerIndex = -1;
+
+ /**
+ * The number of times to retry connecting for each server
+ */
+ private int _serverRetries;
+
+ /**
+ * The current number of retry attempts made
+ */
+ private int _currentServerRetry;
+
+ /**
+ * The number of times to cycle through the servers
+ */
+ private int _cycleRetries;
+
+ /**
+ * The current number of cycles performed.
+ */
+ private int _currentCycleRetries;
+
+ /**
+ * Array of BrokerDetail used to make connections.
+ */
+ private ConnectionURL _connectionDetails;
+
+ public FailoverRoundRobinServers(ConnectionURL connectionDetails)
+ {
+ if (!(connectionDetails.getBrokerCount() > 0))
+ {
+ throw new IllegalArgumentException("At least one broker details must be specified.");
+ }
+
+ _connectionDetails = connectionDetails;
+
+ // There is no current broker at startup so set it to -1.
+ _currentBrokerIndex = -1;
+
+ String cycleRetries = _connectionDetails.getFailoverOption(ConnectionURL.OPTIONS_FAILOVER_CYCLE);
+
+ if (cycleRetries != null)
+ {
+ try
+ {
+ _cycleRetries = Integer.parseInt(cycleRetries);
+ }
+ catch (NumberFormatException nfe)
+ {
+ _cycleRetries = DEFAULT_CYCLE_RETRIES;
+ }
+ }
+
+ _currentCycleRetries = 0;
+
+ _serverRetries = 0;
+ _currentServerRetry = -1;
+ }
+
+ public void reset()
+ {
+ _currentBrokerIndex = 0;
+ _currentCycleRetries = 0;
+ _currentServerRetry = -1;
+ }
+
+ public boolean failoverAllowed()
+ {
+ return ((_currentCycleRetries < _cycleRetries) || (_currentServerRetry < _serverRetries)
+ || (_currentBrokerIndex < (_connectionDetails.getBrokerCount() - 1)));
+ }
+
+ public void attainedConnection()
+ {
+ _currentCycleRetries = 0;
+ _currentServerRetry = -1;
+ }
+
+ public BrokerDetails getCurrentBrokerDetails()
+ {
+ if (_currentBrokerIndex == -1)
+ {
+ return null;
+ }
+
+ return _connectionDetails.getBrokerDetails(_currentBrokerIndex);
+ }
+
+ public BrokerDetails getNextBrokerDetails()
+ {
+ if (_currentBrokerIndex == (_connectionDetails.getBrokerCount() - 1))
+ {
+ if (_currentServerRetry < _serverRetries)
+ {
+ if (_currentBrokerIndex == -1)
+ {
+ _currentBrokerIndex = 0;
+
+ setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex));
+
+ _logger.info("First run using " + _connectionDetails.getBrokerDetails(_currentBrokerIndex));
+ }
+ else
+ {
+ _logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex));
+ }
+
+ _currentServerRetry++;
+ }
+ else
+ {
+ _currentCycleRetries++;
+ // failed to connect to first broker
+ _currentBrokerIndex = 0;
+
+ setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex));
+
+ // This is zero rather than -1 as we are already retrieving the details.
+ _currentServerRetry = 0;
+ }
+ // else - should force client to stop as max retries has been reached.
+ }
+ else
+ {
+ if (_currentServerRetry < _serverRetries)
+ {
+ if (_currentBrokerIndex == -1)
+ {
+ _currentBrokerIndex = 0;
+
+ setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex));
+
+ _logger.info("First run using " + _connectionDetails.getBrokerDetails(_currentBrokerIndex));
+ }
+ else
+ {
+ _logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex));
+ }
+
+ _currentServerRetry++;
+ }
+ else
+ {
+ _currentBrokerIndex++;
+
+ setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex));
+ // This is zero rather than -1 as we are already retrieving the details.
+ _currentServerRetry = 0;
+ }
+ }
+
+ return _connectionDetails.getBrokerDetails(_currentBrokerIndex);
+ }
+
+ public void setBroker(BrokerDetails broker)
+ {
+
+ _connectionDetails.addBrokerDetails(broker);
+
+ int index = _connectionDetails.getAllBrokerDetails().indexOf(broker);
+
+ String serverRetries = broker.getOption(BrokerDetails.OPTIONS_RETRY);
+
+ if (serverRetries != null)
+ {
+ try
+ {
+ _serverRetries = Integer.parseInt(serverRetries);
+ }
+ catch (NumberFormatException nfe)
+ {
+ _serverRetries = DEFAULT_SERVER_RETRIES;
+ }
+ }
+
+ _currentServerRetry = -1;
+ _currentBrokerIndex = index;
+ }
+
+ public void setRetries(int maxRetries)
+ {
+ _cycleRetries = maxRetries;
+ }
+
+ public String methodName()
+ {
+ return "Cycle Servers";
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("Cycle Servers:\n");
+
+ sb.append("Cycle Retries:");
+ sb.append(_cycleRetries);
+ sb.append("\nCurrent Cycle:");
+ sb.append(_currentCycleRetries);
+ sb.append("\nServer Retries:");
+ sb.append(_serverRetries);
+ sb.append("\nCurrent Retry:");
+ sb.append(_currentServerRetry);
+ sb.append("\nCurrent Broker:");
+ sb.append(_currentBrokerIndex);
+ sb.append("\n");
+
+ for (int i = 0; i < _connectionDetails.getBrokerCount(); i++)
+ {
+ if (i == _currentBrokerIndex)
+ {
+ sb.append(">");
+ }
+
+ sb.append(_connectionDetails.getBrokerDetails(i));
+ sb.append("\n");
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java b/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java
new file mode 100644
index 0000000000..68e6d25be0
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jms.failover;
+
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.jms.ConnectionURL;
+
+public class FailoverSingleServer implements FailoverMethod
+{
+ /** The default number of times to rety a conection to this server */
+ public static final int DEFAULT_SERVER_RETRIES = 1;
+
+ /**
+ * The details of the Single Server
+ */
+ private BrokerDetails _brokerDetail;
+
+ /**
+ * The number of times to retry connecting to the sever
+ */
+ private int _retries;
+
+ /**
+ * The current number of attempts made to the server
+ */
+ private int _currentRetries;
+
+
+ public FailoverSingleServer(ConnectionURL connectionDetails)
+ {
+ if (connectionDetails.getBrokerCount() > 0)
+ {
+ setBroker(connectionDetails.getBrokerDetails(0));
+ }
+ else
+ {
+ throw new IllegalArgumentException("BrokerDetails details required for connection.");
+ }
+ }
+
+ public FailoverSingleServer(BrokerDetails brokerDetail)
+ {
+ setBroker(brokerDetail);
+ }
+
+ public void reset()
+ {
+ _currentRetries = -1;
+ }
+
+ public boolean failoverAllowed()
+ {
+ return _currentRetries < _retries;
+ }
+
+ public void attainedConnection()
+ {
+ reset();
+ }
+
+ public BrokerDetails getCurrentBrokerDetails()
+ {
+ return _brokerDetail;
+ }
+
+ public BrokerDetails getNextBrokerDetails()
+ {
+ if (_currentRetries == _retries)
+ {
+ return null;
+ }
+ else
+ {
+ if (_currentRetries < _retries)
+ {
+ _currentRetries ++;
+ }
+
+ return _brokerDetail;
+ }
+ }
+
+ public void setBroker(BrokerDetails broker)
+ {
+ if (broker == null)
+ {
+ throw new IllegalArgumentException("BrokerDetails details cannot be null");
+ }
+ _brokerDetail = broker;
+
+ String retries = broker.getOption(BrokerDetails.OPTIONS_RETRY);
+ if (retries != null)
+ {
+ try
+ {
+ _retries = Integer.parseInt(retries);
+ }
+ catch (NumberFormatException nfe)
+ {
+ _retries = DEFAULT_SERVER_RETRIES;
+ }
+ }
+ else
+ {
+ _retries = DEFAULT_SERVER_RETRIES;
+ }
+
+ reset();
+ }
+
+ public void setRetries(int retries)
+ {
+ _retries = retries;
+ }
+
+ public String methodName()
+ {
+ return "Single Server";
+ }
+
+ public String toString()
+ {
+ return "SingleServer:\n"+
+ "Max Retries:"+_retries+
+ "\nCurrent Retry:"+_currentRetries+
+ "\n"+_brokerDetail+"\n";
+ }
+
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jndi/Example.properties b/Final/java/client/src/main/java/org/apache/qpid/jndi/Example.properties
new file mode 100644
index 0000000000..c457e94cab
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jndi/Example.properties
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialConextFactory
+
+# use the following property to configure the default connector
+#java.naming.provider.url - ignored.
+
+# register some connection factories
+# connectionfactory.[jndiname] = [ConnectionURL]
+connectionfactory.local = amqp://guest:guest@clientid/testpath?brokerlist='vm://:1'
+
+# register some queues in JNDI using the form
+# queue.[jndiName] = [physicalName]
+queue.MyQueue = example.MyQueue
+
+# register some topics in JNDI using the form
+# topic.[jndiName] = [physicalName]
+topic.ibmStocks = stocks.nyse.ibm
+
+# Register an AMQP destination in JNDI
+# NOTE: Qpid currently only supports direct,topics and headers
+# destination.[jniName] = [BindingURL]
+destination.direct = direct://amq.direct//directQueue
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java b/Final/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java
new file mode 100644
index 0000000000..a3174aec7a
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java
@@ -0,0 +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.jndi;
+
+import javax.naming.CompositeName;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingException;
+
+/**
+ * A default implementation of {@link NameParser}
+ * <p/>
+ * Based on class from ActiveMQ.
+ */
+public class NameParserImpl implements NameParser
+{
+ public Name parse(String name) throws NamingException
+ {
+ return new CompositeName(name);
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java b/Final/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java
new file mode 100644
index 0000000000..a46c7f3cd5
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java
@@ -0,0 +1,337 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jndi;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQHeadersExchange;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.BindingURL;
+import org.apache.qpid.url.URLSyntaxException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.jms.Queue;
+import javax.jms.Topic;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class PropertiesFileInitialContextFactory implements InitialContextFactory
+{
+ protected final Logger _logger = LoggerFactory.getLogger(PropertiesFileInitialContextFactory.class);
+
+ private String CONNECTION_FACTORY_PREFIX = "connectionfactory.";
+ private String DESTINATION_PREFIX = "destination.";
+ private String QUEUE_PREFIX = "queue.";
+ private String TOPIC_PREFIX = "topic.";
+
+ public Context getInitialContext(Hashtable environment) throws NamingException
+ {
+ Map data = new ConcurrentHashMap();
+
+ try
+ {
+
+ String file = null;
+ if (environment.containsKey(Context.PROVIDER_URL))
+ {
+ file = (String) environment.get(Context.PROVIDER_URL);
+ }
+ else
+ {
+ file = System.getProperty(Context.PROVIDER_URL);
+ }
+
+ if (file != null)
+ {
+ _logger.info("Loading Properties from:" + file);
+ // Load the properties specified
+ Properties p = new Properties();
+
+ p.load(new BufferedInputStream(new FileInputStream(file)));
+
+ environment.putAll(p);
+ _logger.info("Loaded Context Properties:" + environment.toString());
+ }
+ else
+ {
+ _logger.info("No Provider URL specified.");
+ }
+ }
+ catch (IOException ioe)
+ {
+ _logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL));
+ }
+
+ createConnectionFactories(data, environment);
+
+ createDestinations(data, environment);
+
+ createQueues(data, environment);
+
+ createTopics(data, environment);
+
+ return createContext(data, environment);
+ }
+
+ // Implementation methods
+ // -------------------------------------------------------------------------
+ protected ReadOnlyContext createContext(Map data, Hashtable environment)
+ {
+ return new ReadOnlyContext(environment, data);
+ }
+
+ protected void createConnectionFactories(Map data, Hashtable environment)
+ {
+ for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String key = entry.getKey().toString();
+ if (key.startsWith(CONNECTION_FACTORY_PREFIX))
+ {
+ String jndiName = key.substring(CONNECTION_FACTORY_PREFIX.length());
+ ConnectionFactory cf = createFactory(entry.getValue().toString());
+ if (cf != null)
+ {
+ data.put(jndiName, cf);
+ }
+ }
+ }
+ }
+
+ protected void createDestinations(Map data, Hashtable environment)
+ {
+ for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String key = entry.getKey().toString();
+ if (key.startsWith(DESTINATION_PREFIX))
+ {
+ String jndiName = key.substring(DESTINATION_PREFIX.length());
+ Destination dest = createDestination(entry.getValue().toString());
+ if (dest != null)
+ {
+ data.put(jndiName, dest);
+ }
+ }
+ }
+ }
+
+ protected void createQueues(Map data, Hashtable environment)
+ {
+ for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String key = entry.getKey().toString();
+ if (key.startsWith(QUEUE_PREFIX))
+ {
+ String jndiName = key.substring(QUEUE_PREFIX.length());
+ Queue q = createQueue(entry.getValue().toString());
+ if (q != null)
+ {
+ data.put(jndiName, q);
+ }
+ }
+ }
+ }
+
+ protected void createTopics(Map data, Hashtable environment)
+ {
+ for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String key = entry.getKey().toString();
+ if (key.startsWith(TOPIC_PREFIX))
+ {
+ String jndiName = key.substring(TOPIC_PREFIX.length());
+ Topic t = createTopic(entry.getValue().toString());
+ if (t != null)
+ {
+ data.put(jndiName, t);
+ }
+ }
+ }
+ }
+
+ /**
+ * Factory method to create new Connection Factory instances
+ */
+ protected ConnectionFactory createFactory(String url)
+ {
+ try
+ {
+ return new AMQConnectionFactory(url);
+ }
+ catch (URLSyntaxException urlse)
+ {
+ _logger.warn("Unable to createFactories:" + urlse);
+ }
+
+ return null;
+ }
+
+ /**
+ * Factory method to create new Destination instances from an AMQP BindingURL
+ */
+ protected Destination createDestination(String bindingURL)
+ {
+ AMQBindingURL binding;
+ try
+ {
+ binding = new AMQBindingURL(bindingURL);
+ }
+ catch (URLSyntaxException urlse)
+ {
+ _logger.warn("Unable to destination:" + urlse);
+
+ return null;
+ }
+
+ try
+ {
+ return AMQDestination.createDestination(binding);
+ }
+ catch (IllegalArgumentException iaw)
+ {
+ _logger.warn("Binding: '" + binding + "' not supported");
+
+ return null;
+ }
+ }
+
+ /**
+ * Factory method to create new Queue instances
+ */
+ protected Queue createQueue(Object value)
+ {
+ if (value instanceof AMQShortString)
+ {
+ return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, (AMQShortString) value);
+ }
+ else if (value instanceof String)
+ {
+ return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString((String) value));
+ }
+ else if (value instanceof BindingURL)
+ {
+ return new AMQQueue((BindingURL) value);
+ }
+
+ return null;
+ }
+
+ /**
+ * Factory method to create new Topic instances
+ */
+ protected Topic createTopic(Object value)
+ {
+ if (value instanceof AMQShortString)
+ {
+ return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, (AMQShortString) value);
+ }
+ else if (value instanceof String)
+ {
+ return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, new AMQShortString((String) value));
+ }
+ else if (value instanceof BindingURL)
+ {
+ return new AMQTopic((BindingURL) value);
+ }
+
+ return null;
+ }
+
+ /**
+ * Factory method to create new HeaderExcahnge instances
+ */
+ protected Destination createHeaderExchange(Object value)
+ {
+ if (value instanceof String)
+ {
+ return new AMQHeadersExchange((String) value);
+ }
+ else if (value instanceof BindingURL)
+ {
+ return new AMQHeadersExchange((BindingURL) value);
+ }
+
+ return null;
+ }
+
+ // Properties
+ // -------------------------------------------------------------------------
+ public String getConnectionPrefix()
+ {
+ return CONNECTION_FACTORY_PREFIX;
+ }
+
+ public void setConnectionPrefix(String connectionPrefix)
+ {
+ this.CONNECTION_FACTORY_PREFIX = connectionPrefix;
+ }
+
+ public String getDestinationPrefix()
+ {
+ return DESTINATION_PREFIX;
+ }
+
+ public void setDestinationPrefix(String destinationPrefix)
+ {
+ this.DESTINATION_PREFIX = destinationPrefix;
+ }
+
+ public String getQueuePrefix()
+ {
+ return QUEUE_PREFIX;
+ }
+
+ public void setQueuePrefix(String queuePrefix)
+ {
+ this.QUEUE_PREFIX = queuePrefix;
+ }
+
+ public String getTopicPrefix()
+ {
+ return TOPIC_PREFIX;
+ }
+
+ public void setTopicPrefix(String topicPrefix)
+ {
+ this.TOPIC_PREFIX = topicPrefix;
+ }
+}
diff --git a/Final/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java b/Final/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java
new file mode 100644
index 0000000000..1719ea1219
--- /dev/null
+++ b/Final/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java
@@ -0,0 +1,527 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jndi;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.Binding;
+import javax.naming.CompositeName;
+import javax.naming.Context;
+import javax.naming.LinkRef;
+import javax.naming.Name;
+import javax.naming.NameClassPair;
+import javax.naming.NameNotFoundException;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NotContextException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.Reference;
+import javax.naming.spi.NamingManager;
+
+/**
+ * Based on class from ActiveMQ.
+ * A read-only Context
+ * <p/>
+ * This version assumes it and all its subcontext are read-only and any attempt
+ * to modify (e.g. through bind) will result in an OperationNotSupportedException.
+ * Each Context in the tree builds a cache of the entries in all sub-contexts
+ * to optimise the performance of lookup.
+ * </p>
+ * <p>This implementation is intended to optimise the performance of lookup(String)
+ * to about the level of a HashMap get. It has been observed that the scheme
+ * resolution phase performed by the JVM takes considerably longer, so for
+ * optimum performance lookups should be coded like:</p>
+ * <code>
+ * Context componentContext = (Context)new InitialContext().lookup("java:comp");
+ * String envEntry = (String) componentContext.lookup("env/myEntry");
+ * String envEntry2 = (String) componentContext.lookup("env/myEntry2");
+ * </code>
+ */
+public class ReadOnlyContext implements Context, Serializable
+{
+ private static final long serialVersionUID = -5754338187296859149L;
+ protected static final NameParser nameParser = new NameParserImpl();
+
+ protected final Hashtable environment; // environment for this context
+ protected final Map bindings; // bindings at my level
+ protected final Map treeBindings; // all bindings under me
+
+ private boolean frozen = false;
+ private String nameInNamespace = "";
+ public static final String SEPARATOR = "/";
+
+ public ReadOnlyContext()
+ {
+ environment = new Hashtable();
+ bindings = new HashMap();
+ treeBindings = new HashMap();
+ }
+
+ public ReadOnlyContext(Hashtable env)
+ {
+ if (env == null)
+ {
+ this.environment = new Hashtable();
+ }
+ else
+ {
+ this.environment = new Hashtable(env);
+ }
+
+ this.bindings = Collections.EMPTY_MAP;
+ this.treeBindings = Collections.EMPTY_MAP;
+ }
+
+ public ReadOnlyContext(Hashtable environment, Map bindings)
+ {
+ if (environment == null)
+ {
+ this.environment = new Hashtable();
+ }
+ else
+ {
+ this.environment = new Hashtable(environment);
+ }
+
+ this.bindings = bindings;
+ treeBindings = new HashMap();
+ frozen = true;
+ }
+
+ public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace)
+ {
+ this(environment, bindings);
+ this.nameInNamespace = nameInNamespace;
+ }
+
+ protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env)
+ {
+ this.bindings = clone.bindings;
+ this.treeBindings = clone.treeBindings;
+ this.environment = new Hashtable(env);
+ }
+
+ protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace)
+ {
+ this(clone, env);
+ this.nameInNamespace = nameInNamespace;
+ }
+
+ public void freeze()
+ {
+ frozen = true;
+ }
+
+ boolean isFrozen()
+ {
+ return frozen;
+ }
+
+ /**
+ * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses.
+ * It binds every possible lookup into a map in each context. To do this, each context
+ * strips off one name segment and if necessary creates a new context for it. Then it asks that context
+ * to bind the remaining name. It returns a map containing all the bindings from the next context, plus
+ * the context it just created (if it in fact created it). (the names are suitably extended by the segment
+ * originally lopped off).
+ *
+ * @param name
+ * @param value
+ * @return
+ * @throws javax.naming.NamingException
+ */
+ protected Map internalBind(String name, Object value) throws NamingException
+ {
+ assert (name != null) && (name.length() > 0);
+ assert !frozen;
+
+ Map newBindings = new HashMap();
+ int pos = name.indexOf('/');
+ if (pos == -1)
+ {
+ if (treeBindings.put(name, value) != null)
+ {
+ throw new NamingException("Something already bound at " + name);
+ }
+
+ bindings.put(name, value);
+ newBindings.put(name, value);
+ }
+ else
+ {
+ String segment = name.substring(0, pos);
+ assert segment != null;
+ assert !segment.equals("");
+ Object o = treeBindings.get(segment);
+ if (o == null)
+ {
+ o = newContext();
+ treeBindings.put(segment, o);
+ bindings.put(segment, o);
+ newBindings.put(segment, o);
+ }
+ else if (!(o instanceof ReadOnlyContext))
+ {
+ throw new NamingException("Something already bound where a subcontext should go");
+ }
+
+ ReadOnlyContext readOnlyContext = (ReadOnlyContext) o;
+ String remainder = name.substring(pos + 1);
+ Map subBindings = readOnlyContext.internalBind(remainder, value);
+ for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String subName = segment + "/" + (String) entry.getKey();
+ Object bound = entry.getValue();
+ treeBindings.put(subName, bound);
+ newBindings.put(subName, bound);
+ }
+ }
+
+ return newBindings;
+ }
+
+ protected ReadOnlyContext newContext()
+ {
+ return new ReadOnlyContext();
+ }
+
+ public Object addToEnvironment(String propName, Object propVal) throws NamingException
+ {
+ return environment.put(propName, propVal);
+ }
+
+ public Hashtable getEnvironment() throws NamingException
+ {
+ return (Hashtable) environment.clone();
+ }
+
+ public Object removeFromEnvironment(String propName) throws NamingException
+ {
+ return environment.remove(propName);
+ }
+
+ public Object lookup(String name) throws NamingException
+ {
+ if (name.length() == 0)
+ {
+ return this;
+ }
+
+ Object result = treeBindings.get(name);
+ if (result == null)
+ {
+ result = bindings.get(name);
+ }
+
+ if (result == null)
+ {
+ int pos = name.indexOf(':');
+ if (pos > 0)
+ {
+ String scheme = name.substring(0, pos);
+ Context ctx = NamingManager.getURLContext(scheme, environment);
+ if (ctx == null)
+ {
+ throw new NamingException("scheme " + scheme + " not recognized");
+ }
+
+ return ctx.lookup(name);
+ }
+ else
+ {
+ // Split out the first name of the path
+ // and look for it in the bindings map.
+ CompositeName path = new CompositeName(name);
+
+ if (path.size() == 0)
+ {
+ return this;
+ }
+ else
+ {
+ String first = path.get(0);
+ Object obj = bindings.get(first);
+ if (obj == null)
+ {
+ throw new NameNotFoundException(name);
+ }
+ else if ((obj instanceof Context) && (path.size() > 1))
+ {
+ Context subContext = (Context) obj;
+ obj = subContext.lookup(path.getSuffix(1));
+ }
+
+ return obj;
+ }
+ }
+ }
+
+ if (result instanceof LinkRef)
+ {
+ LinkRef ref = (LinkRef) result;
+ result = lookup(ref.getLinkName());
+ }
+
+ if (result instanceof Reference)
+ {
+ try
+ {
+ result = NamingManager.getObjectInstance(result, null, null, this.environment);
+ }
+ catch (NamingException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw (NamingException) new NamingException("could not look up : " + name).initCause(e);
+ }
+ }
+
+ if (result instanceof ReadOnlyContext)
+ {
+ String prefix = getNameInNamespace();
+ if (prefix.length() > 0)
+ {
+ prefix = prefix + SEPARATOR;
+ }
+
+ result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name);
+ }
+
+ return result;
+ }
+
+ public Object lookup(Name name) throws NamingException
+ {
+ return lookup(name.toString());
+ }
+
+ public Object lookupLink(String name) throws NamingException
+ {
+ return lookup(name);
+ }
+
+ public Name composeName(Name name, Name prefix) throws NamingException
+ {
+ Name result = (Name) prefix.clone();
+ result.addAll(name);
+
+ return result;
+ }
+
+ public String composeName(String name, String prefix) throws NamingException
+ {
+ CompositeName result = new CompositeName(prefix);
+ result.addAll(new CompositeName(name));
+
+ return result.toString();
+ }
+
+ public NamingEnumeration list(String name) throws NamingException
+ {
+ Object o = lookup(name);
+ if (o == this)
+ {
+ return new ListEnumeration();
+ }
+ else if (o instanceof Context)
+ {
+ return ((Context) o).list("");
+ }
+ else
+ {
+ throw new NotContextException();
+ }
+ }
+
+ public NamingEnumeration listBindings(String name) throws NamingException
+ {
+ Object o = lookup(name);
+ if (o == this)
+ {
+ return new ListBindingEnumeration();
+ }
+ else if (o instanceof Context)
+ {
+ return ((Context) o).listBindings("");
+ }
+ else
+ {
+ throw new NotContextException();
+ }
+ }
+
+ public Object lookupLink(Name name) throws NamingException
+ {
+ return lookupLink(name.toString());
+ }
+
+ public NamingEnumeration list(Name name) throws NamingException
+ {
+ return list(name.toString());
+ }
+
+ public NamingEnumeration listBindings(Name name) throws NamingException
+ {
+ return listBindings(name.toString());
+ }
+
+ public void bind(Name name, Object obj) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void bind(String name, Object obj) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void close() throws NamingException
+ {
+ // ignore
+ }
+
+ public Context createSubcontext(Name name) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public Context createSubcontext(String name) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void destroySubcontext(Name name) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void destroySubcontext(String name) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public String getNameInNamespace() throws NamingException
+ {
+ return nameInNamespace;
+ }
+
+ public NameParser getNameParser(Name name) throws NamingException
+ {
+ return nameParser;
+ }
+
+ public NameParser getNameParser(String name) throws NamingException
+ {
+ return nameParser;
+ }
+
+ public void rebind(Name name, Object obj) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void rebind(String name, Object obj) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void rename(Name oldName, Name newName) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void rename(String oldName, String newName) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void unbind(Name name) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ public void unbind(String name) throws NamingException
+ {
+ throw new OperationNotSupportedException();
+ }
+
+ private abstract class LocalNamingEnumeration implements NamingEnumeration
+ {
+ private Iterator i = bindings.entrySet().iterator();
+
+ public boolean hasMore() throws NamingException
+ {
+ return i.hasNext();
+ }
+
+ public boolean hasMoreElements()
+ {
+ return i.hasNext();
+ }
+
+ protected Map.Entry getNext()
+ {
+ return (Map.Entry) i.next();
+ }
+
+ public void close() throws NamingException
+ { }
+ }
+
+ private class ListEnumeration extends LocalNamingEnumeration
+ {
+ public Object next() throws NamingException
+ {
+ return nextElement();
+ }
+
+ public Object nextElement()
+ {
+ Map.Entry entry = getNext();
+
+ return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName());
+ }
+ }
+
+ private class ListBindingEnumeration extends LocalNamingEnumeration
+ {
+ public Object next() throws NamingException
+ {
+ return nextElement();
+ }
+
+ public Object nextElement()
+ {
+ Map.Entry entry = getNext();
+
+ return new Binding((String) entry.getKey(), entry.getValue());
+ }
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java
new file mode 100644
index 0000000000..2c08f1e34a
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java
@@ -0,0 +1,185 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.IBMPerfTest;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.ConnectionFactory;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import java.io.File;
+import java.util.Hashtable;
+
+public class JNDIBindConnectionFactory
+{
+
+ public static final String CONNECTION_FACTORY_BINDING = "amq.ConnectionFactory";
+ public static final String DEFAULT_PROVIDER_FILE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "IBMPerfTestsJNDI";
+ public static final String PROVIDER_URL = "file://" + DEFAULT_PROVIDER_FILE_PATH;
+ public static final String FSCONTEXT_FACTORY = "com.sun.jndi.fscontext.RefFSContextFactory";
+ public static final String DEFAULT_CONNECTION_URL = "amqp://guest:guest@clientid/testpath?brokerlist='tcp://localhost:5672'";
+
+ private static void printUsage()
+ {
+ System.out.println("Using default values: Usage:java JNDIBindConnectionFactory <connection url> [<Connection Factory Binding>] [<Provider URL>] [<JNDI Context Factory>]");
+
+ }
+
+ public static void main(String[] args)
+ {
+ Logger.getRootLogger().setLevel(Level.OFF);
+
+ String connectionFactoryBinding = CONNECTION_FACTORY_BINDING;
+ String provider = PROVIDER_URL;
+ String contextFactory = FSCONTEXT_FACTORY;
+ if (args.length == 0)
+ {
+ printUsage();
+ System.exit(1);
+ }
+
+ String connectionURL = args[0];
+
+ System.out.println("Using Connection:" + connectionURL + "\n");
+
+
+ if (args.length > 1)
+ {
+ connectionFactoryBinding = args[1];
+
+ if (args.length > 2)
+ {
+ provider = args[2];
+
+ if (args.length > 3)
+ {
+ contextFactory = args[3];
+ }
+ }
+ else
+ {
+ System.out.println("Using default File System Context Factory");
+ System.out.println("Using default Connection Factory Binding:" + connectionFactoryBinding);
+ }
+ }
+ else
+ {
+ printUsage();
+ }
+
+
+ System.out.println("File System Context Factory\n" +
+ "Connection:" + connectionURL + "\n" +
+ "Connection Factory Binding:" + connectionFactoryBinding + "\n" +
+ "JNDI Provider URL:" + provider);
+
+ if (provider.startsWith("file"))
+ {
+ File file = new File(provider.substring(provider.indexOf("://") + 3));
+
+ if (file.exists() && !file.isDirectory())
+ {
+ System.out.println("Couldn't make directory file already exists");
+ System.exit(1);
+ }
+ else
+ {
+ if (!file.exists())
+ {
+ if (!file.mkdirs())
+ {
+ System.out.println("Couldn't make directory");
+ System.exit(1);
+ }
+ }
+ }
+ }
+
+ new JNDIBindConnectionFactory(provider, connectionFactoryBinding, contextFactory, connectionURL);
+
+ }
+
+ public JNDIBindConnectionFactory(String provider, String binding, String contextFactory, String CONNECTION_URL)
+ {
+ // Set up the environment for creating the initial context
+ Hashtable env = new Hashtable(11);
+ env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
+
+ env.put(Context.PROVIDER_URL, provider);
+
+ try
+ {
+ // Create the initial context
+ Context ctx = new InitialContext(env);
+
+ // Create the object to be bound
+ ConnectionFactory factory = null;
+
+ try
+ {
+ factory = new AMQConnectionFactory(CONNECTION_URL);
+
+
+ try
+ {
+ Object obj = ctx.lookup(binding);
+
+ if (obj != null)
+ {
+ System.out.println("Un-binding previous Connection Factory");
+ ctx.unbind(binding);
+ }
+ }
+ catch (NamingException e)
+ {
+ System.out.println("Operation failed: " + e);
+ }
+
+ // Perform the bind
+ ctx.bind(binding, factory);
+ System.out.println("Bound Connection Factory:" + binding);
+
+ // Check that it is bound
+ Object obj = ctx.lookup(binding);
+ System.out.println("Connection URL:" + ((AMQConnectionFactory) obj).getConnectionURL());
+
+ System.out.println("JNDI FS Context:" + provider);
+ }
+ catch (NamingException amqe)
+ {
+ System.out.println("Operation failed: " + amqe);
+ }
+ catch (URLSyntaxException e)
+ {
+ System.out.println("Operation failed: " + e);
+ }
+
+ }
+ catch (NamingException e)
+ {
+ System.out.println("Operation failed: " + e);
+ }
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java
new file mode 100644
index 0000000000..10e8b94311
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java
@@ -0,0 +1,213 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.IBMPerfTest;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import java.io.File;
+import java.util.Hashtable;
+
+public class JNDIBindQueue
+{
+ public static final String DEFAULT_PROVIDER_FILE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "IBMPerfTestsJNDI";
+ public static final String PROVIDER_URL = "file://" + DEFAULT_PROVIDER_FILE_PATH;
+ public static final String FSCONTEXT_FACTORY = "com.sun.jndi.fscontext.RefFSContextFactory";
+
+ Connection _connection = null;
+ Context _ctx = null;
+
+
+ public JNDIBindQueue(String queueBinding, String queueName, String provider, String contextFactory)
+ {
+ // Set up the environment for creating the initial context
+ Hashtable env = new Hashtable(11);
+ env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
+
+ env.put(Context.PROVIDER_URL, provider);
+
+ try
+ {
+ // Create the initial context
+ _ctx = new InitialContext(env);
+
+ // Create the object to be bound
+
+ try
+ {
+ _connection = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'");
+ System.out.println("Connected");
+ }
+ catch (Exception amqe)
+ {
+ System.out.println("Unable to create AMQConnectionFactory:" + amqe);
+ }
+
+ if (_connection != null)
+ {
+ bindQueue(queueName, queueBinding);
+ }
+
+ // Check that it is bound
+ Object obj = _ctx.lookup(queueBinding);
+
+ System.out.println("Bound Queue:" + ((AMQQueue) obj).toURL());
+
+ System.out.println("JNDI FS Context:" + provider);
+
+ }
+ catch (NamingException e)
+ {
+ System.out.println("Operation failed: " + e);
+ }
+ finally
+ {
+ try
+ {
+ if (_connection != null)
+ {
+ _connection.close();
+ }
+ }
+ catch (JMSException closeE)
+ {
+ System.out.println("Connection closing failed: " + closeE);
+ }
+ }
+
+
+ }
+
+
+ private void bindQueue(String queueName, String queueBinding) throws NamingException
+ {
+
+ try
+ {
+ Object obj = _ctx.lookup(queueBinding);
+
+ if (obj != null)
+ {
+ System.out.println("Un-binding exisiting object");
+ _ctx.unbind(queueBinding);
+ }
+ }
+ catch (NamingException e)
+ {
+
+ }
+
+ Queue queue = null;
+ try
+ {
+
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ if (session != null)
+ {
+ queue = ((AMQSession) session).createQueue(queueName);
+ }
+ }
+ catch (JMSException jmse)
+ {
+ System.out.println("Unable to create Queue:" + jmse);
+ }
+
+ // Perform the bind
+ _ctx.bind(queueBinding, queue);
+ }
+
+
+ public static void main(String[] args)
+ {
+ Logger.getRootLogger().setLevel(Level.OFF);
+
+ String provider = JNDIBindQueue.PROVIDER_URL;
+ String contextFactory = JNDIBindQueue.FSCONTEXT_FACTORY;
+
+ if (args.length > 1)
+ {
+ String binding = args[0];
+ String queueName = args[1];
+
+ if (args.length > 2)
+ {
+ provider = args[2];
+
+ if (args.length > 3)
+ {
+ contextFactory = args[3];
+ }
+ }
+ else
+ {
+ System.out.println("Using default File System Context Factory");
+ }
+
+ System.out.println("File System Context Factory\n" +
+ "Binding Queue:'" + queueName + "' to '" + binding + "'\n" +
+ "JNDI Provider URL:" + provider);
+
+ if (provider.startsWith("file"))
+ {
+ File file = new File(provider.substring(provider.indexOf("://") + 3));
+
+ if (file.exists() && !file.isDirectory())
+ {
+ System.out.println("Couldn't make directory file already exists");
+ System.exit(1);
+ }
+ else
+ {
+ if (!file.exists())
+ {
+ if (!file.mkdirs())
+ {
+ System.out.println("Couldn't make directory");
+ System.exit(1);
+ }
+ }
+ }
+ }
+
+
+ new JNDIBindQueue(binding, queueName, provider, contextFactory);
+
+ }
+ else
+ {
+ System.out.println("Using Defaults: Usage:java JNDIBindQueue <Binding> <queue name> [<Provider URL> [<JNDI Context Factory>]]");
+ }
+
+ }
+
+
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java
new file mode 100644
index 0000000000..ca071c1187
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java
@@ -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.
+ *
+ */
+package org.apache.qpid.IBMPerfTest;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import java.io.File;
+import java.util.Hashtable;
+
+public class JNDIBindTopic
+{
+ public static final String DEFAULT_PROVIDER_FILE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "IBMPerfTestsJNDI";
+ public static final String PROVIDER_URL = "file://" + DEFAULT_PROVIDER_FILE_PATH;
+
+ public static final String FSCONTEXT_FACTORY = "com.sun.jndi.fscontext.RefFSContextFactory";
+
+ Connection _connection = null;
+ Context _ctx = null;
+
+
+ public JNDIBindTopic(String topicBinding, String topicName, String provider, String contextFactory)
+ {
+ // Set up the environment for creating the initial context
+ Hashtable env = new Hashtable(11);
+ env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
+
+ env.put(Context.PROVIDER_URL, provider);
+
+ try
+ {
+ // Create the initial context
+ _ctx = new InitialContext(env);
+
+ // Create the object to be bound
+
+ try
+ {
+ _connection = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'");
+ System.out.println("Connected");
+ }
+ catch (Exception amqe)
+ {
+ System.out.println("Unable to create AMQConnectionFactory:" + amqe);
+ }
+
+ if (_connection != null)
+ {
+ bindTopic(topicName, topicBinding);
+ }
+
+ // Check that it is bound
+ Object obj = _ctx.lookup(topicBinding);
+
+ System.out.println("Bound Queue:" + ((AMQTopic) obj).toURL());
+
+ System.out.println("JNDI FS Context:" + provider);
+
+ }
+ catch (NamingException e)
+ {
+ System.out.println("Operation failed: " + e);
+ }
+ finally
+ {
+ try
+ {
+ if (_connection != null)
+ {
+ _connection.close();
+ }
+ }
+ catch (JMSException closeE)
+ {
+ System.out.println("Operation failed: " + closeE);
+ }
+ }
+ }
+
+
+ private void bindTopic(String topicName, String topicBinding) throws NamingException
+ {
+
+ try
+ {
+ Object obj = _ctx.lookup(topicBinding);
+
+ if (obj != null)
+ {
+ System.out.println("Un-binding exisiting object");
+ _ctx.unbind(topicBinding);
+ }
+ }
+ catch (NamingException e)
+ {
+
+ }
+
+ Topic topic = null;
+ try
+ {
+
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ if (session != null)
+ {
+ topic = ((AMQSession) session).createTopic(topicName);
+ }
+ }
+ catch (JMSException jmse)
+ {
+ System.out.println("Unable to create Topic:" + jmse);
+ }
+
+ // Perform the bind
+ _ctx.bind(topicBinding, topic);
+ }
+
+
+ public static void main(String[] args)
+ {
+ Logger.getRootLogger().setLevel(Level.OFF);
+
+ String provider = JNDIBindTopic.PROVIDER_URL;
+ String contextFactory = JNDIBindTopic.FSCONTEXT_FACTORY;
+
+ if (args.length > 1)
+ {
+ String binding = args[0];
+ String queueName = args[1];
+
+ if (args.length > 2)
+ {
+ provider = args[2];
+
+ if (args.length > 3)
+ {
+ contextFactory = args[3];
+ }
+ }
+ else
+ {
+ System.out.println("Using default File System Context Factory");
+ }
+
+ System.out.println("File System Context Factory\n" +
+ "Binding Topic:'" + queueName + "' to '" + binding + "'\n" +
+ "JNDI Provider URL:" + provider);
+
+
+ if (provider.startsWith("file"))
+ {
+ File file = new File(provider.substring(provider.indexOf("://") + 3));
+
+ if (file.exists() && !file.isDirectory())
+ {
+ System.out.println("Couldn't make directory file already exists");
+ System.exit(1);
+ }
+ else
+ {
+ if (!file.exists())
+ {
+ if (!file.mkdirs())
+ {
+ System.out.println("Couldn't make directory");
+ System.exit(1);
+ }
+ }
+ }
+ }
+
+ new JNDIBindTopic(binding, queueName, provider, contextFactory);
+
+ }
+ else
+ {
+ System.out.println("Usage:java JNDIBindTopic <Binding> <topic name> [<Provider URL> [<JNDI Context Factory>]]");
+ }
+
+ }
+
+
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt
new file mode 100644
index 0000000000..95ee9f9c77
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt
@@ -0,0 +1,11 @@
+These JNDI setup tools are mainly for use in conjunction with the IBM JMS Performance Harness available here:
+The jar should be placed in the client/test/lib/ directory.
+
+http://www.alphaworks.ibm.com/tech/perfharness
+
+
+These JNDI classes use the the SUN FileSystem context.
+There are two jar files that should be placed in your client/test/lib directory.
+
+http://javashoplm.sun.com/ECom/docs/Welcome.jsp?StoreId=22&PartDetailId=7110-jndi-1.2.1-oth-JPR&SiteId=JSC&TransactionId=noreg
+
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java b/Final/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java
new file mode 100644
index 0000000000..cf8059a143
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java
@@ -0,0 +1,129 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.cluster;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.MessageListener;
+import javax.jms.Message;
+import javax.jms.Session;
+import javax.jms.JMSException;
+import javax.jms.MessageProducer;
+import javax.jms.TextMessage;
+import java.util.Random;
+
+public class Client
+{
+ private final Random random = new Random();
+ private final String name;
+ private final Session session;
+ private final MessageProducer topicProducer;
+ private final MessageProducer queueProducer;
+
+ Client(AMQConnection connection, String name) throws JMSException, InterruptedException
+ {
+ this.name = name;
+ session = connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ AMQTopic topic = new AMQTopic(((AMQSession)session).getDefaultTopicExchangeName(), new AMQShortString("cluster_test_topic"));
+ AMQQueue queue = new AMQQueue(((AMQSession)session).getDefaultQueueExchangeName(), new AMQShortString("cluster_test_queue"));
+
+ topicProducer = session.createProducer(topic);
+ queueProducer = session.createProducer(queue);
+
+ //subscribe to a known topic
+ session.createConsumer(topic).setMessageListener(new TopicHandler());
+ //subscribe to a known queue
+ session.createConsumer(queue).setMessageListener(new QueueHandler());
+
+ connection.start();
+
+ while(true)
+ {
+ Thread.sleep(random.nextInt(60000));
+ sendToQueue(name + ":" + randomString(5));
+ }
+ }
+
+ private synchronized void sendToTopic(String message) throws JMSException
+ {
+ topicProducer.send(session.createTextMessage(message));
+ }
+
+ private synchronized void sendToQueue(String message) throws JMSException
+ {
+ queueProducer.send(session.createTextMessage(message));
+ }
+
+ private String randomString(int length){
+ char[] c = new char[length];
+ for(int i = 0; i < length; i++)
+ {
+ c[i] = (char) ('A' + random.nextInt(26));
+ }
+ return new String(c);
+ }
+
+ private class QueueHandler implements MessageListener
+ {
+ public void onMessage(Message message)
+ {
+ try
+ {
+ sendToTopic(((TextMessage) message).getText());
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private class TopicHandler implements MessageListener
+ {
+ public void onMessage(Message message)
+ {
+ try
+ {
+ System.out.println(((TextMessage) message).getText());
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static void main(String[] argv) throws AMQException, JMSException, InterruptedException, URLSyntaxException
+ {
+ //assume args describe the set of brokers to try
+
+ String clientName = argv.length > 1 ? argv[1] : "testClient";
+ new Client(new AMQConnection(argv.length > 0 ? argv[0] : "vm://:1", "guest", "guest", clientName, "/test"), clientName);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java
new file mode 100644
index 0000000000..1db7e200bd
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java
@@ -0,0 +1,277 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.codec;
+
+import org.apache.qpid.framing.*;
+import org.apache.mina.common.*;
+import org.apache.mina.common.support.BaseIoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+import java.net.SocketAddress;
+
+/**
+ */
+public class BasicDeliverTest
+{
+ public static void main(String[] argv) throws Exception
+ {
+ BasicDeliverTest test = new BasicDeliverTest();
+
+ //warm up:
+ test.encode(512, 100000);
+
+ //real tests:
+ test.encode(16, 10000, 15);
+ test.encode(32, 10000, 15);
+ test.encode(64, 10000, 15);
+ test.encode(128, 10000, 15);
+ test.encode(256, 10000, 15);
+ test.encode(512, 10000, 15);
+ test.encode(1024, 10000, 15);
+ test.encode(2048, 10000, 15);
+
+ test.decode(16, 10000, 15);
+ test.decode(32, 10000, 15);
+ test.decode(64, 10000, 15);
+ test.decode(128, 10000, 15);
+ test.decode(256, 10000, 15);
+ test.decode(512, 10000, 15);
+ test.decode(1024, 10000, 15);
+ test.decode(2048, 10000, 15);
+ }
+
+ void decode(int size, int count, int iterations) throws Exception
+ {
+ long min = Long.MAX_VALUE;
+ long max = 0;
+ long total = 0;
+ for (int i = 0; i < iterations; i++)
+ {
+ long time = decode(size, count);
+ total += time;
+ if (time < min)
+ {
+ min = time;
+ }
+ if (time > max)
+ {
+ max = time;
+ }
+ }
+ System.out.println("Decoded " + count + " messages of " + size +
+ " bytes: avg=" + (total / iterations) + ", min=" + min + ", max=" + max);
+ }
+
+
+ long decode(int size, int count) throws Exception
+ {
+ AMQDataBlock block = getDataBlock(size);
+ ByteBuffer data = ByteBuffer.allocate((int) block.getSize()); // XXX: Is cast a problem?
+ block.writePayload(data);
+ data.flip();
+ AMQDecoder decoder = new AMQDecoder(false);
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < count; i++)
+ {
+ decoder.decode(session, data, decoderOutput);
+ data.rewind();
+ }
+ return System.currentTimeMillis() - start;
+ }
+
+ void encode(int size, int count, int iterations) throws Exception
+ {
+ long min = Long.MAX_VALUE;
+ long max = 0;
+ long total = 0;
+ for (int i = 0; i < iterations; i++)
+ {
+ long time = encode(size, count);
+ total += time;
+ if (time < min)
+ {
+ min = time;
+ }
+ if (time > max)
+ {
+ max = time;
+ }
+ }
+ System.out.println("Encoded " + count + " messages of " + size +
+ " bytes: avg=" + (total / iterations) + ", min=" + min + ", max=" + max);
+ }
+
+ long encode(int size, int count) throws Exception
+ {
+ IoSession session = null;
+ AMQDataBlock block = getDataBlock(size);
+ AMQEncoder encoder = new AMQEncoder();
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < count; i++)
+ {
+ encoder.encode(session, block, encoderOutput);
+ }
+ return System.currentTimeMillis() - start;
+ }
+
+ private final ProtocolEncoderOutput encoderOutput = new ProtocolEncoderOutput()
+ {
+
+ public void write(ByteBuffer byteBuffer)
+ {
+ }
+
+ public void mergeAll()
+ {
+ }
+
+ public WriteFuture flush()
+ {
+ return null;
+ }
+ };
+
+ private final ProtocolDecoderOutput decoderOutput = new ProtocolDecoderOutput()
+ {
+ public void write(Object object)
+ {
+ }
+
+ public void flush()
+ {
+ }
+ };
+
+ private final IoSession session = new BaseIoSession()
+ {
+
+ protected void updateTrafficMask()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoService getService()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoServiceConfig getServiceConfig()
+ {
+ return null;
+ }
+
+ public IoHandler getHandler()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoSessionConfig getConfig()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoFilterChain getFilterChain()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public TransportType getTransportType()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getRemoteAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getLocalAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getServiceAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getScheduledWriteRequests()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getScheduledWriteBytes()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+ };
+
+ private static final char[] DATA = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
+
+ static CompositeAMQDataBlock getDataBlock(int size)
+ {
+ //create a frame representing message delivery
+ AMQFrame[] frames = new AMQFrame[3];
+ frames[0] = wrapBody(createBasicDeliverBody());
+ frames[1] = wrapBody(createContentHeaderBody());
+ frames[2] = wrapBody(createContentBody(size));
+
+ return new CompositeAMQDataBlock(frames);
+ }
+
+ static AMQFrame wrapBody(AMQBody body)
+ {
+ AMQFrame frame = new AMQFrame(1, body);
+ return frame;
+ }
+
+ static ContentBody createContentBody(int size)
+ {
+ ContentBody body = new ContentBody();
+ body.payload = ByteBuffer.allocate(size);
+ for (int i = 0; i < size; i++)
+ {
+ body.payload.put((byte) DATA[i % DATA.length]);
+ }
+ return body;
+ }
+
+ static ContentHeaderBody createContentHeaderBody()
+ {
+ ContentHeaderBody body = new ContentHeaderBody();
+ body.properties = new BasicContentHeaderProperties();
+ body.weight = 1;
+ body.classId = 6;
+ return body;
+ }
+
+ static BasicDeliverBody createBasicDeliverBody()
+ {
+ BasicDeliverBody body = new BasicDeliverBody((byte) 8, (byte) 0,
+ BasicDeliverBody.getClazz((byte) 8, (byte) 0),
+ BasicDeliverBody.getMethod((byte) 8, (byte) 0),
+ new AMQShortString("myConsumerTag"), 1,
+ new AMQShortString("myExchange"), false,
+ new AMQShortString("myRoutingKey"));
+ return body;
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/codec/Client.java b/Final/java/client/src/old_test/java/org/apache/qpid/codec/Client.java
new file mode 100644
index 0000000000..3886021277
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/codec/Client.java
@@ -0,0 +1,133 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.codec;
+
+import org.apache.mina.transport.socket.nio.SocketConnector;
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.framing.ContentBody;
+
+import java.net.InetSocketAddress;
+
+public class Client extends IoHandlerAdapter
+{
+ //private static final int[] DEFAULT_SIZES = new int[]{1024, 512, 256, 128, 56};
+ //private static final int[] DEFAULT_SIZES = new int[]{256, 256, 256, 256, 256, 512, 512, 512, 512, 512};
+ private static final int[] DEFAULT_SIZES = new int[]{256, 512, 256, 512, 256, 512, 256, 512, 256, 512};
+ //private static final int[] DEFAULT_SIZES = new int[]{1024, 1024, 1024, 1024, 1024};
+
+ private final IoSession _session;
+ private final long _start;
+ private final int _size;
+ private final int _count;
+ private int _received;
+ private boolean _closed;
+
+ Client(String host, int port, int size, int count) throws Exception
+ {
+ _count = count;
+ _size = size;
+ AMQDataBlock block = BasicDeliverTest.getDataBlock(size);
+
+ InetSocketAddress address = new InetSocketAddress(host, port);
+ ConnectFuture future = new SocketConnector().connect(address, this);
+ future.join();
+ _session = future.getSession();
+
+ _start = System.currentTimeMillis();
+ for(int i = 0; i < count; i++)
+ {
+ _session.write(block);
+ }
+ }
+
+ void close()
+ {
+ long time = System.currentTimeMillis() - _start;
+ System.out.println("Received " + _received + " messages of " + _size
+ + " bytes in " + time + "ms.");
+ _session.close();
+ synchronized(this)
+ {
+ _closed = true;
+ notify();
+ }
+ }
+
+ void waitForClose() throws InterruptedException
+ {
+ synchronized(this)
+ {
+ while(!_closed)
+ {
+ wait();
+ }
+ }
+ }
+
+ public void sessionCreated(IoSession session) throws Exception
+ {
+ session.getFilterChain().addLast("protocolFilter", new ProtocolCodecFilter(new AMQCodecFactory(false)));
+ }
+
+ public void messageReceived(IoSession session, Object object) throws Exception
+ {
+ if(isContent(object) && ++_received == _count) close();
+ }
+
+ public void exceptionCaught(IoSession session, Throwable throwable) throws Exception
+ {
+ throwable.printStackTrace();
+ close();
+ }
+
+ private static boolean isDeliver(Object o)
+ {
+ return o instanceof AMQFrame && ((AMQFrame) o).getBodyFrame() instanceof BasicDeliverBody;
+ }
+
+ private static boolean isContent(Object o)
+ {
+ return o instanceof AMQFrame && ((AMQFrame) o).getBodyFrame() instanceof ContentBody;
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ String host = argv.length > 0 ? argv[0] : "localhost";
+ int port = argv.length > 1 ? Integer.parseInt(argv[1]) : 8888;
+ int count = argv.length > 2 ? Integer.parseInt(argv[2]) : 10000;
+ int[] sizes = argv.length > 3 ? new int[]{Integer.parseInt(argv[3])} : DEFAULT_SIZES;
+
+ System.out.println("Connecting to " + host + ":" + port);
+
+ for(int i = 0; i < sizes.length; i++)
+ {
+ new Client(host, port, sizes[i], count).waitForClose();
+ Thread.sleep(1000);
+ }
+ }
+
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/codec/Server.java b/Final/java/client/src/old_test/java/org/apache/qpid/codec/Server.java
new file mode 100644
index 0000000000..fa4295e0b2
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/codec/Server.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.codec;
+
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.transport.socket.nio.SocketAcceptor;
+import org.apache.mina.util.SessionUtil;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.CompositeAMQDataBlock;
+
+import java.net.InetSocketAddress;
+
+public class Server extends IoHandlerAdapter
+{
+ Server(int port) throws Exception
+ {
+ new SocketAcceptor().bind(new InetSocketAddress(port), this);
+ System.out.println("Listening on " + port);
+ }
+
+ public void sessionCreated(IoSession session) throws Exception
+ {
+ SessionUtil.initialize(session);
+ session.getFilterChain().addLast("protocolFilter", new ProtocolCodecFilter(new AMQCodecFactory(false)));
+ }
+
+ public void messageReceived(IoSession session, Object object) throws Exception
+ {
+ getAccumulator(session).received(session, (AMQFrame) object);
+ }
+
+ public void sessionOpened(IoSession session) throws Exception
+ {
+ System.out.println("sessionOpened()");
+ }
+
+ public void sessionClosed(IoSession session) throws Exception
+ {
+ System.out.println("sessionClosed()");
+ }
+
+ public void exceptionCaught(IoSession session, Throwable t) throws Exception
+ {
+ System.out.println("exceptionCaught()");
+ t.printStackTrace();
+ session.close();
+ }
+
+ private Accumulator getAccumulator(IoSession session)
+ {
+ Accumulator a = (Accumulator) session.getAttribute(ACCUMULATOR);
+ if(a == null)
+ {
+ a = new Accumulator();
+ session.setAttribute(ACCUMULATOR, a);
+ }
+ return a;
+ }
+
+ private static final String ACCUMULATOR = Accumulator.class.getName();
+
+ private static class Accumulator
+ {
+ private final AMQFrame[] frames = new AMQFrame[3];
+ private int i;
+
+ void received(IoSession session, AMQFrame frame)
+ {
+ frames[i++] = frame;
+ if(i >= frames.length)
+ {
+ i = 0;
+ session.write(new CompositeAMQDataBlock(frames));
+ }
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ int port = argv.length > 0 ? Integer.parseInt(argv[0]) : 8888;
+ new Server(port);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java b/Final/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java
new file mode 100644
index 0000000000..cac0064785
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.config.ConnectionFactoryInitialiser;
+import org.apache.qpid.config.ConnectorConfig;
+
+import javax.jms.ConnectionFactory;
+
+class AMQConnectionFactoryInitialiser implements ConnectionFactoryInitialiser
+{
+ public ConnectionFactory getFactory(ConnectorConfig config)
+ {
+ return new AMQConnectionFactory(config.getHost(), config.getPort(), "/test_path");
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java b/Final/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java
new file mode 100644
index 0000000000..04381d66a0
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java
@@ -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.
+ *
+ */
+package org.apache.qpid.config;
+
+public abstract class AbstractConfig
+{
+ public boolean setOptions(String[] argv)
+ {
+ try
+ {
+ for(int i = 0; i < argv.length - 1; i += 2)
+ {
+ String key = argv[i];
+ String value = argv[i+1];
+ setOption(key, value);
+ }
+ return true;
+ }
+ catch(Exception e)
+ {
+ System.out.println(e.getMessage());
+ }
+ return false;
+ }
+
+ protected int parseInt(String msg, String i)
+ {
+ try
+ {
+ return Integer.parseInt(i);
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException(msg + ": " + i);
+ }
+ }
+
+ protected long parseLong(String msg, String i)
+ {
+ try
+ {
+ return Long.parseLong(i);
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException(msg + ": " + i);
+ }
+ }
+
+ public abstract void setOption(String key, String value);
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java b/Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java
new file mode 100644
index 0000000000..a9984eb09a
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+
+public interface ConnectionFactoryInitialiser
+{
+ public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException;
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/config/Connector.java b/Final/java/client/src/old_test/java/org/apache/qpid/config/Connector.java
new file mode 100644
index 0000000000..ff2377f087
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/config/Connector.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+
+public class Connector
+{
+ public Connection createConnection(ConnectorConfig config) throws Exception
+ {
+ return getConnectionFactory(config).createConnection();
+ }
+
+ ConnectionFactory getConnectionFactory(ConnectorConfig config) throws Exception
+ {
+ String factory = config.getFactory();
+ if(factory == null) factory = AMQConnectionFactoryInitialiser.class.getName();
+ System.out.println("Using " + factory);
+ return ((ConnectionFactoryInitialiser) Class.forName(factory).newInstance()).getFactory(config);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java b/Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java
new file mode 100644
index 0000000000..b120ed3f12
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+public interface ConnectorConfig
+{
+ public String getHost();
+ public int getPort();
+ public String getFactory();
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java b/Final/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java
new file mode 100644
index 0000000000..44285efd96
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.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.config;
+
+import org.apache.qpid.config.ConnectionFactoryInitialiser;
+import org.apache.qpid.config.ConnectorConfig;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.MBeanException;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.NameNotFoundException;
+import java.util.Hashtable;
+
+public class JBossConnectionFactoryInitialiser implements ConnectionFactoryInitialiser
+{
+ public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException
+ {
+ ConnectionFactory cf = null;
+ InitialContext ic = null;
+ Hashtable ht = new Hashtable();
+ ht.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
+ String jbossHost = System.getProperty("jboss.host", "eqd-lxamq01");
+ String jbossPort = System.getProperty("jboss.port", "1099");
+ ht.put(InitialContext.PROVIDER_URL, "jnp://" + jbossHost + ":" + jbossPort);
+ ht.put(InitialContext.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
+
+ try
+ {
+ ic = new InitialContext(ht);
+ if (!doesDestinationExist("topictest.messages", ic))
+ {
+ deployTopic("topictest.messages", ic);
+ }
+ if (!doesDestinationExist("topictest.control", ic))
+ {
+ deployTopic("topictest.control", ic);
+ }
+
+ cf = (ConnectionFactory) ic.lookup("/ConnectionFactory");
+ return cf;
+ }
+ catch (NamingException e)
+ {
+ throw new JMSException("Unable to lookup object: " + e);
+ }
+ catch (Exception e)
+ {
+ throw new JMSException("Error creating topic: " + e);
+ }
+ }
+
+ private boolean doesDestinationExist(String name, InitialContext ic) throws Exception
+ {
+ try
+ {
+ ic.lookup("/" + name);
+ }
+ catch (NameNotFoundException e)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private void deployTopic(String name, InitialContext ic) throws Exception
+ {
+ MBeanServerConnection mBeanServer = lookupMBeanServerProxy(ic);
+
+ ObjectName serverObjectName = new ObjectName("jboss.messaging:service=ServerPeer");
+
+ String jndiName = "/" + name;
+ try
+ {
+ mBeanServer.invoke(serverObjectName, "createTopic",
+ new Object[]{name, jndiName},
+ new String[]{"java.lang.String", "java.lang.String"});
+ }
+ catch (MBeanException e)
+ {
+ System.err.println("Error: " + e);
+ System.err.println("Cause: " + e.getCause());
+ }
+ }
+
+ private MBeanServerConnection lookupMBeanServerProxy(InitialContext ic) throws NamingException
+ {
+ return (MBeanServerConnection) ic.lookup("jmx/invoker/RMIAdaptor");
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java
new file mode 100644
index 0000000000..cb8adae18c
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java
@@ -0,0 +1,112 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.flow;
+
+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.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+
+public class ChannelFlowTest implements MessageListener
+{
+ private int sent;
+ private int received;
+
+ ChannelFlowTest(String broker) throws Exception
+ {
+ this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "/test"));
+ }
+
+ ChannelFlowTest(AMQConnection connection) throws Exception
+ {
+ this(connection, new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("ChannelFlowTest")), true));
+ }
+
+ ChannelFlowTest(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ AMQSession session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE, 50,25);
+
+ //set up a slow consumer
+ session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+
+ //create a publisher
+ MessageProducer producer = session.createProducer(destination);
+ Message msg = session.createTextMessage("Message");
+
+ //publish in bursts that are fast enough to cause channel flow control
+ for(int i = 0; i < 10; i++)
+ {
+ for(int j = 0; j < 100; j++)
+ {
+ producer.send(msg);
+ sent++;
+ }
+ waitUntilReceived(sent - 40);
+ }
+
+ waitUntilReceived(sent);
+
+ session.close();
+ connection.close();
+ }
+
+
+ private synchronized void waitUntilReceived(int count) throws InterruptedException
+ {
+ while(received <count)
+ {
+ wait();
+ }
+ }
+
+ public synchronized void onMessage(Message message)
+ {
+ try
+ {
+ Thread.sleep(50);
+
+ received++;
+ notify();
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ new ChannelFlowTest(argv.length == 0 ? "localhost:5672" : argv[0]);
+ }
+
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java b/Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java
new file mode 100644
index 0000000000..a246352d8b
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java
@@ -0,0 +1,196 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.fragmentation;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.jms.MessageProducer;
+import org.apache.qpid.jms.Session;
+import org.apache.log4j.Logger;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A client that behaves as follows:
+ * <ul><li>Connects to a queue, whose name is specified as a cmd-line argument</li>
+ * <li>Creates a temporary queue</li>
+ * <li>Creates messages containing a property that is the name of the temporary queue</li>
+ * <li>Fires off a message on the original queue and waits for a response on the temporary queue</li>
+ * </ul>
+ */
+public class TestLargePublisher
+{
+ private static final Logger _log = Logger.getLogger(TestLargePublisher.class);
+
+ private AMQConnection _connection;
+
+ private AMQSession _session;
+
+ private class CallbackHandler implements MessageListener
+ {
+ private int _expectedMessageCount;
+
+ private int _actualMessageCount;
+
+ private long _startTime;
+
+ public CallbackHandler(int expectedMessageCount, long startTime)
+ {
+ _expectedMessageCount = expectedMessageCount;
+ _startTime = startTime;
+ }
+
+ public void onMessage(Message m)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Message received: " + m);
+ }
+ _actualMessageCount++;
+ if (_actualMessageCount%1000 == 0)
+ {
+ _log.info("Received message count: " + _actualMessageCount);
+ }
+ /*if (!"henson".equals(m.toString()))
+ {
+ _log.error("AbstractJMSMessage response not correct: expected 'henson' but got " + m.toString());
+ }
+ else
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("AbstractJMSMessage " + m + " received");
+ }
+ else
+ {
+ _log.info("AbstractJMSMessage received");
+ }
+ } */
+
+ if (_actualMessageCount == _expectedMessageCount)
+ {
+ long timeTaken = System.currentTimeMillis() - _startTime;
+ System.out.println("Total time taken to receive " + _expectedMessageCount+ " messages was " +
+ timeTaken + "ms, equivalent to " +
+ (_expectedMessageCount/(timeTaken/1000.0)) + " messages per second");
+ }
+ }
+ }
+
+ public TestLargePublisher(String host, int port, String clientID,
+ final int messageCount) throws AMQException,URLSyntaxException
+ {
+ try
+ {
+ createConnection(host, port, clientID);
+
+ _session = (AMQSession) _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic destination = new AMQTopic(_session.getDefaultTopicExchangeName(), new AMQShortString("large"));
+ MessageProducer producer = (MessageProducer) _session.createProducer(destination);
+
+ _connection.start();
+ //TextMessage msg = _session.createTextMessage(tempDestination.getQueueName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths");
+ final long startTime = System.currentTimeMillis();
+
+ for (int i = 0; i < messageCount; i++)
+ {
+ BytesMessage msg = _session.createBytesMessage();
+ populateMessage(msg);
+ producer.send(msg);
+ }
+ _log.info("Finished sending " + messageCount + " messages");
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void createConnection(String host, int port, String clientID) throws AMQException , URLSyntaxException
+ {
+ _connection = new AMQConnection(host, port, "guest", "guest",
+ clientID, "/test");
+ }
+
+ private void populateMessage(BytesMessage msg) throws JMSException
+ {
+ int size = 1024 * 187; // 187k
+ byte[] data = new byte[size];
+ for (int i = 0; i < data.length; i++)
+ {
+ data[i] = (byte)(i%25);
+ }
+ msg.writeBytes(data);
+ }
+
+ /**
+ *
+ * @param args argument 1 if present specifies the name of the temporary queue to create. Leaving it blank
+ * means the server will allocate a name.
+ */
+ public static void main(String[] args) throws URLSyntaxException
+ {
+ final String host;
+ final int port;
+ final int numMessages;
+ if (args.length == 0)
+ {
+ host = "localhost";
+ port = 5672;
+ numMessages = 100;
+// System.err.println("Usage: TestLargePublisher <host> <port> <number of messages>");
+ }
+ else
+ {
+ host = args[0];
+ port = Integer.parseInt(args[1]);
+ numMessages = Integer.parseInt(args[2]);
+ }
+
+ try
+ {
+ InetAddress address = InetAddress.getLocalHost();
+ String clientID = address.getHostName() + System.currentTimeMillis();
+ TestLargePublisher client = new TestLargePublisher(host, port, clientID, numMessages);
+ }
+ catch (UnknownHostException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ catch (AMQException e)
+ {
+ System.err.println("Error in client: " + e);
+ e.printStackTrace();
+ }
+
+ //System.exit(0);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java b/Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java
new file mode 100644
index 0000000000..b0cde22349
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java
@@ -0,0 +1,167 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.fragmentation;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.log4j.Logger;
+
+import javax.jms.*;
+import java.net.InetAddress;
+
+public class TestLargeSubscriber
+{
+ private static final Logger _logger = Logger.getLogger(TestLargeSubscriber.class);
+
+ private static MessageProducer _destinationProducer;
+
+ private static String _destinationName;
+
+ public static void main(String[] args)
+ {
+ _logger.info("Starting...");
+
+ final String host;
+ final int port;
+ final String username;
+ final String password;
+ final String virtualPath;
+ final int numExpectedMessages;
+ if (args.length == 0)
+ {
+ host = "localhost";
+ port = 5672;
+ username = "guest";
+ password = "guest";
+ virtualPath = "/test";
+ numExpectedMessages = 100;
+ }
+ else if (args.length == 6)
+ {
+ host = args[0];
+ port = Integer.parseInt(args[1]);
+ username = args[2];
+ password = args[3];
+ virtualPath = args[4];
+ numExpectedMessages = Integer.parseInt(args[5]);
+ }
+ else
+ {
+ System.out.println("Usage: host port username password virtual-path expectedMessageCount");
+ System.exit(1);
+ throw new RuntimeException("cannot be reached");
+ }
+
+ try
+ {
+ InetAddress address = InetAddress.getLocalHost();
+ AMQConnection con = new AMQConnection(host, port, username, password,
+ address.getHostName(), virtualPath);
+ final AMQSession session = (AMQSession) con.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ final int expectedMessageCount = numExpectedMessages;
+
+ MessageConsumer consumer = session.createConsumer(new AMQTopic(session.getDefaultTopicExchangeName(),
+ new AMQShortString("large")),
+ 100, true, false, null);
+
+ consumer.setMessageListener(new MessageListener()
+ {
+ private int _messageCount;
+
+ private long _startTime = 0;
+
+ public void onMessage(Message message)
+ {
+ validateMessage(message);
+ if (_messageCount++ == 0)
+ {
+ _startTime = System.currentTimeMillis();
+ }
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Got message '" + message + "'");
+ }
+ if (_messageCount == expectedMessageCount)
+ {
+ long totalTime = System.currentTimeMillis() - _startTime;
+ _logger.error("Total time to receive " + _messageCount + " messages was " +
+ totalTime + "ms. Rate is " + (_messageCount/(totalTime/1000.0)));
+ }
+ }
+
+ private void validateMessage(Message message)
+ {
+ if (!(message instanceof BytesMessage))
+ {
+ _logger.error("Message is not of correct type - should be BytesMessage and is " +
+ message.getClass());
+ }
+ BytesMessage bm = (BytesMessage) message;
+ final int expectedSize = 1024 * 187; // 187k
+ try
+ {
+ if (bm.getBodyLength() != expectedSize)
+ {
+ _logger.error("Message is not correct length - should be " + expectedSize + " and is " +
+ bm.getBodyLength());
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Failed to validate message: " + e, e);
+ }
+ try
+ {
+ byte[] data = new byte[(int)bm.getBodyLength()];
+ bm.readBytes(data);
+ for (int i = 0; i < data.length; i++)
+ {
+ if (data[i] != (byte)(i%25))
+ {
+ _logger.error("byte " + i + " of message is wrong - should be " + i%25 + " but is " +
+ data[i]);
+ }
+ }
+ _logger.info("***** Validated message successfully");
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Failed to validate message: " + e, e);
+ }
+ }
+ });
+ con.start();
+ }
+ catch (Throwable t)
+ {
+ System.err.println("Fatal error: " + t);
+ t.printStackTrace();
+ }
+
+ System.out.println("Waiting...");
+ }
+}
+
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java b/Final/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java
new file mode 100644
index 0000000000..cb5caefc1e
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java
@@ -0,0 +1,117 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.headers;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.jms.Session;
+//import org.apache.qpid.testutil.Config;
+
+import javax.jms.MessageListener;
+import javax.jms.Message;
+import javax.jms.Destination;
+import javax.jms.MessageProducer;
+import javax.jms.JMSException;
+
+public class Listener //implements MessageListener
+{
+/* private final AMQConnection _connection;
+ private final MessageProducer _controller;
+ private final AMQSession _session;
+ private final MessageFactory _factory;
+ private int count;
+ private long start;
+
+ Listener(AMQConnection connection, Destination exchange) throws Exception
+ {
+ _connection = connection;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _factory = new MessageFactory(_session, 0, 19);
+
+ //register for events
+ _factory.createConsumer(exchange).setMessageListener(this);
+ _connection.start();
+
+ _controller = _session.createProducer(exchange);
+ }
+
+ private void shutdown()
+ {
+ try
+ {
+ _session.close();
+ _connection.stop();
+ _connection.close();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private void report()
+ {
+ try
+ {
+ String msg = getReport();
+ _controller.send(_factory.createReportResponseMessage(msg));
+ System.out.println("Sent report: " + msg);
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private String getReport() throws JMSException
+ {
+ long time = (System.currentTimeMillis() - start);
+ return "Received " + count + " in " + time + "ms";
+ }
+
+ public void onMessage(Message message)
+ {
+ if(count == 0) start = System.currentTimeMillis();
+
+ if(_factory.isShutdown(message))
+ {
+ shutdown();
+ }
+ else if(_factory.isReport(message))
+ {
+ //send a report:
+ report();
+ }
+ else if (++count % 100 == 0)
+ {
+ System.out.println("Received " + count + " messages.");
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config();
+ config.setType(Config.HEADERS);
+ config.setName("test_headers_exchange");
+ config.setOptions(argv);
+ new Listener((AMQConnection) config.getConnection(), config.getDestination());
+ }*/
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java b/Final/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java
new file mode 100644
index 0000000000..a2d575fdd4
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.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.headers;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+
+import javax.jms.BytesMessage;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.TextMessage;
+
+/**
+ */
+class MessageFactory
+{
+ private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray();
+
+ private final AMQSession _session;
+ private final byte[] _payload;
+
+ private String[] _headerNames;
+
+ MessageFactory(AMQSession session)
+ {
+ this(session, Integer.getInteger("amqj.test.message_size", 256).intValue(), 5);
+ }
+
+ MessageFactory(AMQSession session, int payloadSize, int headerCount)
+ {
+ if (headerCount < 1)
+ {
+ throw new IllegalArgumentException("Header count must be positive");
+ }
+ _session = session;
+ _payload = new byte[payloadSize];
+ for (int i = 0; i < _payload.length; i++)
+ {
+ _payload[i] = (byte) DATA[i % DATA.length];
+ }
+ _headerNames = new String[headerCount];
+ // note that with the standard encoding the headers get prefixed with an S to indicate their type
+ for (int i = 0; i < _headerNames.length; i++)
+ {
+ if (i < 10)
+ {
+ _headerNames[i] = "F000" + i;
+ }
+ else if (i >= 10 && i < 100)
+ {
+ _headerNames[i] = "F00" + i;
+ }
+ else
+ {
+ _headerNames[i] = "F0" + i;
+ }
+ }
+ }
+
+ Message createEventMessage() throws JMSException
+ {
+ BytesMessage msg = _session.createBytesMessage();
+ if (_payload.length != 0)
+ {
+ msg.writeBytes(_payload);
+ }
+ return setHeaders(msg, _headerNames);
+ }
+
+ Message createShutdownMessage() throws JMSException
+ {
+ return setHeaders(_session.createMessage(), new String[]{"F0000", "SHUTDOWN"});
+ }
+
+ Message createReportRequestMessage() throws JMSException
+ {
+ return setHeaders(_session.createMessage(), new String[]{"F0000", "REPORT"});
+ }
+
+ Message createReportResponseMessage(String msg) throws JMSException
+ {
+ return setHeaders(_session.createTextMessage(msg), new String[]{"CONTROL", "REPORT"});
+ }
+
+ boolean isShutdown(Message m)
+ {
+ return checkPresent(m, "SHUTDOWN");
+ }
+
+ boolean isReport(Message m)
+ {
+ return checkPresent(m, "REPORT");
+ }
+
+ Object getReport(Message m)
+ {
+ try
+ {
+ return ((TextMessage) m).getText();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return e.toString();
+ }
+ }
+
+ FieldTable getConsumerBinding()
+ {
+ FieldTable binding = FieldTableFactory.newFieldTable();
+ binding.setString("SF0000", "value");
+ return binding;
+ }
+
+ FieldTable getControllerBinding()
+ {
+ FieldTable binding = FieldTableFactory.newFieldTable();
+ binding.setString("SCONTROL", "value");
+ return binding;
+ }
+
+ MessageConsumer createConsumer(Destination source) throws Exception
+ {
+ return _session.createConsumer(source, 0, false, true, null, getConsumerBinding());
+ }
+
+ MessageConsumer createController(Destination source) throws Exception
+ {
+ return _session.createConsumer(source, 0, false, true, null, getControllerBinding());
+ }
+
+ private static boolean checkPresent(Message m, String s)
+ {
+ try
+ {
+ return m.getStringProperty(s) != null;
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return false;
+ }
+ }
+
+ private static Message setHeaders(Message m, String[] headers) throws JMSException
+ {
+ for (int i = 0; i < headers.length; i++)
+ {
+ // the value in GRM is 5 bytes
+ m.setStringProperty(headers[i], "value");
+ }
+ return m;
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java b/Final/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java
new file mode 100644
index 0000000000..d9ef702c48
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java
@@ -0,0 +1,133 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.headers;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+//import org.apache.qpid.testutil.Config;
+
+import javax.jms.*;
+
+public class Publisher // implements MessageListener
+{
+/* private final Object _lock = new Object();
+ private final AMQConnection _connection;
+ private final AMQSession _session;
+ private final Destination _exchange;
+ private final MessageFactory _factory;
+ private final MessageProducer _publisher;
+ private int _count;
+
+ Publisher(AMQConnection connection, Destination exchange) throws Exception
+ {
+ _connection = connection;
+ _exchange = exchange;
+ _session = (AMQSession) _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _factory = new MessageFactory(_session, 0, 19);
+ _publisher = _session.createProducer(_exchange);
+ }
+
+ Publisher(Config config) throws Exception
+ {
+ this((AMQConnection) config.getConnection(), config.getDestination());
+ }
+
+ private void test(int msgCount, int consumerCount) throws Exception
+ {
+ _count = consumerCount;
+ _factory.createController(_exchange).setMessageListener(this);
+ _connection.start();
+ long start = System.currentTimeMillis();
+ publish(msgCount);
+ waitForCompletion(consumerCount);
+ long end = System.currentTimeMillis();
+
+ System.out.println("Completed in " + (end - start) + " ms.");
+
+ //request shutdown
+ _publisher.send(_factory.createShutdownMessage());
+
+ _connection.stop();
+ _connection.close();
+ }
+
+ private void publish(int count) throws Exception
+ {
+
+ //send events
+ for (int i = 0; i < count; i++)
+ {
+ _publisher.send(_factory.createEventMessage());
+ if ((i + 1) % 100 == 0)
+ {
+ System.out.println("Sent " + (i + 1) + " messages");
+ }
+ }
+
+ //request report
+ _publisher.send(_factory.createReportRequestMessage());
+ }
+
+ private void waitForCompletion(int consumers) throws Exception
+ {
+ System.out.println("Waiting for completion...");
+ synchronized (_lock)
+ {
+ while (_count > 0)
+ {
+ _lock.wait();
+ }
+ }
+ }
+
+
+ public void onMessage(Message message)
+ {
+ System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining");
+ if (_count == 0)
+ {
+ synchronized (_lock)
+ {
+ _lock.notify();
+ }
+ }
+ }
+
+
+ public static void main(String[] argv) throws Exception
+ {
+ if (argv.length >= 2)
+ {
+ int msgCount = Integer.parseInt(argv[argv.length - 2]);
+ int consumerCount = Integer.parseInt(argv[argv.length - 1]);
+
+ Config config = new Config();
+ config.setType(Config.HEADERS);
+ config.setName("test_headers_exchange");
+ String[] options = new String[argv.length - 2];
+ System.arraycopy(argv, 0, options, 0, options.length);
+ config.setOptions(options);
+
+ new Publisher(config).test(msgCount, consumerCount);
+ }
+
+ }*/
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java b/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java
new file mode 100644
index 0000000000..ee6a12c233
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java
@@ -0,0 +1,273 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jndi.referenceable;
+
+import org.apache.qpid.client.*;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.*;
+import javax.naming.*;
+
+import java.util.Properties;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * Binds a reference from a JNDI source.
+ * Given a properties file with the JNDI information and a binding string.
+ */
+public class Bind
+{
+ private static final String USAGE="USAGE: java bind <JNDI Properties file> -cf <url> <binding> | -c <url> <binding> [-t <topic Name> <binding>] [-q <queue Name> <binding>]";
+ public Bind(String propertiesFile, String bindingURL, Referenceable reference) throws NameAlreadyBoundException, NoInitialContextException
+ {
+ // Set up the environment for creating the initial context
+ String qpid_home = System.getProperty("QPID_HOME");
+
+ if (qpid_home == null || qpid_home.equals(""))
+ {
+ System.out.println("QPID_HOME is not set");
+ System.exit(1);
+ }
+
+ if (qpid_home.charAt(qpid_home.length() - 1) != '/')
+ {
+ qpid_home += "/";
+ }
+
+ try
+ {
+ InputStream inputStream = new FileInputStream(qpid_home + propertiesFile);
+ Properties properties = new Properties();
+ properties.load(inputStream);
+
+ // Create the initial context
+ Context ctx = new InitialContext(properties);
+
+ // Perform the binds
+ ctx.bind(bindingURL, reference);
+
+ // Close the context when we're done
+ ctx.close();
+ }
+ catch (IOException ioe)
+ {
+ System.out.println("Unable to access properties file:" + propertiesFile + " Due to:" + ioe);
+ }
+ catch (NamingException e)
+ {
+ System.out.println("Operation failed: " + e);
+ if (e instanceof NameAlreadyBoundException)
+ {
+ throw (NameAlreadyBoundException) e;
+ }
+
+ if (e instanceof NoInitialContextException)
+ {
+ throw (NoInitialContextException) e;
+ }
+ }
+
+ }
+
+ private static String parse(String[] args, int index, String what, String type)
+ {
+ try
+ {
+ return args[index];
+ }
+ catch (IndexOutOfBoundsException ioobe)
+ {
+ System.out.println("ERROR: No " + what + " specified for " + type + ".");
+ System.out.println(USAGE);
+ System.exit(1);
+ }
+
+ // The path is either return normally or exception.. which calls system exit so keep the compiler happy
+ return "Never going to happen";
+ }
+
+
+ public static void main(String[] args) throws NameAlreadyBoundException, NoInitialContextException, URLSyntaxException, AMQException, JMSException
+ {
+
+
+ org.apache.log4j.Logger.getRootLogger().setLevel(org.apache.log4j.Level.OFF);
+
+// org.apache.log4j.Logger _logger = org.apache.log4j.Logger.getLogger(AMQConnection.class);
+// _logger.setLevel(org.apache.log4j.Level.OFF);
+
+ boolean exit = false;
+
+ String qpid_home = System.getProperty("QPID_HOME");
+
+ if (qpid_home == null || qpid_home.equals(""))
+ {
+ System.out.println("QPID_HOME is not set");
+ exit = true;
+ }
+
+ if (args.length <= 2)
+ {
+ System.out.println("At least a connection or connection factory must be requested to be bound.");
+ exit = true;
+ }
+ else
+ {
+ if ((args.length - 1) % 3 != 0)
+ {
+ System.out.println("Not all values have full details");
+ exit = true;
+ }
+ }
+ if (exit)
+ {
+ System.out.println(USAGE);
+ System.exit(1);
+ }
+
+ if (qpid_home.charAt(qpid_home.length() - 1) != '/')
+
+ {
+ qpid_home += "/";
+ }
+
+ AMQConnectionFactory cf = null;
+ AMQConnection c = null;
+ AMQSession session = null;
+ Referenceable reference = null;
+
+ for (int index = 1; index < args.length; index ++)
+ {
+ String obj = args[index];
+
+ String what = "Invalid";
+ String binding;
+
+ if (obj.startsWith("-c"))
+ {
+ boolean isFactory = obj.contains("f");
+
+
+ if (isFactory)
+ {
+ what = "ConnectionFactory";
+ }
+ else
+ {
+ what = "Factory";
+ }
+
+ String url = parse(args, ++index, "url", what);
+
+ if (isFactory)
+ {
+
+ cf = new AMQConnectionFactory(url);
+ reference = cf;
+ }
+ else
+ {
+ c = new AMQConnection(url);
+ reference = c;
+ }
+
+ }
+
+ if (obj.equals("-t") || obj.equals("-q"))
+ {
+ if (c == null)
+ {
+ c = (AMQConnection) cf.createConnection();
+ }
+
+ if (session == null)
+ {
+ session = (AMQSession) c.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ }
+
+ }
+
+ if (obj.equals("-t"))
+ {
+
+ String topicName = parse(args, ++index, "Topic Name", "Topic");
+ reference = (AMQTopic) session.createTopic(topicName);
+ what = "Topic";
+ }
+ else
+ {
+ if (obj.equals("-q"))
+ {
+ String topicName = parse(args, ++index, "Queue Name", "Queue");
+ reference = (AMQQueue) session.createQueue(topicName);
+ what = "Queue";
+ }
+ }
+
+ binding = parse(args, ++index, "binding", what);
+ if (binding == null)
+ {
+ System.out.println(obj + " is not a known Object to bind.");
+ System.exit(1);
+ }
+ else
+ {
+ System.out.print("Binding:" + reference + " to " + binding);
+ try
+ {
+ new Bind(args[0], binding, reference);
+ System.out.println(" ..Successful");
+
+ }
+ catch (NameAlreadyBoundException nabe)
+ {
+ System.out.println("");
+ if (!obj.startsWith("-c") || index == args.length - 1)
+ {
+ throw nabe;
+ }
+ else
+ {
+ System.out.println("Continuing with other bindings using the same connection details");
+ }
+ }
+ finally
+ {
+ if (!obj.startsWith("-c") || index == args.length - 1)
+ {
+ if (c != null)
+ {
+ c.close();
+ }
+ }
+ }
+ }
+ }
+
+ if (c != null)
+ {
+ c.close();
+ }
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java b/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java
new file mode 100644
index 0000000000..1c9d8b0fd5
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java
@@ -0,0 +1,196 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jndi.referenceable;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * Looksup a reference from a JNDI source.
+ * Given a properties file with the JNDI information and a binding string.
+ */
+public class Lookup
+{
+ private static final String USAGE = "USAGE: java lookup <JNDI Properties file> -b <binding>";
+ private Properties _properties;
+ private Object _object;
+
+ public Lookup(String propertiesFile, String bindingValue) throws NamingException
+ {
+ // Set up the environment for creating the initial context
+ String qpid_home = System.getProperty("QPID_HOME");
+
+ if (qpid_home == null || qpid_home.equals(""))
+ {
+ System.out.println("QPID_HOME is not set");
+ System.exit(1);
+ }
+
+ if (qpid_home.charAt(qpid_home.length() - 1) != '/')
+ {
+ qpid_home += "/";
+ }
+
+ try
+ {
+ InputStream inputStream = new FileInputStream(qpid_home + propertiesFile);
+ Properties properties = new Properties();
+ properties.load(inputStream);
+
+ _properties = properties;
+ lookup(bindingValue);
+ }
+ catch (IOException ioe)
+ {
+ System.out.println("Unable to access properties file:" + propertiesFile + " Due to:" + ioe);
+ }
+ }
+
+ public Object lookup(String bindingValue) throws NamingException
+ {
+
+ // Create the initial context
+ Context ctx = new InitialContext(_properties);
+
+ // Perform the binds
+ _object = ctx.lookup(bindingValue);
+
+ // Close the context when we're done
+ ctx.close();
+
+ return getObject();
+ }
+
+ public Object getObject()
+ {
+ return _object;
+ }
+
+ private static String parse(String[] args, int index, String what)
+ {
+ try
+ {
+ return args[index];
+ }
+ catch (IndexOutOfBoundsException ioobe)
+ {
+ System.out.println("ERROR: No " + what + " specified.");
+ System.out.println(USAGE);
+ System.exit(1);
+ }
+
+ // The path is either return normally or exception.. which calls system exit so keep the compiler happy
+ return "Never going to happen";
+ }
+
+
+ public static void main(String[] args) throws NamingException
+ {
+ boolean exit = false;
+
+ String qpid_home = System.getProperty("QPID_HOME");
+
+ if (qpid_home == null || qpid_home.equals(""))
+ {
+ System.out.println("QPID_HOME is not set");
+ exit = true;
+ }
+
+ if (args.length <= 2)
+ {
+ System.out.println("At least a connection or connection factory must be requested to be bound.");
+ exit = true;
+ }
+ else
+ {
+ if ((args.length - 1) % 2 != 0)
+ {
+ System.out.println("Not all values have full details");
+ exit = true;
+ }
+ }
+ if (exit)
+ {
+ System.out.println(USAGE);
+ System.exit(1);
+ }
+
+ if (qpid_home.charAt(qpid_home.length() - 1) != '/')
+
+ {
+ qpid_home += "/";
+ }
+
+ for (int index = 1; index < args.length; index ++)
+ {
+ String obj = args[index];
+
+
+ if (obj.equals("-b"))
+ {
+ String binding = parse(args, ++index, "binding");
+
+ if (binding == null)
+ {
+ System.out.println("Binding not specified.");
+ System.exit(1);
+ }
+ else
+ {
+ System.out.print("Looking up:" + binding);
+ try
+ {
+ Lookup l = new Lookup(args[0], binding);
+
+ Object object = l.getObject();
+
+ if (object instanceof Connection)
+ {
+ try
+ {
+ ((Connection) object).close();
+ }
+ catch (JMSException jmse)
+ {
+ ;
+ }
+ }
+ }
+ catch (NamingException nabe)
+ {
+ System.out.println("Problem unbinding " + binding + " continuing with other values.");
+ }
+ }
+ }// if -b
+ else
+ {
+ System.out.println("Continuing with other bindings option not known:" + obj);
+ }
+ }//for
+ }//main
+}//class
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java b/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java
new file mode 100644
index 0000000000..1acead674c
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java
@@ -0,0 +1,166 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.jndi.referenceable;
+
+import javax.naming.*;
+
+import java.util.Properties;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * Unbinds a reference from a JNDI source.
+ * Given a properties file with the JNDI information and a binding string.
+ */
+public class Unbind
+{
+ private static final String USAGE = "USAGE: java unbind <JNDI Properties file> -b <binding>";
+
+ public Unbind(String propertiesFile, String bindingValue) throws NamingException
+ {
+ // Set up the environment for creating the initial context
+ String qpid_home = System.getProperty("QPID_HOME");
+
+ if (qpid_home == null || qpid_home.equals(""))
+ {
+ System.out.println("QPID_HOME is not set");
+ System.exit(1);
+ }
+
+ if (qpid_home.charAt(qpid_home.length() - 1) != '/')
+ {
+ qpid_home += "/";
+ }
+
+ try
+ {
+ InputStream inputStream = new FileInputStream(qpid_home + propertiesFile);
+ Properties properties = new Properties();
+ properties.load(inputStream);
+
+ // Create the initial context
+ Context ctx = new InitialContext(properties);
+
+ // Perform the binds
+ ctx.unbind(bindingValue);
+
+ // Close the context when we're done
+ ctx.close();
+ }
+ catch (IOException ioe)
+ {
+ System.out.println("Unable to access properties file:" + propertiesFile + " Due to:" + ioe);
+ }
+ }
+
+ private static String parse(String[] args, int index, String what)
+ {
+ try
+ {
+ return args[index];
+ }
+ catch (IndexOutOfBoundsException ioobe)
+ {
+ System.out.println("ERROR: No " + what + " specified.");
+ System.out.println(USAGE);
+ System.exit(1);
+ }
+
+ // The path is either return normally or exception.. which calls system exit so keep the compiler happy
+ return "Never going to happen";
+ }
+
+
+ public static void main(String[] args) throws NamingException
+ {
+ boolean exit = false;
+
+ String qpid_home = System.getProperty("QPID_HOME");
+
+ if (qpid_home == null || qpid_home.equals(""))
+ {
+ System.out.println("QPID_HOME is not set");
+ exit = true;
+ }
+
+ if (args.length <= 2)
+ {
+ System.out.println("At least a connection or connection factory must be requested to be bound.");
+ exit = true;
+ }
+ else
+ {
+ if ((args.length - 1) % 2 != 0)
+ {
+ System.out.println("Not all values have full details");
+ exit = true;
+ }
+ }
+ if (exit)
+ {
+ System.out.println(USAGE);
+ System.exit(1);
+ }
+
+ if (qpid_home.charAt(qpid_home.length() - 1) != '/')
+
+ {
+ qpid_home += "/";
+ }
+
+ for (int index = 1; index < args.length; index ++)
+ {
+ String obj = args[index];
+
+
+ if (obj.equals("-b"))
+ {
+ String binding = parse(args, ++index, "binding");
+
+ if (binding == null)
+ {
+ System.out.println("Binding not specified.");
+ System.exit(1);
+ }
+ else
+ {
+ System.out.print("UnBinding:" + binding);
+ try
+ {
+ new Unbind(args[0], binding);
+ System.out.println(" ..Successful");
+ }
+ catch (NamingException nabe)
+ {
+ System.out.println("");
+
+ System.out.println("Problem unbinding " + binding + " continuing with other values.");
+ }
+ }
+ }// if -b
+ else
+ {
+ System.out.println("Continuing with other bindings option not known:" + obj);
+ }
+ }//for
+ }//main
+}//class
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java
new file mode 100644
index 0000000000..4865a68dc4
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java
@@ -0,0 +1,153 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.latency;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.jms.MessageProducer;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.JMSException;
+import javax.jms.TextMessage;
+import javax.jms.BytesMessage;
+
+public class LatencyTest implements MessageListener
+{
+ private volatile boolean waiting;
+ private int sent;
+ private int received;
+
+ private final byte[] data;
+
+ private long min = Long.MAX_VALUE;
+ private long max = 0;
+ private long total = 0;
+
+ LatencyTest(String broker, int count, int delay, int length) throws Exception
+ {
+ this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "/test"), count, delay, length);
+ }
+
+ LatencyTest(AMQConnection connection, int count, int delay, int length) throws Exception
+ {
+ this(connection, new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("LatencyTest")), true), count, delay, length);
+ }
+
+ LatencyTest(AMQConnection connection, AMQDestination destination, int count, int delay, int length) throws Exception
+ {
+ AMQSession session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ data = new byte[length];
+ for(int i = 0; i < data.length; i++)
+ {
+ data[i] = (byte) (i % 100);
+ }
+
+ //set up a consumer
+ session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+
+ //create a publisher
+ MessageProducer producer = session.createProducer(destination, false, false, true);
+
+ //publish at a low volume
+ for(int i = 0; i < count; i++)
+ {
+ BytesMessage msg = session.createBytesMessage();
+ msg.writeBytes(data);
+ msg.setStringProperty("sent-at", Long.toString(System.nanoTime()));
+ producer.send(msg);
+ Thread.sleep(delay);
+ if(++sent % 100 == 0)
+ {
+ System.out.println("Sent " + sent + " of " + count);
+ }
+ }
+
+ waitUntilReceived(sent);
+
+ session.close();
+ connection.close();
+
+ System.out.println("Latency (in nanoseconds): avg=" + (total/sent) + ", min=" + min + ", max=" + max
+ + ", avg(discarding min and max)=" + ((total - min - max) / (sent - 2)));
+ }
+
+
+ private synchronized void waitUntilReceived(int count) throws InterruptedException
+ {
+ waiting = true;
+ while(received < count)
+ {
+ wait();
+ }
+ waiting = false;
+ }
+
+ public void onMessage(Message message)
+ {
+ received++;
+ try
+ {
+ long sent = Long.parseLong(message.getStringProperty("sent-at"));
+ long time = System.nanoTime() - sent;
+ total += time;
+ min = Math.min(min, time);
+ max = Math.max(max, time);
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ }
+
+ if(waiting){
+ synchronized(this)
+ {
+ notify();
+ }
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ String host = argv.length > 0 ? argv[0] : "localhost:5672";
+ if("-help".equals(host))
+ {
+ System.out.println("Usage: <broker> <message count> <delay between messages> <message size>");
+ }
+ int count = argv.length > 1 ? Integer.parseInt(argv[1]) : 1000;
+ int delay = argv.length > 2 ? Integer.parseInt(argv[2]) : 1000;
+ int size = argv.length > 3 ? Integer.parseInt(argv[3]) : 512;
+ new LatencyTest(host, count, delay, size);
+ }
+
+
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java
new file mode 100644
index 0000000000..f0ac0e6902
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.mina;
+
+import org.apache.log4j.Logger;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+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 java.io.IOException;
+import java.net.InetSocketAddress;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests MINA socket performance. This acceptor simply reads data from the network and writes it back again.
+ *
+ */
+public class AcceptorTest extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(AcceptorTest.class);
+
+ public static int PORT = 9999;
+
+ private static class TestHandler extends IoHandlerAdapter
+ {
+ private int _sentCount;
+
+ private int _bytesSent;
+
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ ((ByteBuffer) message).acquire();
+ session.write(message);
+ _logger.debug("Sent response " + ++_sentCount);
+ _bytesSent += ((ByteBuffer)message).remaining();
+ _logger.debug("Bytes sent: " + _bytesSent);
+ }
+
+ public void messageSent(IoSession session, Object message) throws Exception
+ {
+ //((ByteBuffer) message).release();
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause) throws Exception
+ {
+ _logger.error("Error: " + cause, cause);
+ }
+ }
+
+ public void testStartAcceptor() throws IOException
+ {
+ IoAcceptor acceptor = null;
+ acceptor = new SocketAcceptor();
+
+ SocketAcceptorConfig config = (SocketAcceptorConfig) acceptor.getDefaultConfig();
+ SocketSessionConfig sc = (SocketSessionConfig) config.getSessionConfig();
+ sc.setTcpNoDelay(true);
+ sc.setSendBufferSize(32768);
+ sc.setReceiveBufferSize(32768);
+
+ config.setThreadModel(ReadWriteThreadModel.getInstance());
+
+ acceptor.bind(new InetSocketAddress(PORT),
+ new TestHandler());
+ _logger.info("Bound on port " + PORT);
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ AcceptorTest a = new AcceptorTest();
+ a.testStartAcceptor();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(AcceptorTest.class);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java
new file mode 100644
index 0000000000..bfe29c47e6
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java
@@ -0,0 +1,93 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.mina;
+
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import junit.framework.TestCase;
+
+public class BlockingAcceptorTest extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(BlockingAcceptorTest.class);
+
+ public static int PORT = 9999;
+
+ public void testStartAcceptor() throws IOException
+ {
+
+ ServerSocket sock = new ServerSocket(PORT);
+
+ sock.setReuseAddress(true);
+ sock.setReceiveBufferSize(32768);
+ _logger.info("Bound on port " + PORT);
+
+ while (true)
+ {
+ final Socket s = sock.accept();
+ _logger.info("Received connection from " + s.getRemoteSocketAddress());
+ s.setReceiveBufferSize(32768);
+ s.setSendBufferSize(32768);
+ s.setTcpNoDelay(true);
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ byte[] chunk = new byte[32768];
+ try
+ {
+ InputStream is = s.getInputStream();
+ OutputStream os = s.getOutputStream();
+
+ while (true)
+ {
+ int count = is.read(chunk, 0, chunk.length);
+ if (count > 0)
+ {
+ os.write(chunk, 0, count);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.error("Error - closing connection: " + e, e);
+ }
+ }
+ }, "SocketReaderWriter").start();
+ }
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ BlockingAcceptorTest a = new BlockingAcceptorTest();
+ a.testStartAcceptor();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(AcceptorTest.class);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java
new file mode 100644
index 0000000000..910345624f
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java
@@ -0,0 +1,271 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.mina;
+
+import org.apache.log4j.Logger;
+import org.apache.mina.common.*;
+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 java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.TestCase;
+
+public class WriterTest extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(WriterTest.class);
+
+ private static class RunnableWriterTest implements Runnable
+ {
+ private Logger _logger;
+
+ private IoSession _session;
+
+ private long _startTime;
+
+ private long[] _chunkTimes;
+
+ private int _chunkCount = 500000;
+
+ private int _chunkSize = 1024;
+
+ private CountDownLatch _notifier;
+
+ public RunnableWriterTest(Logger logger)
+ {
+ _logger = logger;
+ }
+
+ public void run()
+ {
+ _startTime = System.currentTimeMillis();
+ _notifier = new CountDownLatch(1);
+ for (int i = 0; i < _chunkCount; i++)
+ {
+ ByteBuffer buf = ByteBuffer.allocate(_chunkSize, false);
+ byte check = (byte) (i % 128);
+ buf.put(check);
+ buf.fill((byte)88, buf.remaining());
+ buf.flip();
+ _session.write(buf);
+ }
+
+ try
+ {
+ _logger.info("All buffers sent; waiting for receipt from server");
+ _notifier.await();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ _logger.info("Completed");
+ long totalTime = System.currentTimeMillis() - _startTime;
+ _logger.info("Total time: " + totalTime);
+ _logger.info("MB per second: " + (_chunkSize * _chunkCount)/totalTime);
+ long lastChunkTime = _startTime;
+ double average = 0;
+ for (int i = 0; i < _chunkTimes.length; i++)
+ {
+ if (i == 0)
+ {
+ average = _chunkTimes[i] - _startTime;
+ }
+ else
+ {
+ long delta = _chunkTimes[i] - lastChunkTime;
+ if (delta != 0)
+ {
+ average = (average + delta)/2;
+ }
+ }
+ lastChunkTime = _chunkTimes[i];
+ }
+ _logger.info("Average chunk time: " + average + "ms");
+ CloseFuture cf = _session.close();
+ cf.join();
+ }
+
+ private class WriterHandler extends IoHandlerAdapter
+ {
+ private int _chunksReceived = 0;
+
+ private int _partialBytesRead = 0;
+
+ private byte _partialCheckNumber;
+
+ private int _totalBytesReceived = 0;
+
+ public void messageReceived(IoSession session, Object message) throws Exception
+ {
+ ByteBuffer result = (ByteBuffer) message;
+ _totalBytesReceived += result.remaining();
+ int size = result.remaining();
+ long now = System.currentTimeMillis();
+ if (_partialBytesRead > 0)
+ {
+ int offset = _chunkSize - _partialBytesRead;
+ if (size >= offset)
+ {
+ _chunkTimes[_chunksReceived++] = now;
+ result.position(offset);
+ }
+ else
+ {
+ // have not read even one chunk, including the previous partial bytes
+ _partialBytesRead += size;
+ return;
+ }
+ }
+
+ int chunkCount = result.remaining()/_chunkSize;
+
+ for (int i = 0; i < chunkCount; i++)
+ {
+ _chunkTimes[_chunksReceived++] = now;
+ byte check = result.get();
+ _logger.debug("Check number " + check + " read");
+ if (check != (byte)((_chunksReceived - 1)%128))
+ {
+ _logger.error("Check number " + check + " read when expected " + (_chunksReceived%128));
+ }
+ _logger.debug("Chunk times recorded");
+
+ try
+ {
+ result.skip(_chunkSize - 1);
+ }
+ catch (IllegalArgumentException e)
+ {
+ _logger.error("Position was: " + result.position());
+ _logger.error("Tried to skip to: " + (_chunkSize * i));
+ _logger.error("limit was; " + result.limit());
+ }
+ }
+ _logger.debug("Chunks received now " + _chunksReceived);
+ _logger.debug("Bytes received: " + _totalBytesReceived);
+ _partialBytesRead = result.remaining();
+
+ if (_partialBytesRead > 0)
+ {
+ _partialCheckNumber = result.get();
+ }
+
+ if (_chunksReceived >= _chunkCount)
+ {
+ _notifier.countDown();
+ }
+
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause) throws Exception
+ {
+ _logger.error("Error: " + cause, cause);
+ }
+ }
+
+ public void startWriter(int chunkSize) throws IOException, InterruptedException
+ {
+ _chunkSize = chunkSize;
+
+ IoConnector ioConnector = null;
+
+ ioConnector = new SocketConnector();
+
+ SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig();
+ cfg.setThreadModel(ThreadModel.MANUAL);
+ SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig();
+ scfg.setTcpNoDelay(true);
+ scfg.setSendBufferSize(32768);
+ scfg.setReceiveBufferSize(32768);
+
+ final InetSocketAddress address = new InetSocketAddress("localhost", AcceptorTest.PORT);
+ _logger.info("Attempting connection to " + address);
+ ConnectFuture future = ioConnector.connect(address, new WriterHandler());
+ // wait for connection to complete
+ future.join();
+ _logger.info("Connection completed");
+ // we call getSession which throws an IOException if there has been an error connecting
+ _session = future.getSession();
+ _chunkTimes = new long[_chunkCount];
+ Thread t = new Thread(this);
+ t.start();
+ t.join();
+ _logger.info("Test completed");
+ }
+ }
+
+ private RunnableWriterTest _runnableWriterTest = new RunnableWriterTest(_logger);
+
+ public void test1k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 1k test");
+ _runnableWriterTest.startWriter(1024);
+ }
+
+ public void test2k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 2k test");
+ _runnableWriterTest.startWriter(2048);
+ }
+
+ public void test4k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 4k test");
+ _runnableWriterTest.startWriter(4096);
+ }
+
+ public void test8k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 8k test");
+ _runnableWriterTest.startWriter(8192);
+ }
+
+ public void test16k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 16k test");
+ _runnableWriterTest.startWriter(16384);
+ }
+
+ public void test32k() throws IOException, InterruptedException
+ {
+ _logger.info("Starting 32k test");
+ _runnableWriterTest.startWriter(32768);
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException
+ {
+ WriterTest w = new WriterTest();
+ //w.test1k();
+ //w.test2k();
+ //w.test4k();
+ w.test8k();
+ //w.test16k();
+ //w.test32k();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(WriterTest.class);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java
new file mode 100644
index 0000000000..db02b9954a
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java
@@ -0,0 +1,269 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.multiconsumer;
+
+import java.io.ByteArrayOutputStream;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.Session;
+
+/**
+ * Test AMQ.
+ */
+public class AMQTest extends TestCase implements ExceptionListener
+{
+
+ private final static String COMPRESSION_PROPNAME = "_MSGAPI_COMP";
+ private final static String UTF8 = "UTF-8";
+ private static final String SUBJECT = "test.amq";
+ private static final String DUMMYCONTENT = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ private static final String HUGECONTENT;
+
+ private AMQConnection connect = null;
+ private Session pubSession = null;
+ private Session subSession = null;
+ private Topic topic = null;
+
+ static
+ {
+ StringBuilder sb = new StringBuilder(DUMMYCONTENT.length() * 115);
+ for (int i = 0; i < 100; i++)
+ {
+ sb.append(DUMMYCONTENT);
+ }
+ HUGECONTENT = sb.toString();
+ }
+
+ private void setup() throws Exception
+ {
+ connect = new AMQConnection("localhost", 5672, "guest", "guest", "client1", "/");
+ connect.setExceptionListener(this);
+ pubSession = connect.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
+ subSession = connect.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
+ topic = new AMQTopic(pubSession.getDefaultTopicExchangeName(), new AMQShortString(SUBJECT));
+
+ connect.start();
+ }
+
+ public void testMultipleListeners() throws Exception
+ {
+ setup();
+ try
+ {
+ // Create 5 listeners
+ MsgHandler[] listeners = new MsgHandler[5];
+ for (int i = 0; i < listeners.length; i++)
+ {
+ listeners[i] = new MsgHandler();
+ MessageConsumer subscriber = subSession.createConsumer(topic);
+ subscriber.setMessageListener(listeners[i]);
+ }
+ MessageProducer publisher = pubSession.createProducer(topic);
+ // Send a single message
+ TextMessage msg = pubSession.createTextMessage();
+ msg.setText(DUMMYCONTENT);
+ publisher.send(msg);
+ Thread.sleep(5000);
+ // Check listeners to ensure they all got it
+ for (int i = 0; i < listeners.length; i++)
+ {
+ if (listeners[i].isGotIt())
+ {
+ System.out.println("Got callback for listener " + i);
+ }
+ else
+ {
+ TestCase.fail("Listener " + i + " did not get callback");
+ }
+ }
+ }
+ catch (Throwable e)
+ {
+ System.err.println("Error: " + e);
+ e.printStackTrace(System.err);
+ }
+ finally
+ {
+ close();
+ }
+ }
+
+ public void testCompression() throws Exception
+ {
+ setup();
+ String comp = this.compressString(HUGECONTENT);
+ try
+ {
+ MsgHandler listener = new MsgHandler();
+ MessageConsumer subscriber = subSession.createConsumer(topic);
+ subscriber.setMessageListener(listener);
+ MessageProducer publisher = pubSession.createProducer(topic);
+
+ // Send a single message
+ TextMessage msg = pubSession.createTextMessage();
+ // Set the compressed text
+ msg.setText(comp);
+ msg.setBooleanProperty(COMPRESSION_PROPNAME, true);
+ publisher.send(msg);
+ Thread.sleep(1000);
+ // Check listeners to ensure we got it
+ if (listener.isGotIt())
+ {
+ System.out.println("Got callback for listener");
+ }
+ else
+ {
+ TestCase.fail("Listener did not get callback");
+ }
+ }
+ finally
+ {
+ close();
+ }
+ }
+
+ private void close() throws Exception
+ {
+ if (connect != null)
+ {
+ connect.close();
+ }
+ }
+
+ private class MsgHandler implements MessageListener
+ {
+ private boolean gotIt = false;
+
+ public void onMessage(Message msg)
+ {
+ try
+ {
+ TextMessage textMessage = (TextMessage) msg;
+ String string = textMessage.getText();
+ if (string != null && string.length() > 0)
+ {
+ gotIt = true;
+ }
+ if (textMessage.getBooleanProperty(COMPRESSION_PROPNAME))
+ {
+ string = inflateString(string);
+ }
+ System.out.println("Got callback of size " + (string==null?0:string.length()));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean isGotIt()
+ {
+ return this.gotIt;
+ }
+ }
+
+ private String compressString(String string) throws Exception
+ {
+ long start = System.currentTimeMillis();
+ byte[] input = string.getBytes();
+ Deflater compressor = new Deflater(Deflater.BEST_COMPRESSION);
+ compressor.setInput(input);
+ compressor.finish();
+
+ // Get byte array from output of compressor
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length);
+ byte[] buf = new byte[1024];
+ while (!compressor.finished())
+ {
+ int cnt = compressor.deflate(buf);
+ baos.write(buf, 0, cnt);
+ }
+ baos.close();
+ byte[] output = baos.toByteArray();
+
+ // Convert byte array into String
+ byte[] base64 = Base64.encodeBase64(output);
+ String sComp = new String(base64, UTF8);
+
+ long diff = System.currentTimeMillis() - start;
+ System.out.println("Compressed text from " + input.length + " to "
+ + sComp.getBytes().length + " in " + diff + " ms");
+ System.out.println("Compressed text = '" + sComp + "'");
+
+ return sComp;
+ }
+
+ private String inflateString(String string) throws Exception
+ {
+ byte[] input = string.getBytes();
+
+ // First convert Base64 string back to binary array
+ byte[] bytes = Base64.decodeBase64(input);
+
+ // Set string as input data for decompressor
+ Inflater decompressor = new Inflater();
+ decompressor.setInput(bytes);
+
+ // Decompress the data
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
+ byte[] buf = new byte[1024];
+ while (!decompressor.finished())
+ {
+ int count = decompressor.inflate(buf);
+ bos.write(buf, 0, count);
+ }
+ bos.close();
+ byte[] output = bos.toByteArray();
+
+ // Get the decompressed data
+ return new String(output, UTF8);
+ }
+
+ /**
+ * @see javax.jms.ExceptionListener#onException(javax.jms.JMSException)
+ */
+ public void onException(JMSException e)
+ {
+ System.err.println(e.getMessage());
+ e.printStackTrace(System.err);
+ }
+
+
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java b/Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java
new file mode 100644
index 0000000000..33891142b5
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java
@@ -0,0 +1,176 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.pubsub1;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.jms.MessageProducer;
+import org.apache.qpid.jms.Session;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.TextMessage;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A client that behaves as follows:
+ * <ul><li>Connects to a queue, whose name is specified as a cmd-line argument</li>
+ * <li>Creates a temporary queue</li>
+ * <li>Creates messages containing a property that is the name of the temporary queue</li>
+ * <li>Fires off a message on the original queue and waits for a response on the temporary queue</li>
+ * </ul>
+ *
+ */
+public class TestPublisher
+{
+ private static final Logger _log = Logger.getLogger(TestPublisher.class);
+
+ private AMQConnection _connection;
+
+ private Session _session;
+
+ private class CallbackHandler implements MessageListener
+ {
+ private int _expectedMessageCount;
+
+ private int _actualMessageCount;
+
+ private long _startTime;
+
+ public CallbackHandler(int expectedMessageCount, long startTime)
+ {
+ _expectedMessageCount = expectedMessageCount;
+ _startTime = startTime;
+ }
+
+ public void onMessage(Message m)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Message received: " + m);
+ }
+ _actualMessageCount++;
+ if (_actualMessageCount%1000 == 0)
+ {
+ _log.info("Received message count: " + _actualMessageCount);
+ }
+ /*if (!"henson".equals(m.toString()))
+ {
+ _log.error("AbstractJMSMessage response not correct: expected 'henson' but got " + m.toString());
+ }
+ else
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("AbstractJMSMessage " + m + " received");
+ }
+ else
+ {
+ _log.info("AbstractJMSMessage received");
+ }
+ } */
+
+ if (_actualMessageCount == _expectedMessageCount)
+ {
+ long timeTaken = System.currentTimeMillis() - _startTime;
+ System.out.println("Total time taken to receive " + _expectedMessageCount+ " messages was " +
+ timeTaken + "ms, equivalent to " +
+ (_expectedMessageCount/(timeTaken/1000.0)) + " messages per second");
+ }
+ }
+ }
+
+ public TestPublisher(String host, int port, String clientID, String commandQueueName,
+ final int messageCount) throws AMQException, URLSyntaxException
+ {
+ try
+ {
+ createConnection(host, port, clientID);
+
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ AMQTopic destination = new AMQTopic(_session.getDefaultTopicExchangeName(), new AMQShortString(commandQueueName));
+ MessageProducer producer = (MessageProducer) _session.createProducer(destination);
+
+ _connection.start();
+ //TextMessage msg = _session.createTextMessage(tempDestination.getQueueName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths");
+ final long startTime = System.currentTimeMillis();
+
+ for (int i = 0; i < messageCount; i++)
+ {
+ TextMessage msg = _session.createTextMessage(destination.getTopicName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths: " + i);
+
+ //msg.setIntProperty("a",i % 2);
+ //msg.setIntProperty("b",i % 4);
+
+ producer.send(msg);
+ }
+ _log.info("Finished sending " + messageCount + " messages");
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+
+ private void createConnection(String host, int port, String clientID) throws AMQException, URLSyntaxException
+ {
+ _connection = new AMQConnection(host, port, "guest", "guest",
+ clientID, "/test");
+ }
+
+ /**
+ *
+ * @param args argument 1 if present specifies the name of the temporary queue to create. Leaving it blank
+ * means the server will allocate a name.
+ */
+ public static void main(String[] args) throws URLSyntaxException
+ {
+ if (args.length == 0)
+ {
+ System.err.println("Usage: TestPublisher <host> <port> <command queue name> <number of messages>");
+ }
+ try
+ {
+ int port = Integer.parseInt(args[1]);
+ InetAddress address = InetAddress.getLocalHost();
+ String clientID = address.getHostName() + System.currentTimeMillis();
+ TestPublisher client = new TestPublisher(args[0], port, clientID, args[2], Integer.parseInt(args[3]));
+ }
+ catch (UnknownHostException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ catch (AMQException e)
+ {
+ System.err.println("Error in client: " + e);
+ e.printStackTrace();
+ }
+
+ //System.exit(0);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java b/Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java
new file mode 100644
index 0000000000..450d9b3914
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.pubsub1;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Topic;
+import java.net.InetAddress;
+
+public class TestSubscriber
+{
+ private static final Logger _logger = Logger.getLogger(TestSubscriber.class);
+
+ private static class TestMessageListener implements MessageListener
+ {
+ private String _name;
+
+ private int _expectedMessageCount;
+
+ private int _messageCount;
+
+ private long _startTime = 0;
+
+ public TestMessageListener(String name, int expectedMessageCount)
+ {
+ _name = name;
+ _expectedMessageCount = expectedMessageCount;
+ }
+
+ public void onMessage(javax.jms.Message message)
+ {
+ if (_messageCount++ == 0)
+ {
+ _startTime = System.currentTimeMillis();
+ }
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info(_name + " got message '" + message + "'");
+ }
+ if (_messageCount == _expectedMessageCount)
+ {
+ long totalTime = System.currentTimeMillis() - _startTime;
+ _logger.error(_name + ": Total time to receive " + _messageCount + " messages was " +
+ totalTime + "ms. Rate is " + (_messageCount/(totalTime/1000.0)));
+ }
+ if (_messageCount > _expectedMessageCount)
+ {
+ _logger.error("Oops! More messages received than expected (" + _messageCount + ")");
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ _logger.info("Starting...");
+
+ if (args.length != 7)
+ {
+ System.out.println("Usage: host port username password virtual-path expectedMessageCount selector");
+ System.exit(1);
+ }
+ try
+ {
+ InetAddress address = InetAddress.getLocalHost();
+ AMQConnection con1 = new AMQConnection(args[0], Integer.parseInt(args[1]), args[2], args[3],
+ address.getHostName(), args[4]);
+ final Session session1 = con1.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ AMQConnection con2 = new AMQConnection(args[0], Integer.parseInt(args[1]), args[2], args[3],
+ address.getHostName(), args[4]);
+ final Session session2 = con2.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ String selector = args[6];
+
+ final int expectedMessageCount = Integer.parseInt(args[5]);
+ _logger.info("Message selector is <" + selector + ">...");
+
+ Topic t = new AMQTopic(session1.getDefaultTopicExchangeName(), new AMQShortString("cbr"));
+ MessageConsumer consumer1 = session1.createConsumer(t,
+ 100, false, false, selector);
+ MessageConsumer consumer2 = session2.createConsumer(t,
+ 100, false, false, selector);
+
+ consumer1.setMessageListener(new TestMessageListener("ML 1", expectedMessageCount));
+ consumer2.setMessageListener(new TestMessageListener("ML 2", expectedMessageCount));
+ con1.start();
+ con2.start();
+ }
+ catch (Throwable t)
+ {
+ System.err.println("Fatal error: " + t);
+ t.printStackTrace();
+ }
+
+ System.out.println("Waiting...");
+ }
+}
+
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java b/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java
new file mode 100644
index 0000000000..f59b36166a
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.connection;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.log4j.Logger;
+
+import junit.framework.TestCase;
+
+public class TestManyConnections extends TestCase
+{
+ private static final Logger _log = Logger.getLogger(TestManyConnections.class);
+
+ private AMQConnection[] _connections;
+
+ private void createConnection(int index, String brokerHosts, String clientID, String username, String password,
+ String vpath) throws AMQException, URLSyntaxException
+ {
+ _connections[index] = new AMQConnection(brokerHosts, username, password,
+ clientID, vpath);
+ }
+
+ private void createConnections(int count) throws AMQException, URLSyntaxException
+ {
+ _connections = new AMQConnection[count];
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < count; i++)
+ {
+ createConnection(i, "vm://:1", "myClient" + i, "guest", "guest", "test");
+ }
+ long endTime = System.currentTimeMillis();
+ _log.info("Time to create " + count + " connections: " + (endTime - startTime) +
+ "ms");
+ }
+
+ public void testCreate10Connections() throws AMQException, URLSyntaxException
+ {
+ createConnections(10);
+ }
+
+ public void testCreate50Connections() throws AMQException, URLSyntaxException
+ {
+ createConnections(50);
+ }
+
+ public void testCreate100Connections() throws AMQException, URLSyntaxException
+ {
+ createConnections(100);
+ }
+
+ public void testCreate250Connections() throws AMQException, URLSyntaxException
+ {
+ createConnections(250);
+ }
+
+ public void testCreate500Connections() throws AMQException, URLSyntaxException
+ {
+ createConnections(500);
+ }
+
+ public void testCreate1000Connections() throws AMQException, URLSyntaxException
+ {
+ createConnections(1000);
+ }
+
+ public void testCreate5000Connections() throws AMQException, URLSyntaxException
+ {
+ createConnections(5000);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TestManyConnections.class);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java b/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java
new file mode 100644
index 0000000000..5ab5722146
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java
@@ -0,0 +1,153 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.jndi;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQTopic;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import java.util.Properties;
+import java.io.InputStream;
+
+
+import junit.framework.TestCase;
+
+public class PropertiesFileInitialContextFactoryTest extends TestCase
+{
+ InitialContextFactory contextFactory;
+ Properties _properties;
+ Properties _fileProperties;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ //create simple set of hardcoded props
+ _properties = new Properties();
+ _properties.put("java.naming.factory.initial", "org.apache.qpid.jndi.PropertiesFileInitialContextFactory");
+ _properties.put("connectionfactory.local", "amqp://guest:guest@clientid/testpath?brokerlist='vm://:1'");
+ _properties.put("queue.MyQueue", "example.MyQueue");
+ _properties.put("topic.ibmStocks", "stocks.nyse.ibm");
+ _properties.put("destination.direct", "direct://amq.direct//directQueue");
+
+ //create properties from file as a more realistic test
+ _fileProperties = new Properties();
+ ClassLoader cl = this.getClass().getClassLoader();
+ InputStream is = cl.getResourceAsStream("org/apache/qpid/test/unit/jndi/example.properties");
+ _fileProperties.load(is);
+ }
+
+ /**
+ * Test using hardcoded properties
+ */
+ public void testWithoutFile()
+ {
+ Context ctx = null;
+
+ try
+ {
+ ctx = new InitialContext(_properties);
+ }
+ catch (NamingException ne)
+ {
+ fail("Error loading context:" + ne);
+ }
+
+ checkPropertiesMatch(ctx, "Using hardcoded properties: ");
+ }
+
+ /**
+ * Test using properties from example file
+ */
+ public void testWithFile()
+ {
+ Context ctx = null;
+
+ try
+ {
+ ctx = new InitialContext(_fileProperties);
+ }
+ catch (Exception e)
+ {
+ fail("Error loading context:" + e);
+ }
+
+ checkPropertiesMatch(ctx, "Using properties from file: ");
+ }
+
+ public void tearDown()
+ {
+ _properties = null;
+ _fileProperties = null;
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(PropertiesFileInitialContextFactoryTest.class);
+ }
+
+ private void checkPropertiesMatch(Context ctx, String errorInfo)
+ {
+ try
+ {
+ AMQConnectionFactory cf = (AMQConnectionFactory) ctx.lookup("local");
+ assertEquals("amqp://guest:guest@clientid/testpath?brokerlist='vm://:1'", cf.getConnectionURL().toString());
+ }
+ catch (NamingException ne)
+ {
+ fail(errorInfo + "Unable to create Connection Factory:" + ne);
+ }
+
+ try
+ {
+ AMQQueue queue = (AMQQueue) ctx.lookup("MyQueue");
+ assertEquals("example.MyQueue", queue.getRoutingKey().toString());
+ }
+ catch (NamingException ne)
+ {
+ fail(errorInfo + "Unable to create queue:" + ne);
+ }
+
+ try
+ {
+ AMQTopic topic = (AMQTopic) ctx.lookup("ibmStocks");
+ assertEquals("stocks.nyse.ibm", topic.getTopicName().toString());
+ }
+ catch (Exception ne)
+ {
+ fail(errorInfo + "Unable to create topic:" + ne);
+ }
+
+ try
+ {
+ AMQQueue direct = (AMQQueue) ctx.lookup("direct");
+ assertEquals("directQueue", direct.getRoutingKey().toString());
+ }
+ catch (NamingException ne)
+ {
+ fail(errorInfo + "Unable to create direct destination:" + ne);
+ }
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties b/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties
new file mode 100644
index 0000000000..ea9dc5ae0e
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties
@@ -0,0 +1,38 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory
+
+# use the following property to configure the default connector
+#java.naming.provider.url - ignored.
+
+# register some connection factories
+# connectionfactory.[jndiname] = [ConnectionURL]
+connectionfactory.local = amqp://guest:guest@clientid/testpath?brokerlist='vm://:1'
+
+# register some queues in JNDI using the form
+# queue.[jndiName] = [physicalName]
+queue.MyQueue = example.MyQueue
+
+# register some topics in JNDI using the form
+# topic.[jndiName] = [physicalName]
+topic.ibmStocks = stocks.nyse.ibm
+
+# Register an AMQP destination in JNDI
+# NOTE: Qpid currently only supports direct,topics and headers
+# destination.[jniName] = [BindingURL]
+destination.direct = direct://amq.direct//directQueue
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/topic/Config.java b/Final/java/client/src/old_test/java/org/apache/qpid/topic/Config.java
new file mode 100644
index 0000000000..bb740f9094
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/topic/Config.java
@@ -0,0 +1,243 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.topic;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.config.ConnectorConfig;
+import org.apache.qpid.config.ConnectionFactoryInitialiser;
+import org.apache.qpid.config.Connector;
+import org.apache.qpid.config.AbstractConfig;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+
+class Config extends AbstractConfig implements ConnectorConfig
+{
+
+ private String host = "localhost";
+ private int port = 5672;
+ private String factory = null;
+
+ private int payload = 256;
+ private int messages = 1000;
+ private int clients = 1;
+ private int batch = 1;
+ private long delay = 1;
+ private int warmup;
+ private int ackMode= AMQSession.NO_ACKNOWLEDGE;
+ private String clientId;
+ private String subscriptionId;
+ private boolean persistent;
+
+ public Config()
+ {
+ }
+
+ int getAckMode()
+ {
+ return ackMode;
+ }
+
+ void setPayload(int payload)
+ {
+ this.payload = payload;
+ }
+
+ int getPayload()
+ {
+ return payload;
+ }
+
+ void setClients(int clients)
+ {
+ this.clients = clients;
+ }
+
+ int getClients()
+ {
+ return clients;
+ }
+
+ void setMessages(int messages)
+ {
+ this.messages = messages;
+ }
+
+ int getMessages()
+ {
+ return messages;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public void setHost(String host)
+ {
+ this.host = host;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ public String getFactory()
+ {
+ return factory;
+ }
+
+ public void setPort(int port)
+ {
+ this.port = port;
+ }
+
+ int getBatch()
+ {
+ return batch;
+ }
+
+ void setBatch(int batch)
+ {
+ this.batch = batch;
+ }
+
+ int getWarmup()
+ {
+ return warmup;
+ }
+
+ void setWarmup(int warmup)
+ {
+ this.warmup = warmup;
+ }
+
+ public long getDelay()
+ {
+ return delay;
+ }
+
+ public void setDelay(long delay)
+ {
+ this.delay = delay;
+ }
+
+ String getClientId()
+ {
+ return clientId;
+ }
+
+ String getSubscriptionId()
+ {
+ return subscriptionId;
+ }
+
+ boolean usePersistentMessages()
+ {
+ return persistent;
+ }
+
+ public void setOption(String key, String value)
+ {
+ if("-host".equalsIgnoreCase(key))
+ {
+ setHost(value);
+ }
+ else if("-port".equalsIgnoreCase(key))
+ {
+ try
+ {
+ setPort(Integer.parseInt(value));
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException("Bad port number: " + value);
+ }
+ }
+ else if("-payload".equalsIgnoreCase(key))
+ {
+ setPayload(parseInt("Bad payload size", value));
+ }
+ else if("-messages".equalsIgnoreCase(key))
+ {
+ setMessages(parseInt("Bad message count", value));
+ }
+ else if("-clients".equalsIgnoreCase(key))
+ {
+ setClients(parseInt("Bad client count", value));
+ }
+ else if("-batch".equalsIgnoreCase(key))
+ {
+ setBatch(parseInt("Bad batch count", value));
+ }
+ else if("-delay".equalsIgnoreCase(key))
+ {
+ setDelay(parseLong("Bad batch delay", value));
+ }
+ else if("-warmup".equalsIgnoreCase(key))
+ {
+ setWarmup(parseInt("Bad warmup count", value));
+ }
+ else if("-ack".equalsIgnoreCase(key))
+ {
+ ackMode = parseInt("Bad ack mode", value);
+ }
+ else if("-factory".equalsIgnoreCase(key))
+ {
+ factory = value;
+ }
+ else if("-clientId".equalsIgnoreCase(key))
+ {
+ clientId = value;
+ }
+ else if("-subscriptionId".equalsIgnoreCase(key))
+ {
+ subscriptionId = value;
+ }
+ else if("-persistent".equalsIgnoreCase(key))
+ {
+ persistent = "true".equalsIgnoreCase(value);
+ }
+ else
+ {
+ System.out.println("Ignoring unrecognised option: " + key);
+ }
+ }
+
+ static String getAckModeDescription(int ackMode)
+ {
+ switch(ackMode)
+ {
+ case AMQSession.NO_ACKNOWLEDGE: return "NO_ACKNOWLEDGE";
+ case AMQSession.AUTO_ACKNOWLEDGE: return "AUTO_ACKNOWLEDGE";
+ case AMQSession.CLIENT_ACKNOWLEDGE: return "CLIENT_ACKNOWLEDGE";
+ case AMQSession.DUPS_OK_ACKNOWLEDGE: return "DUPS_OK_ACKNOWELDGE";
+ case AMQSession.PRE_ACKNOWLEDGE: return "PRE_ACKNOWLEDGE";
+ }
+ return "AckMode=" + ackMode;
+ }
+
+ public Connection createConnection() throws Exception
+ {
+ return new Connector().createConnection(this);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java b/Final/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java
new file mode 100644
index 0000000000..47c608cfe4
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java
@@ -0,0 +1,141 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.topic;
+
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+public class Listener implements MessageListener
+{
+ private final Connection _connection;
+ private final MessageProducer _controller;
+ private final javax.jms.Session _session;
+ private final MessageFactory _factory;
+ private boolean init;
+ private int count;
+ private long start;
+
+ Listener(Connection connection, int ackMode) throws Exception
+ {
+ this(connection, ackMode, null);
+ }
+
+ Listener(Connection connection, int ackMode, String name) throws Exception
+ {
+ _connection = connection;
+ _session = connection.createSession(false, ackMode);
+ _factory = new MessageFactory(_session);
+
+ //register for events
+ if(name == null)
+ {
+ _factory.createTopicConsumer().setMessageListener(this);
+ }
+ else
+ {
+ _factory.createDurableTopicConsumer(name).setMessageListener(this);
+ }
+
+ _connection.start();
+
+ _controller = _factory.createControlPublisher();
+ System.out.println("Waiting for messages " +
+ Config.getAckModeDescription(ackMode)
+ + (name == null ? "" : " (subscribed with name " + name + " and client id " + connection.getClientID() + ")")
+ + "...");
+
+ }
+
+ private void shutdown()
+ {
+ try
+ {
+ _session.close();
+ _connection.stop();
+ _connection.close();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private void report()
+ {
+ try
+ {
+ String msg = getReport();
+ _controller.send(_factory.createReportResponseMessage(msg));
+ System.out.println("Sent report: " + msg);
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private String getReport()
+ {
+ long time = (System.currentTimeMillis() - start);
+ return "Received " + count + " in " + time + "ms";
+ }
+
+ public void onMessage(Message message)
+ {
+ if(!init)
+ {
+ start = System.currentTimeMillis();
+ count = 0;
+ init = true;
+ }
+
+ if(_factory.isShutdown(message))
+ {
+ shutdown();
+ }
+ else if(_factory.isReport(message))
+ {
+ //send a report:
+ report();
+ init = false;
+ }
+ else if (++count % 100 == 0)
+ {
+ System.out.println("Received " + count + " messages.");
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config();
+ config.setOptions(argv);
+
+ Connection con = config.createConnection();
+ if(config.getClientId() != null)
+ {
+ con.setClientID(config.getClientId());
+ }
+ new Listener(con, config.getAckMode(), config.getSubscriptionId());
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java b/Final/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java
new file mode 100644
index 0000000000..39d64069d1
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java
@@ -0,0 +1,155 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.topic;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+
+import javax.jms.*;
+
+/**
+ */
+class MessageFactory
+{
+ private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray();
+
+ private final Session _session;
+ private final Topic _topic;
+ private final Topic _control;
+ private final byte[] _payload;
+
+
+ MessageFactory(Session session) throws JMSException
+ {
+ this(session, 256);
+ }
+
+ MessageFactory(Session session, int size) throws JMSException
+ {
+ _session = session;
+ if(session instanceof AMQSession)
+ {
+ _topic = new AMQTopic(((AMQSession)session).getDefaultTopicExchangeName(),new AMQShortString("topictest.messages"));
+ _control = new AMQTopic(((AMQSession)session).getDefaultTopicExchangeName(),new AMQShortString("topictest.control"));
+ }
+ else
+ {
+ _topic = session.createTopic("topictest.messages");
+ _control = session.createTopic("topictest.control");
+ }
+ _payload = new byte[size];
+
+ for(int i = 0; i < size; i++)
+ {
+ _payload[i] = (byte) DATA[i % DATA.length];
+ }
+ }
+
+ Topic getTopic()
+ {
+ return _topic;
+ }
+
+ Message createEventMessage() throws JMSException
+ {
+ BytesMessage msg = _session.createBytesMessage();
+ msg.writeBytes(_payload);
+ return msg;
+ }
+
+ Message createShutdownMessage() throws JMSException
+ {
+ return _session.createTextMessage("SHUTDOWN");
+ }
+
+ Message createReportRequestMessage() throws JMSException
+ {
+ return _session.createTextMessage("REPORT");
+ }
+
+ Message createReportResponseMessage(String msg) throws JMSException
+ {
+ return _session.createTextMessage(msg);
+ }
+
+ boolean isShutdown(Message m)
+ {
+ return checkText(m, "SHUTDOWN");
+ }
+
+ boolean isReport(Message m)
+ {
+ return checkText(m, "REPORT");
+ }
+
+ Object getReport(Message m)
+ {
+ try
+ {
+ return ((TextMessage) m).getText();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return e.toString();
+ }
+ }
+
+ MessageConsumer createTopicConsumer() throws Exception
+ {
+ return _session.createConsumer(_topic);
+ }
+
+ MessageConsumer createDurableTopicConsumer(String name) throws Exception
+ {
+ return _session.createDurableSubscriber(_topic, name);
+ }
+
+ MessageConsumer createControlConsumer() throws Exception
+ {
+ return _session.createConsumer(_control);
+ }
+
+ MessageProducer createTopicPublisher() throws Exception
+ {
+ return _session.createProducer(_topic);
+ }
+
+ MessageProducer createControlPublisher() throws Exception
+ {
+ return _session.createProducer(_control);
+ }
+
+ private static boolean checkText(Message m, String s)
+ {
+ try
+ {
+ return m instanceof TextMessage && ((TextMessage) m).getText().equals(s);
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return false;
+ }
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java b/Final/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java
new file mode 100644
index 0000000000..d788029ee9
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.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.topic;
+
+import javax.jms.*;
+
+public class Publisher implements MessageListener
+{
+ private final Object _lock = new Object();
+ private final Connection _connection;
+ private final Session _session;
+ private final MessageFactory _factory;
+ private final MessageProducer _publisher;
+ private int _count;
+
+ Publisher(Connection connection, int size, int ackMode, boolean persistent) throws Exception
+ {
+ _connection = connection;
+ _session = _connection.createSession(false, ackMode);
+ _factory = new MessageFactory(_session, size);
+ _publisher = _factory.createTopicPublisher();
+ _publisher.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
+ System.out.println("Publishing " + (persistent ? "persistent" : "non-persistent") + " messages of " + size + " bytes, " + Config.getAckModeDescription(ackMode) + ".");
+ }
+
+ private void test(Config config) throws Exception
+ {
+ test(config.getBatch(), config.getDelay(), config.getMessages(), config.getClients(), config.getWarmup());
+ }
+
+ private void test(int batches, long delay, int msgCount, int consumerCount, int warmup) throws Exception
+ {
+ _factory.createControlConsumer().setMessageListener(this);
+ _connection.start();
+
+ if(warmup > 0)
+ {
+ System.out.println("Runing warmup (" + warmup + " msgs)");
+ long time = batch(warmup, consumerCount);
+ System.out.println("Warmup completed in " + time + "ms");
+ }
+
+ long[] times = new long[batches];
+ for(int i = 0; i < batches; i++)
+ {
+ if(i > 0) Thread.sleep(delay*1000);
+ times[i] = batch(msgCount, consumerCount);
+ System.out.println("Batch " + (i+1) + " of " + batches + " completed in " + times[i] + " ms.");
+ }
+
+ long min = min(times);
+ long max = max(times);
+ System.out.println("min: " + min + ", max: " + max + " avg: " + avg(times, min, max));
+
+ //request shutdown
+ _publisher.send(_factory.createShutdownMessage());
+
+ _connection.stop();
+ _connection.close();
+ }
+
+ private long batch(int msgCount, int consumerCount) throws Exception
+ {
+ _count = consumerCount;
+ long start = System.currentTimeMillis();
+ publish(msgCount);
+ waitForCompletion(consumerCount);
+ return System.currentTimeMillis() - start;
+ }
+
+ private void publish(int count) throws Exception
+ {
+
+ //send events
+ for (int i = 0; i < count; i++)
+ {
+ _publisher.send(_factory.createEventMessage());
+ if ((i + 1) % 100 == 0)
+ {
+ System.out.println("Sent " + (i + 1) + " messages");
+ }
+ }
+
+ //request report
+ _publisher.send(_factory.createReportRequestMessage());
+ }
+
+ private void waitForCompletion(int consumers) throws Exception
+ {
+ System.out.println("Waiting for completion...");
+ synchronized (_lock)
+ {
+ while (_count > 0)
+ {
+ _lock.wait();
+ }
+ }
+ }
+
+
+ public void onMessage(Message message)
+ {
+ System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining");
+ if (_count == 0)
+ {
+ synchronized (_lock)
+ {
+ _lock.notify();
+ }
+ }
+ }
+
+ static long min(long[] times)
+ {
+ long min = times.length > 0 ? times[0] : 0;
+ for(int i = 0; i < times.length; i++)
+ {
+ min = Math.min(min, times[i]);
+ }
+ return min;
+ }
+
+ static long max(long[] times)
+ {
+ long max = times.length > 0 ? times[0] : 0;
+ for(int i = 0; i < times.length; i++)
+ {
+ max = Math.max(max, times[i]);
+ }
+ return max;
+ }
+
+ static long avg(long[] times, long min, long max)
+ {
+ long sum = 0;
+ for(int i = 0; i < times.length; i++)
+ {
+ sum += times[i];
+ }
+ sum -= min;
+ sum -= max;
+
+ return (sum / (times.length - 2));
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config();
+ config.setOptions(argv);
+
+ Connection con = config.createConnection();
+ int size = config.getPayload();
+ int ackMode = config.getAckMode();
+ boolean persistent = config.usePersistentMessages();
+ new Publisher(con, size, ackMode, persistent).test(config);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java
new file mode 100644
index 0000000000..bd104e5407
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java
@@ -0,0 +1,110 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transacted;
+
+import org.apache.qpid.config.ConnectorConfig;
+import org.apache.qpid.config.AbstractConfig;
+import org.apache.qpid.config.Connector;
+
+import javax.jms.Connection;
+
+class Config extends AbstractConfig implements ConnectorConfig
+{
+ private String host = "localhost";
+ private int port = 5672;
+ private String factory;
+ private boolean echo;
+ private int batch = 100;
+ private boolean persistent = true;
+
+ Config(String[] argv)
+ {
+ setOptions(argv);
+ }
+
+ Connection createConnection() throws Exception
+ {
+ return new Connector().createConnection(this);
+ }
+
+ public boolean isEchoOn()
+ {
+ return echo;
+ }
+
+ public boolean usePersistentMessages()
+ {
+ return persistent;
+ }
+
+ public int getBatchSize()
+ {
+ return batch;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ public String getFactory()
+ {
+ return factory;
+ }
+
+ public void setOption(String key, String value)
+ {
+ if("-host".equalsIgnoreCase(key))
+ {
+ host = value;
+ }
+ else if("-port".equalsIgnoreCase(key))
+ {
+ port = parseInt("Bad port number", value);
+ }
+ else if("-factory".equalsIgnoreCase(key))
+ {
+ factory = value;
+ }
+ else if("-echo".equalsIgnoreCase(key))
+ {
+ echo = "true".equalsIgnoreCase(value);
+ }
+ else if("-persistent".equalsIgnoreCase(key))
+ {
+ persistent = "true".equalsIgnoreCase(value);
+ }
+ else if("-batch".equalsIgnoreCase(key))
+ {
+ batch = parseInt("Bad batch size", value);
+ }
+ else
+ {
+ System.out.println("Ignoring nrecognised option " + key);
+ }
+ }
+
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java
new file mode 100644
index 0000000000..8f15bf089e
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transacted;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.client.AMQQueue;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import java.util.Arrays;
+
+public class Ping
+{
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config(argv);
+ Connection con = config.createConnection();
+ con.setClientID("ping");
+ new Relay(new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("ping")), new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("pong")), con,
+ config.isEchoOn(),
+ config.getBatchSize(),
+ config.usePersistentMessages()).start();
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java
new file mode 100644
index 0000000000..f4f4b20d7c
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transacted;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.client.AMQQueue;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+
+public class Pong
+{
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config(argv);
+ Connection con = config.createConnection();
+ con.setClientID("pong");
+ new Relay(new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("pong")), new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("ping")), con,
+ config.isEchoOn(),
+ config.getBatchSize(),
+ config.usePersistentMessages()).start();
+
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java
new file mode 100644
index 0000000000..cede95e5f0
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java
@@ -0,0 +1,127 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transacted;
+
+import org.apache.qpid.client.AMQSession;
+
+import javax.jms.MessageProducer;
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+import javax.jms.Destination;
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.TextMessage;
+import javax.jms.DeliveryMode;
+
+class Relay implements Runnable
+{
+ private final Connection _con;
+ private final Session _session;
+ private final MessageConsumer _src;
+ private final MessageProducer _dest;
+ private final int _batch;
+ private final boolean _echo;
+ private int _counter;
+ private long start;
+ private boolean _running;
+
+ Relay(Destination src, Destination dest, Connection con) throws JMSException
+ {
+ this(src, dest, con, false, 100, true);
+ }
+
+ Relay(Destination src, Destination dest, Connection con, boolean echo, int batch, boolean persistent) throws JMSException
+ {
+ _echo = echo;
+ _batch = batch;
+ _con = con;
+ _session = con.createSession(true, AMQSession.NO_ACKNOWLEDGE);
+ _src = _session.createConsumer(src);
+ _dest = _session.createProducer(dest);
+ _dest.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
+
+ }
+
+ public void run()
+ {
+ start = System.currentTimeMillis();
+ try{
+ while(true) relay();
+ }
+ catch(JMSException e)
+ {
+ e.printStackTrace();
+ }
+ try
+ {
+ _session.close();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ void relay() throws JMSException
+ {
+ _dest.send(relay(_src.receive()));
+ _session.commit();
+ }
+
+ Message relay(Message in) throws JMSException
+ {
+ if(!_running)
+ {
+ System.out.println(_con.getClientID() + " started.");
+ _running = true;
+ }
+ if(++_counter % _batch == 0)
+ {
+ long time = System.currentTimeMillis() - start;
+ System.out.println(_batch + " iterations performed in " + time + " ms");
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ start = System.currentTimeMillis();
+ }
+ if(_echo)
+ {
+ System.out.println("Received: " + ((TextMessage) in).getText());
+ }
+ return _session.createTextMessage(_con.getClientID() + _counter);
+ }
+
+ void start() throws InterruptedException, JMSException
+ {
+ Thread runner = new Thread(this);
+ runner.start();
+ _con.start();
+ System.out.println(_con.getClientID() + " waiting...");
+ runner.join();
+ _con.close();
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java
new file mode 100644
index 0000000000..de718d828a
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.transacted;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.client.AMQQueue;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Session;
+
+public class Start
+{
+ public static void main(String[] argv) throws Exception
+ {
+ Connection con = new Config(argv).createConnection();
+ AMQQueue ping = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("ping"));
+ Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ session.createProducer(ping).send(session.createTextMessage("start"));
+ session.close();
+ con.close();
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java b/Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java
new file mode 100644
index 0000000000..71d806b338
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java
@@ -0,0 +1,151 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.weblogic;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+
+import javax.jms.*;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.Context;
+import java.net.InetAddress;
+import java.util.Hashtable;
+
+public class ServiceProvider
+{
+ private static final String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
+ private static final String JMS_FACTORY = "transientJMSConnectionFactory";
+
+ private static final Logger _logger = Logger.getLogger(ServiceProvider.class);
+
+ private static MessageProducer _destinationProducer;
+
+ private static Queue _destinationQ;
+
+ public static void main(String[] args)
+ {
+ _logger.info("Starting...");
+
+ if (args.length != 2)
+ {
+ System.out.println("Usage: <WLS URI> <service queue>");
+ System.exit(1);
+ }
+ try
+ {
+ String url = args[0];
+ String receiveQueue = args[1];
+
+ final InitialContext ctx = getInitialContext(url);
+
+ QueueConnectionFactory qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
+ QueueConnection qcon = qconFactory.createQueueConnection();
+ final QueueSession qsession = qcon.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue receiveQ = (Queue) ctx.lookup(receiveQueue);
+
+ _logger.info("Service (queue) name is '" + receiveQ + "'...");
+
+ String selector = (args.length > 2 && args[2] != null && args[2].length() > 1) ? args[2] : null;
+
+ _logger.info("Message selector is <" + selector + ">...");
+
+ MessageConsumer consumer = qsession.createConsumer(receiveQ, selector);
+
+ consumer.setMessageListener(new MessageListener()
+ {
+ private int _messageCount;
+
+ public void onMessage(javax.jms.Message message)
+ {
+ //_logger.info("Got message '" + message + "'");
+
+ TextMessage tm = (TextMessage) message;
+
+ try
+ {
+ Queue responseQueue = (Queue)tm.getJMSReplyTo();
+ if (!responseQueue.equals(_destinationQ))
+ {
+ _destinationQ = responseQueue;
+ _logger.info("Creating destination for " + responseQueue);
+
+ try
+ {
+ _destinationProducer = qsession.createProducer(_destinationQ);
+ _destinationProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+ }
+ catch (JMSException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ _messageCount++;
+ if (_messageCount % 1000 == 0)
+ {
+ _logger.info("Received message total: " + _messageCount);
+ _logger.info("Sending response to '" + responseQueue + "'");
+ }
+
+ String payload = "This is a response: sing together: 'Mahnah mahnah...'" + tm.getText();
+ TextMessage msg = qsession.createTextMessage(payload);
+ if (tm.propertyExists("timeSent"))
+ {
+ _logger.info("timeSent property set on message");
+ final long timeSent = tm.getLongProperty("timeSent");
+ msg.setLongProperty("timeSent", timeSent);
+ _logger.info("time taken to go from service request to provider is: " + (System.currentTimeMillis() - timeSent));
+ }
+ _destinationProducer.send(msg);
+ if (_messageCount % 1000 == 0)
+ {
+ tm.acknowledge();
+ _logger.info("Sent response to '" + responseQueue + "'");
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error sending message: " + e, e);
+ }
+ }
+ });
+ qcon.start();
+ }
+ catch (Throwable t)
+ {
+ System.err.println("Fatal error: " + t);
+ t.printStackTrace();
+ }
+
+
+ System.out.println("Waiting...");
+ }
+
+ private static InitialContext getInitialContext(String url) throws NamingException
+ {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
+ env.put(Context.PROVIDER_URL, url);
+ return new InitialContext(env);
+ }
+}
diff --git a/Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java b/Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java
new file mode 100644
index 0000000000..2f64a1dde5
--- /dev/null
+++ b/Final/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java
@@ -0,0 +1,185 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.weblogic;
+
+import org.apache.log4j.Logger;
+
+import javax.jms.*;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import java.util.Hashtable;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: U806869
+ * Date: 28-May-2005
+ * Time: 21:54:51
+ * To change this template use File | Settings | File Templates.
+ */
+public class ServiceRequestingClient
+{
+ private static final Logger _log = Logger.getLogger(ServiceRequestingClient.class);
+ private static final String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
+ private static final String JMS_FACTORY = "transientJMSConnectionFactory";
+
+ private static class CallbackHandler implements MessageListener
+ {
+ private int _expectedMessageCount;
+
+ private int _actualMessageCount;
+
+ private long _startTime;
+
+ private long _averageLatency;
+
+ public CallbackHandler(int expectedMessageCount, long startTime)
+ {
+ _expectedMessageCount = expectedMessageCount;
+ _startTime = startTime;
+ }
+
+ public void onMessage(Message m)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Message received: " + m);
+ }
+ try
+ {
+ if (m.propertyExists("timeSent"))
+ {
+ long timeSent = m.getLongProperty("timeSent");
+ long now = System.currentTimeMillis();
+ if (_averageLatency == 0)
+ {
+ _averageLatency = now - timeSent;
+ _log.info("Latency " + _averageLatency);
+ }
+ else
+ {
+ _log.info("Individual latency: " + (now-timeSent));
+ _averageLatency = (_averageLatency + (now - timeSent))/2;
+ _log.info("Average latency now: " + _averageLatency);
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ _log.error("Could not calculate latency");
+ }
+
+ _actualMessageCount++;
+ if (_actualMessageCount%1000 == 0)
+ {
+ try
+ {
+ m.acknowledge();
+ }
+ catch (JMSException e)
+ {
+ _log.error("Error acknowledging message");
+ }
+ _log.info("Received message count: " + _actualMessageCount);
+ }
+ /*if (!"henson".equals(m.toString()))
+ {
+ _log.error("Message response not correct: expected 'henson' but got " + m.toString());
+ }
+ else
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Message " + m + " received");
+ }
+ else
+ {
+ _log.info("Message received");
+ }
+ } */
+
+ if (_actualMessageCount == _expectedMessageCount)
+ {
+ long timeTaken = System.currentTimeMillis() - _startTime;
+ System.out.println("Total time taken to receive " + _expectedMessageCount+ " messages was " +
+ timeTaken + "ms, equivalent to " +
+ (_expectedMessageCount/(timeTaken/1000.0)) + " messages per second");
+ System.out.println("Average latency is: " + _averageLatency);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception
+ {
+ if (args.length != 3)
+ {
+ System.out.println("Usage: IXPublisher <WLS URL> <sendQueue> <count> will publish count messages to ");
+ System.out.println("queue sendQueue and waits for a response on a temp queue");
+ System.exit(1);
+ }
+
+ String url = args[0];
+ String sendQueue = args[1];
+ int messageCount = Integer.parseInt(args[2]);
+
+ InitialContext ctx = getInitialContext(url);
+
+ QueueConnectionFactory qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
+ QueueConnection qcon = qconFactory.createQueueConnection();
+ QueueSession qsession = qcon.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue sendQ = (Queue) ctx.lookup(sendQueue);
+ Queue receiveQ = qsession.createTemporaryQueue();
+ QueueSender qsender = qsession.createSender(sendQ);
+ qsender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+ _log.debug("Queue sender created for service queue " + sendQ);
+
+ javax.jms.MessageConsumer messageConsumer = (javax.jms.MessageConsumer) qsession.createConsumer(receiveQ);
+
+ //TextMessage msg = _session.createTextMessage(tempDestination.getQueueName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths");
+ final long startTime = System.currentTimeMillis();
+
+ messageConsumer.setMessageListener(new CallbackHandler(messageCount, startTime));
+ qcon.start();
+ for (int i = 0; i < messageCount; i++)
+ {
+ TextMessage msg = qsession.createTextMessage("/Presented to in conjunction with Mahnah Mahnah and the Snowths:" + i);
+ msg.setJMSReplyTo(receiveQ);
+ if (i%1000 == 0)
+ {
+ long timeNow = System.currentTimeMillis();
+ msg.setLongProperty("timeSent", timeNow);
+ }
+ qsender.send(msg);
+ }
+
+ new Thread("foo").start();
+ //qsession.close();
+ //qcon.close();
+ }
+
+ private static InitialContext getInitialContext(String url) throws NamingException
+ {
+ Hashtable env = new Hashtable();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
+ env.put(Context.PROVIDER_URL, url);
+ return new InitialContext(env);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java b/Final/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java
new file mode 100644
index 0000000000..5323ad28bf
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.vmpipe.support;
+
+import org.apache.mina.common.IdleStatus;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This file is a patch to override MINA, because of the IdentityHashMap bug. Workaround to be supplied in MINA 1.0.7.
+ * This patched file will be removed once upgraded onto a newer MINA.
+ *
+ * Dectects idle sessions and fires <tt>sessionIdle</tt> events to them.
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ */
+public class VmPipeIdleStatusChecker
+{
+ private static final VmPipeIdleStatusChecker INSTANCE = new VmPipeIdleStatusChecker();
+
+ public static VmPipeIdleStatusChecker getInstance()
+ {
+ return INSTANCE;
+ }
+
+ private final Map sessions = new HashMap(); // will use as a set
+
+ private final Worker worker = new Worker();
+
+ private VmPipeIdleStatusChecker()
+ {
+ worker.start();
+ }
+
+ public void addSession(VmPipeSessionImpl session)
+ {
+ synchronized (sessions)
+ {
+ sessions.put(session, session);
+ }
+ }
+
+ private class Worker extends Thread
+ {
+ private Worker()
+ {
+ super("VmPipeIdleStatusChecker");
+ setDaemon(true);
+ }
+
+ public void run()
+ {
+ for (;;)
+ {
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ { }
+
+ long currentTime = System.currentTimeMillis();
+
+ synchronized (sessions)
+ {
+ Iterator it = sessions.keySet().iterator();
+ while (it.hasNext())
+ {
+ VmPipeSessionImpl session = (VmPipeSessionImpl) it.next();
+ if (!session.isConnected())
+ {
+ it.remove();
+ }
+ else
+ {
+ notifyIdleSession(session, currentTime);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void notifyIdleSession(VmPipeSessionImpl session, long currentTime)
+ {
+ notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.BOTH_IDLE), IdleStatus.BOTH_IDLE,
+ Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE)));
+ notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.READER_IDLE), IdleStatus.READER_IDLE,
+ Math.max(session.getLastReadTime(), session.getLastIdleTime(IdleStatus.READER_IDLE)));
+ notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.WRITER_IDLE), IdleStatus.WRITER_IDLE,
+ Math.max(session.getLastWriteTime(), session.getLastIdleTime(IdleStatus.WRITER_IDLE)));
+ }
+
+ private void notifyIdleSession0(VmPipeSessionImpl session, long currentTime, long idleTime, IdleStatus status,
+ long lastIoTime)
+ {
+ if ((idleTime > 0) && (lastIoTime != 0) && ((currentTime - lastIoTime) >= idleTime))
+ {
+ session.increaseIdleCount(status);
+ session.getFilterChain().fireSessionIdle(session, status);
+ }
+ }
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java b/Final/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
new file mode 100644
index 0000000000..fe418535d6
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
@@ -0,0 +1,152 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.TextMessage;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class tests all the alerts an AMQQueue can throw based on threshold
+ * values of different parameters
+ */
+public class AMQQueueDeferredOrderingTest extends TestCase
+{
+
+ private static final int NUM_MESSAGES = 1000;
+
+ private AMQConnection con;
+ private Session session;
+ private AMQQueue queue;
+ private MessageConsumer consumer;
+
+ private static final Logger _logger = LoggerFactory.getLogger(AMQQueueDeferredOrderingTest.class);
+
+ private ASyncProducer producerThread;
+ private static final String BROKER = "vm://:1";
+
+ private class ASyncProducer extends Thread
+ {
+
+ private MessageProducer producer;
+ private final Logger _logger = LoggerFactory.getLogger(ASyncProducer.class);
+ private Session session;
+ private int start;
+ private int end;
+
+ public ASyncProducer(AMQQueue q, int start, int end) throws Exception
+ {
+ this.session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ this._logger.info("Create Consumer of Q1");
+ this.producer = this.session.createProducer(q);
+ this.start = start;
+ this.end = end;
+ }
+
+ public void run()
+ {
+ try
+ {
+ this._logger.info("Starting to send messages");
+ for (int i = start; i < end && !interrupted(); i++)
+ {
+ producer.send(session.createTextMessage(Integer.toString(i)));
+ }
+ this._logger.info("Sent " + (end - start) + " messages");
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+
+ _logger.info("Create Connection");
+ con = new AMQConnection(BROKER, "guest", "guest", "OrderingTest", "test");
+ _logger.info("Create Session");
+ session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _logger.info("Create Q");
+ queue = new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q"), new AMQShortString("Q"),
+ false, true);
+ _logger.info("Create Consumer of Q");
+ consumer = session.createConsumer(queue);
+ _logger.info("Start Connection");
+ con.start();
+ }
+
+ public void testPausedOrder() throws Exception
+ {
+
+ // Setup initial messages
+ _logger.info("Creating first producer thread");
+ producerThread = new ASyncProducer(queue, 0, NUM_MESSAGES / 2);
+ producerThread.start();
+ // Wait for them to be done
+ producerThread.join();
+
+ // Setup second set of messages to produce while we consume
+ _logger.info("Creating second producer thread");
+ producerThread = new ASyncProducer(queue, NUM_MESSAGES / 2, NUM_MESSAGES);
+ producerThread.start();
+
+ // Start consuming and checking they're in order
+ _logger.info("Consuming messages");
+ for (int i = 0; i < NUM_MESSAGES; i++)
+ {
+ Message msg = consumer.receive(3000);
+ assertNotNull("Message should not be null", msg);
+ assertTrue("Message should be a text message", msg instanceof TextMessage);
+ assertEquals("Message content does not match expected", Integer.toString(i), ((TextMessage) msg).getText());
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _logger.info("Interuptting producer thread");
+ producerThread.interrupt();
+ _logger.info("Closing connection");
+ con.close();
+
+ TransportConnection.killAllVMBrokers();
+ super.tearDown();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(AMQQueueDeferredOrderingTest.class);
+ }
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java b/Final/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java
new file mode 100644
index 0000000000..7cca22de6c
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery queue
+ * <p/>
+ * The message delivery process:
+ * Mina puts a message on _queue in AMQSession and the dispatcher thread take()s
+ * from here and dispatches to the _consumers. If the _consumer doesn't have a message listener set at connection start
+ * then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple consumers on a
+ * session can run in any order and a synchronous put/poll will block the dispatcher).
+ * <p/>
+ * When setting the message listener later the _synchronousQueue is just poll()'ed and the first message delivered
+ * the remaining messages will be left on the queue and lost, subsequent messages on the session will arrive first.
+ */
+public class DispatcherTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(DispatcherTest.class);
+
+ Context _context;
+
+ private static final int MSG_COUNT = 6;
+ private int _receivedCount = 0;
+ private int _receivedCountWhileStopped = 0;
+ private Connection _clientConnection, _producerConnection;
+ private MessageConsumer _consumer;
+ MessageProducer _producer;
+ Session _clientSession, _producerSession;
+
+ private final CountDownLatch _allFirstMessagesSent = new CountDownLatch(1); // all messages Sent Lock
+ private final CountDownLatch _allSecondMessagesSent = new CountDownLatch(1); // all messages Sent Lock
+
+ private volatile boolean _connectionStopped = false;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/test?brokerlist='vm://:1'");
+ env.put("queue.queue", "MessageListenerTest");
+
+ _context = factory.getInitialContext(env);
+
+ Queue queue = (Queue) _context.lookup("queue");
+
+ // Create Client 1
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _consumer = _clientSession.createConsumer(queue);
+
+ // Create Producer
+ _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _producerConnection.start();
+
+ _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _producer = _producerSession.createProducer(queue);
+
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ _producer.send(_producerSession.createTextMessage("Message " + msg));
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+
+ _clientConnection.close();
+
+ _producerConnection.close();
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testAsynchronousRecieve()
+ {
+ _logger.info("Test Start");
+
+ assertTrue(!((AMQConnection) _clientConnection).started());
+
+ // Set default Message Listener
+ try
+ {
+ _consumer.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _logger.info("Client 1 ML 1 Received Message(" + _receivedCount + "):" + message);
+
+ _receivedCount++;
+
+ if (_receivedCount == MSG_COUNT)
+ {
+ _allFirstMessagesSent.countDown();
+ }
+
+ if (_connectionStopped)
+ {
+ _logger.info("Running with Message:" + _receivedCount);
+ }
+
+ if (_connectionStopped && (_allFirstMessagesSent.getCount() == 0))
+ {
+ _receivedCountWhileStopped++;
+ }
+
+ if (_allFirstMessagesSent.getCount() == 0)
+ {
+ if (_receivedCount == (MSG_COUNT * 2))
+ {
+ _allSecondMessagesSent.countDown();
+ }
+ }
+ }
+ });
+
+ assertTrue("Connecion should not be started", !((AMQConnection) _clientConnection).started());
+ _clientConnection.start();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error Setting Default ML on consumer1");
+ }
+
+ try
+ {
+ _allFirstMessagesSent.await(1000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ try
+ {
+ assertTrue("Connecion should be started", ((AMQConnection) _clientConnection).started());
+ _clientConnection.stop();
+ _connectionStopped = true;
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error stopping connection");
+ }
+
+ try
+ {
+ _logger.error("Send additional messages");
+
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ _producer.send(_producerSession.createTextMessage("Message " + msg));
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Unable to send additional messages", e);
+ }
+
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ // ignore
+ }
+
+ try
+ {
+ _logger.info("Restarting connection");
+
+ _connectionStopped = false;
+ _clientConnection.start();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error Setting Better ML on consumer1", e);
+ }
+
+ _logger.info("Waiting upto 2 seconds for messages");
+
+ try
+ {
+ _allSecondMessagesSent.await(1000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ assertEquals("Messages not received correctly", 0, _allFirstMessagesSent.getCount());
+ assertEquals("Messages not received correctly", 0, _allSecondMessagesSent.getCount());
+ assertEquals("Client didn't get all messages", MSG_COUNT * 2, _receivedCount);
+ assertEquals("Messages received while stopped is not 0", 0, _receivedCountWhileStopped);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(DispatcherTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java b/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java
new file mode 100644
index 0000000000..7461f6c200
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client;
+
+/**
+ * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery
+ * queue <p/> The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread
+ * take()s from here and dispatches to the _consumers. If the _consumer1 doesn't have a message listener set at
+ * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple
+ * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher). <p/> When setting
+ * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining
+ * messages will be left on the queue and lost, subsequent messages on the session will arrive first.
+ */
+public class MessageListenerMultiConsumerImmediatePrefetch extends MessageListenerMultiConsumerTest
+{
+ protected void setUp() throws Exception
+ {
+ System.setProperty(AMQSession.IMMEDIATE_PREFETCH, "true");
+ super.setUp();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MessageListenerMultiConsumerImmediatePrefetch.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java b/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
new file mode 100644
index 0000000000..20632e245f
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery
+ * queue <p/> The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread
+ * take()s from here and dispatches to the _consumers. If the _consumer1 doesn't have a message listener set at
+ * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple
+ * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher). <p/> When setting
+ * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining
+ * messages will be left on the queue and lost, subsequent messages on the session will arrive first.
+ */
+public class MessageListenerMultiConsumerTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MessageListenerMultiConsumerTest.class);
+
+ Context _context;
+
+ private static final int MSG_COUNT = 6;
+ private int receivedCount1 = 0;
+ private int receivedCount2 = 0;
+ private Connection _clientConnection;
+ private MessageConsumer _consumer1;
+ private MessageConsumer _consumer2;
+ private Session _clientSession1;
+ private Queue _queue;
+ private final CountDownLatch _allMessagesSent = new CountDownLatch(2); // all messages Sent Lock
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/test?brokerlist='vm://:1'");
+ env.put("queue.queue", "direct://amq.direct//" + this.getClass().getName());
+
+ _context = factory.getInitialContext(env);
+
+ _queue = (Queue) _context.lookup("queue");
+
+ // Create Client 1
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession1 = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _consumer1 = _clientSession1.createConsumer(_queue);
+
+ // Create Client 2
+ Session clientSession2 = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _consumer2 = clientSession2.createConsumer(_queue);
+
+ // Create Producer
+ 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();
+
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _clientConnection.close();
+
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testRecieveInterleaved() throws Exception
+ {
+ int msg = 0;
+ int MAX_LOOPS = MSG_COUNT * 2;
+ for (int loops = 0; (msg < MSG_COUNT) || (loops < MAX_LOOPS); loops++)
+ {
+
+ if (_consumer1.receive(100) != null)
+ {
+ msg++;
+ }
+
+ if (_consumer2.receive(100) != null)
+ {
+ msg++;
+ }
+ }
+
+ assertEquals("Not all messages received.", MSG_COUNT, msg);
+ }
+
+ public void testAsynchronousRecieve() throws Exception
+ {
+ _consumer1.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _logger.info("Client 1 Received Message(" + receivedCount1 + "):" + message);
+
+ receivedCount1++;
+
+ if (receivedCount1 == (MSG_COUNT / 2))
+ {
+ _allMessagesSent.countDown();
+ }
+
+ }
+ });
+
+ _consumer2.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _logger.info("Client 2 Received Message(" + receivedCount2 + "):" + message);
+
+ receivedCount2++;
+ if (receivedCount2 == (MSG_COUNT / 2))
+ {
+ _allMessagesSent.countDown();
+ }
+ }
+ });
+
+ _logger.info("Waiting upto 2 seconds for messages");
+
+ try
+ {
+ _allMessagesSent.await(4000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ assertEquals(MSG_COUNT, receivedCount1 + receivedCount2);
+ }
+
+ public void testRecieveC2Only() throws Exception
+ {
+ if (
+ !Boolean.parseBoolean(
+ System.getProperties().getProperty(AMQSession.IMMEDIATE_PREFETCH,
+ AMQSession.IMMEDIATE_PREFETCH_DEFAULT)))
+ {
+ _logger.info("Performing Receive only on C2");
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ assertTrue(MSG_COUNT + " msg should be received. Only received:" + msg, _consumer2.receive(1000) != null);
+ }
+ }
+ }
+
+ public void testRecieveBoth() throws Exception
+ {
+ if (
+ !Boolean.parseBoolean(
+ System.getProperties().getProperty(AMQSession.IMMEDIATE_PREFETCH,
+ AMQSession.IMMEDIATE_PREFETCH_DEFAULT)))
+ {
+ _logger.info("Performing Receive only with two consumers on one session ");
+
+ MessageConsumer consumer2 = _clientSession1.createConsumer(_queue);
+
+ for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
+ {
+
+ assertTrue(_consumer1.receive() != null);
+ }
+
+ for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
+ {
+ assertTrue(consumer2.receive() != null);
+ }
+ }
+ else
+ {
+ _logger.info("Performing Receive only on both C1 and C2");
+
+ for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
+ {
+
+ assertTrue(_consumer1.receive() != null);
+ }
+
+ for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
+ {
+ assertTrue(_consumer2.receive() != null);
+ }
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MessageListenerMultiConsumerTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java b/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java
new file mode 100644
index 0000000000..87630fad5b
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery
+ * queue <p/> The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread
+ * take()s from here and dispatches to the _consumers. If the _consumer doesn't have a message listener set at
+ * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple
+ * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher). <p/> When setting
+ * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining
+ * messages will be left on the queue and lost, subsequent messages on the session will arrive first.
+ */
+public class MessageListenerTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MessageListenerTest.class);
+
+ Context _context;
+
+ private static final int MSG_COUNT = 5;
+ private int receivedCount = 0;
+ private MessageConsumer _consumer;
+ private Connection _clientConnection;
+ private CountDownLatch _awaitMessages = new CountDownLatch(MSG_COUNT);
+ private static final String BROKER = "vm://:1";
+ private static final String VHOST = "test";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ if (BROKER.contains("vm://"))
+ {
+ TransportConnection.createVMBroker(1);
+ }
+
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/" + VHOST + "?brokerlist='" + BROKER + "'");
+ env.put("queue.queue", "MessageListenerTest");
+
+ _context = factory.getInitialContext(env);
+
+ Queue queue = (Queue) _context.lookup("queue");
+
+ // Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ Session clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _consumer = clientSession.createConsumer(queue);
+
+ // Create Producer
+
+ 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();
+
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _clientConnection.close();
+
+ super.tearDown();
+ if (BROKER.contains("vm://"))
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+ }
+
+ public void testSynchronousRecieve() throws Exception
+ {
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ assertTrue(_consumer.receive(2000) != null);
+ }
+ }
+
+ public void testAsynchronousRecieve() throws Exception
+ {
+ _consumer.setMessageListener(this);
+
+ _logger.info("Waiting 3 seconds for messages");
+
+ try
+ {
+ _awaitMessages.await(3000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+ // Should have recieved all async messages
+ assertEquals(MSG_COUNT, receivedCount);
+
+ }
+
+ public void testRecieveTheUseMessageListener() throws Exception
+ {
+
+ _logger.error("Test disabled as initial receive is not called first");
+ // Perform initial receive to start connection
+ // assertTrue(_consumer.receive(2000) != null);
+ // receivedCount++;
+
+ // Sleep to ensure remaining 4 msgs end up on _synchronousQueue
+ // Thread.sleep(1000);
+
+ // Set the message listener and wait for the messages to come in.
+ _consumer.setMessageListener(this);
+
+ _logger.info("Waiting 3 seconds for messages");
+
+ try
+ {
+ _awaitMessages.await(3000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+ // Should have recieved all async messages
+ assertEquals(MSG_COUNT, receivedCount);
+
+ }
+
+ public void onMessage(Message message)
+ {
+ _logger.info("Received Message(" + receivedCount + "):" + message);
+
+ receivedCount++;
+ _awaitMessages.countDown();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MessageListenerTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java b/Final/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
new file mode 100644
index 0000000000..21f3e273aa
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.client;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery
+ * queue <p/> The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread
+ * take()s from here and dispatches to the _consumers. If the _consumer1 doesn't have a message listener set at
+ * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple
+ * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher). <p/> When setting
+ * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining
+ * messages will be left on the queue and lost, subsequent messages on the session will arrive first.
+ */
+public class ResetMessageListenerTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ResetMessageListenerTest.class);
+
+ Context _context;
+
+ private static final int MSG_COUNT = 6;
+ private int receivedCount1ML1 = 0;
+ private int receivedCount1ML2 = 0;
+ private int receivedCount2 = 0;
+ private Connection _clientConnection, _producerConnection;
+ private MessageConsumer _consumer1;
+ private MessageConsumer _consumer2;
+ MessageProducer _producer;
+ Session _clientSession, _producerSession;
+
+ private final CountDownLatch _allFirstMessagesSent = new CountDownLatch(2); // all messages Sent Lock
+ private final CountDownLatch _allSecondMessagesSent = new CountDownLatch(2); // all messages Sent Lock
+
+ private String oldImmediatePrefetch;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+
+ oldImmediatePrefetch = System.getProperty(AMQSession.IMMEDIATE_PREFETCH);
+ System.setProperty(AMQSession.IMMEDIATE_PREFETCH, "true");
+
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/test?brokerlist='vm://:1'");
+ env.put("queue.queue", "direct://amq.direct//ResetMessageListenerTest");
+
+ _context = factory.getInitialContext(env);
+
+ Queue queue = (Queue) _context.lookup("queue");
+
+ // Create Client 1
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _consumer1 = _clientSession.createConsumer(queue);
+
+ // Create Client 2 on same session
+ _consumer2 = _clientSession.createConsumer(queue);
+
+ // Create Producer
+ _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _producerConnection.start();
+
+ _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _producer = _producerSession.createProducer(queue);
+
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ _producer.send(_producerSession.createTextMessage("Message " + msg));
+ }
+
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _clientConnection.close();
+ _producerConnection.close();
+
+ super.tearDown();
+ if (oldImmediatePrefetch == null)
+ {
+ oldImmediatePrefetch = AMQSession.IMMEDIATE_PREFETCH_DEFAULT;
+ }
+ System.setProperty(AMQSession.IMMEDIATE_PREFETCH, oldImmediatePrefetch);
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testAsynchronousRecieve()
+ {
+
+ _logger.info("Test Start");
+
+ // Set default Message Listener
+ try
+ {
+ _consumer1.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _logger.info("Client 1 ML 1 Received Message(" + receivedCount1ML1 + "):" + message);
+
+ receivedCount1ML1++;
+ if (receivedCount1ML1 == (MSG_COUNT / 2))
+ {
+ _allFirstMessagesSent.countDown();
+ }
+ }
+ });
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error Setting Default ML on consumer1");
+ }
+
+ try
+ {
+ _consumer2.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _logger.info("Client 2 Received Message(" + receivedCount2 + "):" + message);
+
+ receivedCount2++;
+ if (receivedCount2 == (MSG_COUNT / 2))
+ {
+ _logger.info("Client 2 received all its messages1");
+ _allFirstMessagesSent.countDown();
+ }
+
+ if (receivedCount2 == MSG_COUNT)
+ {
+ _logger.info("Client 2 received all its messages2");
+ _allSecondMessagesSent.countDown();
+ }
+ }
+ });
+
+ _clientConnection.start();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error Setting Default ML on consumer2");
+
+ }
+
+ try
+ {
+ _allFirstMessagesSent.await(1000, TimeUnit.MILLISECONDS);
+ _logger.info("Received first batch of messages");
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ try
+ {
+ _clientConnection.stop();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error stopping connection");
+ }
+
+ _logger.info("Reset Message Listener to better listener while connection stopped, will restart session");
+ try
+ {
+ _consumer1.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _logger.info("Client 1 ML2 Received Message(" + receivedCount1ML1 + "):" + message);
+
+ receivedCount1ML2++;
+ if (receivedCount1ML2 == (MSG_COUNT / 2))
+ {
+ _allSecondMessagesSent.countDown();
+ }
+ }
+ });
+
+ _clientConnection.start();
+ }
+ catch (javax.jms.IllegalStateException e)
+ {
+ _logger.error("Connection not stopped while setting ML", e);
+ fail("Unable to change message listener:" + e.getCause());
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error Setting Better ML on consumer1", e);
+ }
+
+ try
+ {
+ _logger.info("Send additional messages");
+
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ _producer.send(_producerSession.createTextMessage("Message " + msg));
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Unable to send additional messages", e);
+ }
+
+ _logger.info("Waiting upto 2 seconds for messages");
+
+ try
+ {
+ _allSecondMessagesSent.await(5000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+ assertEquals("First batch of messages not received correctly", 0, _allFirstMessagesSent.getCount());
+ assertEquals("Second batch of messages not received correctly", 0, _allSecondMessagesSent.getCount());
+ assertEquals("Client 1 ML1 didn't get all messages", MSG_COUNT / 2, receivedCount1ML1);
+ assertEquals("Client 2 didn't get all messages", MSG_COUNT, receivedCount2);
+ assertEquals("Client 1 ML2 didn't get all messages", MSG_COUNT / 2, receivedCount1ML2);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ResetMessageListenerTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java b/Final/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java
new file mode 100644
index 0000000000..4cffcecf8a
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java
@@ -0,0 +1,71 @@
+package org.apache.qpid.framing;
+
+import junit.framework.TestCase;
+import org.apache.qpid.client.state.listener.SpecificMethodFrameListener;
+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 class SpecificMethodFrameListenerTest extends TestCase
+{
+
+ SpecificMethodFrameListener close1a = new SpecificMethodFrameListener(1, ChannelCloseOkBody.class);
+ SpecificMethodFrameListener close1b = new SpecificMethodFrameListener(1, ChannelCloseOkBody.class);
+ SpecificMethodFrameListener close2 = new SpecificMethodFrameListener(2, ChannelCloseOkBody.class);
+ SpecificMethodFrameListener open1a = new SpecificMethodFrameListener(1, ChannelOpenOkBody.class);
+ SpecificMethodFrameListener open1b = new SpecificMethodFrameListener(1, ChannelOpenOkBody.class);
+
+ public void testEquals()
+ {
+ //Check that the the same objects are equal
+ assertEquals("ChannelCloseOKBody a should equal a", close1a, close1a);
+ assertEquals("ChannelOpenOkBody a should equal a", open1a, open1a);
+
+ //check that the same values in differnt objects are equal
+ assertEquals("ChannelCloseOKBody b should equal a", close1b, close1a);
+ assertEquals("ChannelCloseOKBody a should equal b", close1a, close1b);
+ assertEquals("ChannelOpenOkBody a should equal b", open1a, open1b);
+ assertEquals("ChannelOpenOkBody a should equal b", open1a, open1b);
+
+ //Chec that different values fail
+ //Different channels
+ assertFalse("ChannelCloseOKBody channel 1 should NOT equal channel 2", close1a.equals(close2));
+ assertFalse("ChannelCloseOKBody channel 1 should NOT equal channel 2", close2.equals(close1a));
+
+ //Different Bodies
+ assertFalse("ChannelCloseOKBody should not equal ChannelOpenOkBody", close1a.equals(open1a));
+ assertFalse("ChannelOpenOkBody should not equal ChannelCloseOKBody", open1a.equals(close1a));
+ }
+
+ public void testProcessMethod() throws AMQFrameDecodingException
+ {
+ ChannelCloseOkBody ccob = (ChannelCloseOkBody) ChannelCloseOkBody.getFactory().newInstance((byte) 8, (byte) 0, ByteBuffer.allocate(0), 0);
+ ChannelOpenOkBody coob = (ChannelOpenOkBody) ChannelOpenOkBody.getFactory().newInstance((byte) 8, (byte) 0, ByteBuffer.allocate(0), 0);
+
+ assertTrue("This SpecificMethodFrameListener should process a ChannelCloseOkBody", close1a.processMethod(1, ccob));
+ assertFalse("This SpecificMethodFrameListener should NOT process a ChannelOpenOkBody", close1a.processMethod(1, coob));
+
+
+
+
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.java b/Final/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.java
new file mode 100644
index 0000000000..60a26c8e62
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.java
@@ -0,0 +1,234 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import java.io.Serializable;
+import java.util.Enumeration;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+
+public class NonQpidObjectMessage implements ObjectMessage {
+
+ private JMSObjectMessage _realMessage;
+ private String _contentString;
+
+ /**
+ * Allows us to construct a JMS message which
+ * does not inherit from the Qpid message superclasses
+ * and expand our unit testing of MessageConverter et al
+ */
+ public NonQpidObjectMessage()
+ {
+ _realMessage = new JMSObjectMessage();
+ }
+
+ public String getJMSMessageID() throws JMSException {
+ return _realMessage.getJMSMessageID();
+ }
+
+ public void setJMSMessageID(String string) throws JMSException {
+ _realMessage.setJMSMessageID(string);
+ }
+
+ public long getJMSTimestamp() throws JMSException {
+ return _realMessage.getJMSTimestamp();
+ }
+
+ public void setJMSTimestamp(long l) throws JMSException {
+ _realMessage.setJMSTimestamp(l);
+ }
+
+ public byte[] getJMSCorrelationIDAsBytes() throws JMSException {
+ return _realMessage.getJMSCorrelationIDAsBytes();
+ }
+
+ public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException {
+ _realMessage.setJMSCorrelationIDAsBytes(bytes);
+ }
+
+ public void setJMSCorrelationID(String string) throws JMSException {
+ _realMessage.setJMSCorrelationID(string);
+ }
+
+ public String getJMSCorrelationID() throws JMSException {
+ return _realMessage.getJMSCorrelationID();
+ }
+
+ public Destination getJMSReplyTo() throws JMSException {
+ return _realMessage.getJMSReplyTo();
+ }
+
+ public void setJMSReplyTo(Destination destination) throws JMSException {
+ _realMessage.setJMSReplyTo(destination);
+ }
+
+ public Destination getJMSDestination() throws JMSException {
+ return _realMessage.getJMSDestination();
+ }
+
+ public void setJMSDestination(Destination destination) throws JMSException {
+ _realMessage.setJMSDestination(destination);
+ }
+
+ public int getJMSDeliveryMode() throws JMSException {
+ return _realMessage.getJMSDeliveryMode();
+ }
+
+ public void setJMSDeliveryMode(int i) throws JMSException {
+ _realMessage.setJMSDeliveryMode(i);
+ }
+
+ public boolean getJMSRedelivered() throws JMSException {
+ return _realMessage.getJMSRedelivered();
+ }
+
+ public void setJMSRedelivered(boolean b) throws JMSException {
+ _realMessage.setJMSRedelivered(b);
+ }
+
+ public String getJMSType() throws JMSException {
+ return _realMessage.getJMSType();
+ }
+
+ public void setJMSType(String string) throws JMSException {
+ _realMessage.setJMSType(string);
+ }
+
+ public long getJMSExpiration() throws JMSException {
+ return _realMessage.getJMSExpiration();
+ }
+
+ public void setJMSExpiration(long l) throws JMSException {
+ _realMessage.setJMSExpiration(l);
+ }
+
+ public int getJMSPriority() throws JMSException {
+ return _realMessage.getJMSPriority();
+ }
+
+ public void setJMSPriority(int i) throws JMSException {
+ _realMessage.setJMSPriority(i);
+ }
+
+ public void clearProperties() throws JMSException {
+ _realMessage.clearProperties();
+ }
+
+ public boolean propertyExists(String string) throws JMSException {
+ return _realMessage.propertyExists(string);
+ }
+
+ public boolean getBooleanProperty(String string) throws JMSException {
+ return _realMessage.getBooleanProperty(string);
+ }
+
+ public byte getByteProperty(String string) throws JMSException {
+ return _realMessage.getByteProperty(string);
+ }
+
+ public short getShortProperty(String string) throws JMSException {
+ return _realMessage.getShortProperty(string);
+ }
+
+ public int getIntProperty(String string) throws JMSException {
+ return _realMessage.getIntProperty(string);
+ }
+
+ public long getLongProperty(String string) throws JMSException {
+ return _realMessage.getLongProperty(string);
+ }
+
+ public float getFloatProperty(String string) throws JMSException {
+ return _realMessage.getFloatProperty(string);
+ }
+
+ public double getDoubleProperty(String string) throws JMSException {
+ return _realMessage.getDoubleProperty(string);
+ }
+
+ public String getStringProperty(String string) throws JMSException {
+ return _realMessage.getStringProperty(string);
+ }
+
+ public Object getObjectProperty(String string) throws JMSException {
+ return _realMessage.getObjectProperty(string);
+ }
+
+ public Enumeration getPropertyNames() throws JMSException {
+ return _realMessage.getPropertyNames();
+ }
+
+ public void setBooleanProperty(String string, boolean b) throws JMSException {
+ _realMessage.setBooleanProperty(string,b);
+ }
+
+ public void setByteProperty(String string, byte b) throws JMSException {
+ _realMessage.setByteProperty(string,b);
+ }
+
+ public void setShortProperty(String string, short i) throws JMSException {
+ _realMessage.setShortProperty(string,i);
+ }
+
+ public void setIntProperty(String string, int i) throws JMSException {
+ _realMessage.setIntProperty(string,i);
+ }
+
+ public void setLongProperty(String string, long l) throws JMSException {
+ _realMessage.setLongProperty(string,l);
+ }
+
+ public void setFloatProperty(String string, float v) throws JMSException {
+ _realMessage.setFloatProperty(string,v);
+ }
+
+ public void setDoubleProperty(String string, double v) throws JMSException {
+ _realMessage.setDoubleProperty(string,v);
+ }
+
+ public void setStringProperty(String string, String string1) throws JMSException {
+ _realMessage.setStringProperty(string,string1);
+ }
+
+ public void setObjectProperty(String string, Object object) throws JMSException {
+ _realMessage.setObjectProperty(string,object);
+ }
+
+ public void acknowledge() throws JMSException {
+ _realMessage.acknowledge();
+ }
+
+ public void clearBody() throws JMSException {
+ _realMessage.clearBody();
+ }
+
+ public void setObject(Serializable serializable) throws JMSException {
+ if (serializable instanceof String)
+ {
+ _contentString = (String)serializable;
+ }
+ }
+
+ public Serializable getObject() throws JMSException {
+ return _contentString; }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java b/Final/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java
new file mode 100644
index 0000000000..9b477c19e2
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import javax.jms.JMSException;
+
+public class TestMessageHelper
+{
+ public static JMSTextMessage newJMSTextMessage() throws JMSException
+ {
+ return new JMSTextMessage();
+ }
+
+ public static JMSBytesMessage newJMSBytesMessage() throws JMSException
+ {
+ return new JMSBytesMessage();
+ }
+
+ public static JMSMapMessage newJMSMapMessage() throws JMSException
+ {
+ return new JMSMapMessage();
+ }
+
+ public static JMSStreamMessage newJMSStreamMessage()
+ {
+ return new JMSStreamMessage();
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java
new file mode 100644
index 0000000000..b6f46b4acc
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.ack;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.Session;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.TextMessage;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class RecoverTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(RecoverTest.class);
+
+ private Exception _error;
+ private AtomicInteger count;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ _error = null;
+ count = new AtomicInteger();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ count = null;
+ }
+
+ public void testRecoverResendsMsgs() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+
+ Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("someQ"),
+ new AMQShortString("someQ"), false, true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ // force synch to ensure the consumer has resulted in a bound queue
+ // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS);
+ // This is the default now
+
+ AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+
+ _logger.info("Sending four messages");
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer.send(producerSession.createTextMessage("msg2"));
+ producer.send(producerSession.createTextMessage("msg3"));
+ producer.send(producerSession.createTextMessage("msg4"));
+
+ con2.close();
+
+ _logger.info("Starting connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive();
+ tm.acknowledge();
+ _logger.info("Received and acknowledged first message");
+ consumer.receive();
+ consumer.receive();
+ consumer.receive();
+ _logger.info("Received all four messages. Calling recover with three outstanding messages");
+ // no ack for last three messages so when I call recover I expect to get three messages back
+ consumerSession.recover();
+ tm = (TextMessage) consumer.receive(3000);
+ assertEquals("msg2", tm.getText());
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertEquals("msg3", tm.getText());
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertEquals("msg4", tm.getText());
+
+ _logger.info("Received redelivery of three messages. Acknowledging last message");
+ tm.acknowledge();
+
+ _logger.info("Calling acknowledge with no outstanding messages");
+ // all acked so no messages to be delivered
+ consumerSession.recover();
+
+ tm = (TextMessage) consumer.receiveNoWait();
+ assertNull(tm);
+ _logger.info("No messages redelivered as is expected");
+
+ con.close();
+ }
+
+ public void testRecoverResendsMsgsAckOnEarlier() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+
+ Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("someQ"),
+ new AMQShortString("someQ"), false, true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ // force synch to ensure the consumer has resulted in a bound queue
+ // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS);
+ // This is the default now
+
+ AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+
+ _logger.info("Sending four messages");
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer.send(producerSession.createTextMessage("msg2"));
+ producer.send(producerSession.createTextMessage("msg3"));
+ producer.send(producerSession.createTextMessage("msg4"));
+
+ con2.close();
+
+ _logger.info("Starting connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive();
+ consumer.receive();
+ tm.acknowledge();
+ _logger.info("Received 2 messages, acknowledge() first message, should acknowledge both");
+
+ consumer.receive();
+ consumer.receive();
+ _logger.info("Received all four messages. Calling recover with two outstanding messages");
+ // no ack for last three messages so when I call recover I expect to get three messages back
+ consumerSession.recover();
+ TextMessage tm3 = (TextMessage) consumer.receive(3000);
+ assertEquals("msg3", tm3.getText());
+
+ TextMessage tm4 = (TextMessage) consumer.receive(3000);
+ assertEquals("msg4", tm4.getText());
+
+ _logger.info("Received redelivery of two messages. calling acknolwedgeThis() first of those message");
+ ((org.apache.qpid.jms.Message) tm3).acknowledgeThis();
+
+ _logger.info("Calling recover");
+ // all acked so no messages to be delivered
+ consumerSession.recover();
+
+ tm4 = (TextMessage) consumer.receive(3000);
+ assertEquals("msg4", tm4.getText());
+ ((org.apache.qpid.jms.Message) tm4).acknowledgeThis();
+
+ _logger.info("Calling recover");
+ // all acked so no messages to be delivered
+ consumerSession.recover();
+
+ tm = (TextMessage) consumer.receiveNoWait();
+ assertNull(tm);
+ _logger.info("No messages redelivered as is expected");
+
+ con.close();
+ }
+
+ public void testAcknowledgePerConsumer() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+
+ Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q1"), new AMQShortString("Q1"),
+ false, true);
+ Queue queue2 =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q2"), new AMQShortString("Q2"),
+ false, true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ MessageConsumer consumer2 = consumerSession.createConsumer(queue2);
+
+ AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+ MessageProducer producer2 = producerSession.createProducer(queue2);
+
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer2.send(producerSession.createTextMessage("msg2"));
+
+ con2.close();
+
+ _logger.info("Starting connection");
+ con.start();
+
+ TextMessage tm2 = (TextMessage) consumer2.receive();
+ assertNotNull(tm2);
+ assertEquals("msg2", tm2.getText());
+
+ tm2.acknowledge();
+
+ consumerSession.recover();
+
+ TextMessage tm1 = (TextMessage) consumer.receive(2000);
+ assertNotNull(tm1);
+ assertEquals("msg1", tm1.getText());
+
+ con.close();
+
+ }
+
+ public void testRecoverInAutoAckListener() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+
+ final Session consumerSession = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), new AMQShortString("Q3"),
+ false, true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ MessageProducer producer = consumerSession.createProducer(queue);
+ producer.send(consumerSession.createTextMessage("hello"));
+
+ final Object lock = new Object();
+
+ consumer.setMessageListener(new MessageListener()
+ {
+
+ public void onMessage(Message message)
+ {
+ try
+ {
+ count.incrementAndGet();
+ if (count.get() == 1)
+ {
+ if (message.getJMSRedelivered())
+ {
+ setError(
+ new Exception("Message marked as redilvered on what should be first delivery attempt"));
+ }
+
+ consumerSession.recover();
+ }
+ else if (count.get() == 2)
+ {
+ if (!message.getJMSRedelivered())
+ {
+ setError(
+ new Exception(
+ "Message not marked as redilvered on what should be second delivery attempt"));
+ }
+ }
+ else
+ {
+ System.err.println(message);
+ fail("Message delivered too many times!: " + count);
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error recovering session: " + e, e);
+ setError(e);
+ }
+
+ synchronized (lock)
+ {
+ lock.notify();
+ }
+ }
+ });
+
+ con.start();
+
+ long waitTime = 300000L;
+ long waitUntilTime = System.currentTimeMillis() + waitTime;
+
+ synchronized (lock)
+ {
+ while ((count.get() <= 1) && (waitTime > 0))
+ {
+ lock.wait(waitTime);
+ if (count.get() <= 1)
+ {
+ waitTime = waitUntilTime - System.currentTimeMillis();
+ }
+ }
+ }
+
+ Thread.sleep(1000);
+
+ if (count.get() != 2)
+ {
+ System.err.println("Count != 2 : " + count);
+ }
+
+ assertTrue(count.get() == 2);
+
+ con.close();
+
+ if (_error != null)
+ {
+ throw _error;
+ }
+ }
+
+ private void setError(Exception e)
+ {
+ _error = e;
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(RecoverTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java
new file mode 100644
index 0000000000..da1b46ee2c
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.mina.common.ByteBuffer;
+
+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.message.JMSBytesMessage;
+import org.apache.qpid.testutil.VMBrokerSetup;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.BytesMessage;
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotReadableException;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class BytesMessageTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(BytesMessageTest.class);
+
+ private Connection _connection;
+ private Destination _destination;
+ private Session _session;
+ private final List<JMSBytesMessage> received = new ArrayList<JMSBytesMessage>();
+ private final List<byte[]> messages = new ArrayList<byte[]>();
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection, randomize("BytesMessageTest"), true));
+ }
+
+ void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // Set up a slow consumer.
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ try
+ {
+ send(_count);
+ waitFor(_count);
+ check();
+ _logger.info("Completed without failure");
+ }
+ finally
+ {
+ _connection.close();
+ }
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ BytesMessage msg = _session.createBytesMessage();
+
+ try
+ {
+ msg.readFloat();
+ Assert.fail("Message should not be readable");
+ }
+ catch (MessageNotReadableException mnwe)
+ {
+ // normal execution
+ }
+
+ byte[] data = ("Message " + i).getBytes();
+ msg.writeBytes(data);
+ messages.add(data);
+ producer.send(msg);
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ while (received.size() < count)
+ {
+ received.wait();
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ List<byte[]> actual = new ArrayList<byte[]>();
+ for (JMSBytesMessage m : received)
+ {
+ ByteBuffer buffer = m.getData();
+ byte[] data = new byte[buffer.remaining()];
+ buffer.get(data);
+ actual.add(data);
+
+ // Check Body Write Status
+ try
+ {
+ m.writeBoolean(true);
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.writeBoolean(true);
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ }
+
+ assertEqual(messages.iterator(), actual.iterator());
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEquivalent((byte[]) expected.next(), (byte[]) actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEquivalent(byte[] expected, byte[] actual)
+ {
+ if (expected.length != actual.length)
+ {
+ throw new RuntimeException("Expected length " + expected.length + " got " + actual.length);
+ }
+
+ for (int i = 0; i < expected.length; i++)
+ {
+ if (expected[i] != actual[i])
+ {
+ throw new RuntimeException("Failed on byte " + i + " of " + expected.length);
+ }
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSBytesMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ final String connectionString;
+ final int count;
+ if (argv.length == 0)
+ {
+ connectionString = "vm://:1";
+ count = 100;
+ }
+ else
+ {
+ connectionString = argv[0];
+ count = Integer.parseInt(argv[1]);
+ }
+
+ System.out.println("connectionString = " + connectionString);
+ System.out.println("count = " + count);
+
+ BytesMessageTest test = new BytesMessageTest();
+ test._connectionString = connectionString;
+ test._count = count;
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new VMBrokerSetup(new junit.framework.TestSuite(BytesMessageTest.class));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java
new file mode 100644
index 0000000000..ddbc69826d
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java
@@ -0,0 +1,96 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.jms.JMSException;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.client.message.TestMessageHelper;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+
+public class FieldTableKeyEnumeratorTest extends TestCase
+{
+ public void testTrue()
+ {
+
+ }
+ public void testKeyEnumeration()
+ {
+ FieldTable result = FieldTableFactory.newFieldTable();
+ result.setObject("one", 1L);
+ result.setObject("two", 2L);
+ result.setObject("three", 3L);
+ result.setObject("four", 4L);
+ result.setObject("five", 5L);
+
+ Iterator iterator = result.keys().iterator();
+
+ try
+ {
+ assertTrue("one".equals(iterator.next()));
+ assertTrue("two".equals(iterator.next()));
+ assertTrue("three".equals(iterator.next()));
+ assertTrue("four".equals(iterator.next()));
+ assertTrue("five".equals(iterator.next()));
+ }
+ catch (NoSuchElementException e)
+ {
+ fail("All elements should be found.");
+ }
+
+ }
+
+ public void testPropertEnu()
+ {
+ try
+ {
+ JMSTextMessage text = TestMessageHelper.newJMSTextMessage();
+
+ text.setBooleanProperty("Boolean1", true);
+ text.setBooleanProperty("Boolean2", true);
+ text.setIntProperty("Int", 2);
+ text.setLongProperty("Long", 2);
+
+ Enumeration e = text.getPropertyNames();
+
+ assertTrue("Boolean1".equals(e.nextElement()));
+ assertTrue("Boolean2".equals(e.nextElement()));
+ assertTrue("Int".equals(e.nextElement()));
+ assertTrue("Long".equals(e.nextElement()));
+ }
+ catch (JMSException e)
+ {
+
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(FieldTableKeyEnumeratorTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java
new file mode 100644
index 0000000000..aff496becf
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java
@@ -0,0 +1,176 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.TestCase;
+
+import org.apache.mina.common.ByteBuffer;
+
+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.message.JMSBytesMessage;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.FieldTableFactory;
+import org.apache.qpid.testutil.VMBrokerSetup;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.BytesMessage;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class FieldTableMessageTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(FieldTableMessageTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private final ArrayList<JMSBytesMessage> received = new ArrayList<JMSBytesMessage>();
+ private FieldTable _expected;
+ private int _count = 10;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection, randomize("FieldTableMessageTest"), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+
+ // _expected = new FieldTableTest().load("FieldTableTest2.properties");
+ _expected = load();
+ }
+
+ private FieldTable load() throws IOException
+ {
+ FieldTable result = FieldTableFactory.newFieldTable();
+ result.setLong("one", 1L);
+ result.setLong("two", 2L);
+ result.setLong("three", 3L);
+ result.setLong("four", 4L);
+ result.setLong("five", 5L);
+
+ return result;
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _logger.info("Completed without failure");
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException, IOException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ BytesMessage msg = _session.createBytesMessage();
+ msg.writeBytes(_expected.getDataAsBytes());
+ producer.send(msg);
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ while (received.size() < count)
+ {
+ received.wait();
+ }
+ }
+ }
+
+ void check() throws JMSException, AMQFrameDecodingException
+ {
+ for (Object m : received)
+ {
+ ByteBuffer buffer = ((JMSBytesMessage) m).getData();
+ FieldTable actual = FieldTableFactory.newFieldTable(buffer, buffer.remaining());
+ for (String key : _expected.keys())
+ {
+ assertEquals("Values for " + key + " did not match", _expected.getObject(key), actual.getObject(key));
+ }
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSBytesMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ FieldTableMessageTest test = new FieldTableMessageTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ test._count = (argv.length > 1) ? Integer.parseInt(argv[1]) : 5;
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new VMBrokerSetup(new junit.framework.TestSuite(FieldTableMessageTest.class));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java
new file mode 100644
index 0000000000..60ed688897
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.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.test.unit.basic;
+
+import java.util.Enumeration;
+
+import javax.jms.JMSException;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.client.message.TestMessageHelper;
+
+public class FieldTablePropertyTest extends TestCase
+{
+ public void testPropertyNames()
+ {
+ try
+ {
+ JMSTextMessage text = TestMessageHelper.newJMSTextMessage();
+
+ text.setBooleanProperty("Boolean1", true);
+ text.setBooleanProperty("Boolean2", true);
+ text.setIntProperty("Int", 2);
+ text.setLongProperty("Long", 2);
+
+ Enumeration e = text.getPropertyNames();
+
+ assertEquals("Boolean1", e.nextElement());
+ assertTrue("Boolean2".equals(e.nextElement()));
+ assertTrue("Int".equals(e.nextElement()));
+ assertTrue("Long".equals(e.nextElement()));
+ }
+ catch (JMSException e)
+ {
+
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(FieldTablePropertyTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java
new file mode 100644
index 0000000000..83846e0081
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java
@@ -0,0 +1,130 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.test.unit.basic;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException;
+import org.apache.qpid.client.transport.TransportConnection;
+
+import junit.framework.TestCase;
+
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+import javax.jms.QueueSession;
+import javax.jms.Queue;
+import javax.jms.QueueSender;
+import javax.jms.TextMessage;
+import javax.jms.InvalidDestinationException;
+
+public class InvalidDestinationTest extends TestCase
+{
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private MessageConsumer _consumer;
+
+ private static final String VM_BROKER = "vm://:1";
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ createVMBroker();
+ _connection = new AMQConnection(VM_BROKER, "guest", "guest", "ReceiveTestClient", "test");
+ }
+
+ public void createVMBroker()
+ {
+ try
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ catch (AMQVMBrokerCreationException e)
+ {
+ fail("Unable to create broker: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _connection.close();
+ TransportConnection.killVMBroker(1);
+ super.tearDown();
+ }
+
+
+
+ public void testInvalidDestination() throws Exception
+ {
+ Queue invalidDestination = new AMQQueue("amq.direct","unknownQ");
+ AMQQueue validDestination = new AMQQueue("amq.direct","knownQ");
+ QueueSession queueSession = _connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // This is the only easy way to create and bind a queue from the API :-(
+ MessageConsumer consumer = queueSession.createConsumer(validDestination);
+
+ QueueSender sender = queueSession.createSender(invalidDestination);
+ TextMessage msg = queueSession.createTextMessage("Hello");
+ try
+ {
+ sender.send(msg);
+ fail("Expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // pass
+ }
+ sender.close();
+
+ sender = queueSession.createSender(null);
+ invalidDestination = new AMQQueue("amq.direct","unknownQ");
+
+ try
+ {
+ sender.send(invalidDestination,msg);
+ fail("Expected InvalidDestinationException");
+ }
+ catch (InvalidDestinationException ex)
+ {
+ // pass
+ }
+ sender.send(validDestination,msg);
+ sender.close();
+ validDestination = new AMQQueue("amq.direct","knownQ");
+ sender = queueSession.createSender(validDestination);
+ sender.send(msg);
+
+
+
+
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+
+ return new junit.framework.TestSuite(InvalidDestinationTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java
new file mode 100644
index 0000000000..03698b2ab2
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.client.transport.TransportConnection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LargeMessageTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(LargeMessageTest.class);
+
+ private AMQConnection _connection;
+ private Destination _destination;
+ private AMQSession _session;
+ private final List<JMSTextMessage> received = new ArrayList<JMSTextMessage>();
+ public String _broker = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ try
+ {
+ init(new AMQConnection(_broker, "guest", "guest", "LargeMessageTest", "test"));
+ }
+ catch (Exception e)
+ {
+ fail("Unable to initialilse connection: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ _session.close();
+ _connection.close();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination = new AMQQueue(connection, "LargeMessageTest", true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ connection.start();
+ }
+
+ // Test boundary of 1 packet to 2 packets
+ public void test64kminus1()
+ {
+ checkLargeMessage((64 * 1024) - 1);
+ }
+
+ public void test64k()
+ {
+ checkLargeMessage(64 * 1024);
+ }
+
+ public void test64kplus1()
+ {
+ checkLargeMessage((64 * 1024) + 1);
+ }
+
+ // Test packet boundary of 3 packtes
+ public void test128kminus1()
+ {
+ checkLargeMessage((128 * 1024) - 1);
+ }
+
+ public void test128k()
+ {
+ checkLargeMessage(128 * 1024);
+ }
+
+ public void test128kplus1()
+ {
+ checkLargeMessage((128 * 1024) + 1);
+ }
+
+ // Testing larger messages
+
+ public void test256k()
+ {
+ checkLargeMessage(256 * 1024);
+ }
+
+ public void test512k()
+ {
+ checkLargeMessage(512 * 1024);
+ }
+
+ public void test1024k()
+ {
+ checkLargeMessage(1024 * 1024);
+ }
+
+ protected void checkLargeMessage(int messageSize)
+ {
+ try
+ {
+ MessageConsumer consumer = _session.createConsumer(_destination);
+ MessageProducer producer = _session.createProducer(_destination);
+ _logger.info("Testing message of size:" + messageSize);
+
+ String _messageText = buildLargeMessage(messageSize);
+
+ _logger.debug("Message built");
+
+ producer.send(_session.createTextMessage(_messageText));
+
+ TextMessage result = (TextMessage) consumer.receive(10000);
+
+ assertNotNull("Null message recevied", result);
+ assertEquals("Message Size", _messageText.length(), result.getText().length());
+ assertEquals("Message content differes", _messageText, result.getText());
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ fail("Excpetion occured:" + e.getCause());
+ }
+ }
+
+ private String buildLargeMessage(int size)
+ {
+ StringBuilder builder = new StringBuilder(size);
+
+ char ch = 'a';
+
+ for (int i = 1; i <= size; i++)
+ {
+ builder.append(ch);
+
+ if ((i % 1000) == 0)
+ {
+ ch++;
+ if (ch == ('z' + 1))
+ {
+ ch = 'a';
+ }
+ }
+ }
+
+ return builder.toString();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(LargeMessageTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java
new file mode 100644
index 0000000000..6708fefa86
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java
@@ -0,0 +1,1277 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSMapMessage;
+import org.apache.qpid.client.transport.TransportConnection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class MapMessageTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MapMessageTest.class);
+
+ private AMQConnection _connection;
+ private Destination _destination;
+ private AMQSession _session;
+ private final List<JMSMapMessage> received = new ArrayList<JMSMapMessage>();
+
+ private static final String MESSAGE = "Message ";
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+ private byte[] _bytes = { 99, 98, 97, 96, 95 };
+ private static final float _smallfloat = 100.0f;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ TransportConnection.createVMBroker(1);
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+ catch (Exception e)
+ {
+ fail("Unable to initialilse connection: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _logger.info("Tearing Down unit.basic.MapMessageTest");
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination = new AMQQueue(connection, randomize("MapMessageTest"), true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ MapMessage message = _session.createMapMessage();
+
+ setMapValues(message, i);
+
+ producer.send(message);
+ }
+ }
+
+ private void setMapValues(MapMessage message, int i) throws JMSException
+ {
+ message.setBoolean("odd", (i / 2) == 0);
+ message.setByte("byte", (byte) Byte.MAX_VALUE);
+ message.setBytes("bytes", _bytes);
+ message.setChar("char", (char) 'c');
+ message.setDouble("double", (double) Double.MAX_VALUE);
+ message.setFloat("float", (float) Float.MAX_VALUE);
+ message.setFloat("smallfloat", 100);
+ message.setInt("messageNumber", i);
+ message.setInt("int", (int) Integer.MAX_VALUE);
+ message.setLong("long", (long) Long.MAX_VALUE);
+ message.setShort("short", (short) Short.MAX_VALUE);
+ message.setString("message", MESSAGE + i);
+
+ // Test Setting Object Values
+ message.setObject("object-bool", true);
+ message.setObject("object-byte", Byte.MAX_VALUE);
+ message.setObject("object-bytes", _bytes);
+ message.setObject("object-char", 'c');
+ message.setObject("object-double", Double.MAX_VALUE);
+ message.setObject("object-float", Float.MAX_VALUE);
+ message.setObject("object-int", Integer.MAX_VALUE);
+ message.setObject("object-long", Long.MAX_VALUE);
+ message.setObject("object-short", Short.MAX_VALUE);
+
+ // Set a null String value
+ message.setString("nullString", null);
+ // Highlight protocol problem
+ message.setString("emptyString", "");
+
+ }
+
+ void waitFor(int count) throws Exception
+ {
+ long waitTime = 30000L;
+ long waitUntilTime = System.currentTimeMillis() + 30000L;
+
+ synchronized (received)
+ {
+ while ((received.size() < count) && (waitTime > 0))
+ {
+ if (received.size() < count)
+ {
+ received.wait(waitTime);
+ }
+
+ if (received.size() < count)
+ {
+ waitTime = waitUntilTime - System.currentTimeMillis();
+ }
+ }
+
+ if (received.size() < count)
+ {
+ throw new Exception("Timed-out. Waiting for " + count + " only got " + received.size());
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ List<String> actual = new ArrayList<String>();
+ int count = 0;
+ for (JMSMapMessage m : received)
+ {
+ actual.add(m.getString("message"));
+
+ testMapValues(m, count);
+
+ testCorrectExceptions(m);
+
+ testMessageWriteStatus(m);
+
+ testPropertyWriteStatus(m);
+
+ count++;
+ }
+ }
+
+ private void testCorrectExceptions(JMSMapMessage m) throws JMSException
+ {
+ testBoolean(m);
+
+ testByte(m);
+
+ testBytes(m);
+
+ testChar(m);
+
+ testDouble(m);
+
+ testFloat(m);
+
+ testInt(m);
+
+ testLong(m);
+
+ testShort(m);
+
+ testString(m);
+ }
+
+ private void testString(JMSMapMessage m) throws JMSException
+ {
+
+ Assert.assertFalse(m.getBoolean("message"));
+
+ try
+ {
+ m.getByte("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("message");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("message");
+ fail("Exception Expected.");
+ }
+ catch (NumberFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("message");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(MESSAGE + m.getInt("messageNumber"), m.getString("message"));
+ }
+
+ private void testShort(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Short.MAX_VALUE, m.getShort("short"));
+
+ // Try bad reads
+ try
+ {
+ m.getChar("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Short.MAX_VALUE, m.getInt("short"));
+
+ Assert.assertEquals(Short.MAX_VALUE, m.getLong("short"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("short");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Short.MAX_VALUE, m.getString("short"));
+ }
+
+ private void testLong(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Long.MAX_VALUE, m.getLong("long"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("long");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Long.MAX_VALUE, m.getString("long"));
+ }
+
+ private void testDouble(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Double.MAX_VALUE, m.getDouble("double"));
+
+ // Try bad reads
+ try
+ {
+ m.getBytes("double");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Double.MAX_VALUE, m.getString("double"));
+ }
+
+ private void testFloat(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Float.MAX_VALUE, m.getFloat("float"));
+
+ Assert.assertEquals(_smallfloat, (float) m.getDouble("smallfloat"));
+
+ // Try bad reads
+ try
+ {
+ m.getBytes("float");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Float.MAX_VALUE, m.getString("float"));
+ }
+
+ private void testInt(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Integer.MAX_VALUE, m.getInt("int"));
+
+ Assert.assertEquals(Integer.MAX_VALUE, (int) m.getLong("int"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("int");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Integer.MAX_VALUE, m.getString("int"));
+ }
+
+ private void testChar(JMSMapMessage m) throws JMSException
+ {
+
+ // Try bad reads
+ try
+ {
+ m.getBoolean("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals('c', m.getChar("char"));
+
+ try
+ {
+ m.getInt("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("char");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + 'c', m.getString("char"));
+ }
+
+ private void testBytes(JMSMapMessage m) throws JMSException
+ {
+ // Try bad reads
+ try
+ {
+ m.getBoolean("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getByte("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getShort("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getChar("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getInt("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ try
+ {
+ m.getLong("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ assertBytesEqual(_bytes, m.getBytes("bytes"));
+
+ try
+ {
+ m.getString("bytes");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ }
+
+ private void testByte(JMSMapMessage m) throws JMSException
+ {
+ // Try bad reads
+ try
+ {
+ m.getBoolean("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals(Byte.MAX_VALUE, m.getByte("byte"));
+
+ Assert.assertEquals((short) Byte.MAX_VALUE, m.getShort("byte"));
+
+ // Try bad reads
+ try
+ {
+ m.getChar("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+
+ // Reading a byte as an int is ok
+ Assert.assertEquals((short) Byte.MAX_VALUE, m.getInt("byte"));
+
+ Assert.assertEquals((short) Byte.MAX_VALUE, m.getLong("byte"));
+
+ // Try bad reads
+ try
+ {
+ m.getFloat("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("byte");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + Byte.MAX_VALUE, m.getString("byte"));
+
+ }
+
+ private void testBoolean(JMSMapMessage m) throws JMSException
+ {
+
+ Assert.assertEquals((m.getInt("messageNumber") / 2) == 0, m.getBoolean("odd"));
+
+ // Try bad reads
+ try
+ {
+ m.getByte("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ // Try bad reads
+ try
+ {
+ m.getShort("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getChar("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException npe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getInt("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getLong("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getFloat("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getDouble("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+ // Try bad reads
+ try
+ {
+ m.getBytes("odd");
+ fail("Exception Expected.");
+ }
+ catch (MessageFormatException nfe)
+ {
+ // normal execution
+ }
+
+ Assert.assertEquals("" + ((m.getInt("messageNumber") / 2) == 0), m.getString("odd"));
+ }
+
+ private void testPropertyWriteStatus(JMSMapMessage m) throws JMSException
+ {
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+ }
+
+ private void testMessageWriteStatus(JMSMapMessage m) throws JMSException
+ {
+ try
+ {
+ m.setInt("testint", 3);
+ fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.setInt("testint", 3);
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+ }
+
+ private void testMapValues(JMSMapMessage m, int count) throws JMSException
+ {
+ // Test get<Primiative>
+
+ // Boolean
+ assertEqual((count / 2) == 0, m.getBoolean("odd"));
+ assertEqual("" + ((count / 2) == 0), m.getString("odd"));
+
+ // Byte
+ assertEqual(Byte.MAX_VALUE, m.getByte("byte"));
+ assertEqual("" + Byte.MAX_VALUE, m.getString("byte"));
+
+ // Bytes
+ assertBytesEqual(_bytes, m.getBytes("bytes"));
+
+ // Char
+ assertEqual('c', m.getChar("char"));
+
+ // Double
+ assertEqual(Double.MAX_VALUE, m.getDouble("double"));
+ assertEqual("" + Double.MAX_VALUE, m.getString("double"));
+
+ // Float
+ assertEqual(Float.MAX_VALUE, m.getFloat("float"));
+ assertEqual(_smallfloat, (float) m.getDouble("smallfloat"));
+ assertEqual("" + Float.MAX_VALUE, m.getString("float"));
+
+ // Integer
+ assertEqual(Integer.MAX_VALUE, m.getInt("int"));
+ assertEqual("" + Integer.MAX_VALUE, m.getString("int"));
+ assertEqual(count, m.getInt("messageNumber"));
+
+ // long
+ assertEqual(Long.MAX_VALUE, m.getLong("long"));
+ assertEqual("" + Long.MAX_VALUE, m.getString("long"));
+
+ // Short
+ assertEqual(Short.MAX_VALUE, m.getShort("short"));
+ assertEqual("" + Short.MAX_VALUE, m.getString("short"));
+ assertEqual((int) Short.MAX_VALUE, m.getInt("short"));
+
+ // String
+ assertEqual(MESSAGE + count, m.getString("message"));
+
+ // Test getObjects
+ assertEqual(true, m.getObject("object-bool"));
+ assertEqual(Byte.MAX_VALUE, m.getObject("object-byte"));
+ assertBytesEqual(_bytes, (byte[]) m.getObject("object-bytes"));
+ assertEqual('c', m.getObject("object-char"));
+ assertEqual(Double.MAX_VALUE, m.getObject("object-double"));
+ assertEqual(Float.MAX_VALUE, m.getObject("object-float"));
+ assertEqual(Integer.MAX_VALUE, m.getObject("object-int"));
+ assertEqual(Long.MAX_VALUE, m.getObject("object-long"));
+ assertEqual(Short.MAX_VALUE, m.getObject("object-short"));
+
+ // Check Special values
+ assertTrue(m.getString("nullString") == null);
+ assertEqual("", m.getString("emptyString"));
+ }
+
+ private void assertBytesEqual(byte[] expected, byte[] actual)
+ {
+ Assert.assertEquals(expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ Assert.assertEquals(expected[index], actual[index]);
+ }
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ _logger.info("****************** Recevied Messgage:" + (JMSMapMessage) message);
+ received.add((JMSMapMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ MapMessageTest test = new MapMessageTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ if (argv.length > 1)
+ {
+ test._count = Integer.parseInt(argv[1]);
+ }
+
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MapMessageTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java
new file mode 100644
index 0000000000..65b3d60ad9
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.exchange.ExchangeDefaults;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+public class MultipleConnectionTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MultipleConnectionTest.class);
+
+ public static final String _defaultBroker = "vm://:1";
+ public String _connectionString = _defaultBroker;
+
+ private static class Receiver
+ {
+ private AMQConnection _connection;
+ private Session[] _sessions;
+ private MessageCounter[] _counters;
+
+ Receiver(String broker, AMQDestination dest, int sessions) throws Exception
+ {
+ this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "test"), dest, sessions);
+ }
+
+ Receiver(AMQConnection connection, AMQDestination dest, int sessions) throws Exception
+ {
+ _connection = connection;
+ _sessions = new AMQSession[sessions];
+ _counters = new MessageCounter[sessions];
+ for (int i = 0; i < sessions; i++)
+ {
+ _sessions[i] = _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _counters[i] = new MessageCounter(_sessions[i].toString());
+ _sessions[i].createConsumer(dest).setMessageListener(_counters[i]);
+ }
+
+ _connection.start();
+ }
+
+ void close() throws JMSException
+ {
+ _connection.close();
+ }
+ }
+
+ private static class Publisher
+ {
+ private AMQConnection _connection;
+ private Session _session;
+ private MessageProducer _producer;
+
+ Publisher(String broker, AMQDestination dest) throws Exception
+ {
+ this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "test"), dest);
+ }
+
+ Publisher(AMQConnection connection, AMQDestination dest) throws Exception
+ {
+ _connection = connection;
+ _session = _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _producer = _session.createProducer(dest);
+ }
+
+ void send(String msg) throws JMSException
+ {
+ _producer.send(_session.createTextMessage(msg));
+ }
+
+ void close() throws JMSException
+ {
+ _connection.close();
+ }
+ }
+
+ private static class MessageCounter implements MessageListener
+ {
+ private final String _name;
+ private int _count;
+
+ MessageCounter(String name)
+ {
+ _name = name;
+ }
+
+ public synchronized void onMessage(Message message)
+ {
+ _count++;
+ notify();
+ }
+
+ synchronized boolean waitUntil(int expected, long maxWait) throws InterruptedException
+ {
+ long start = System.currentTimeMillis();
+ while (expected > _count)
+ {
+ long timeLeft = maxWait - timeSince(start);
+ if (timeLeft < 0)
+ {
+ break;
+ }
+
+ wait(timeLeft);
+ }
+
+ return expected <= _count;
+ }
+
+ private long timeSince(long start)
+ {
+ return System.currentTimeMillis() - start;
+ }
+
+ public synchronized String toString()
+ {
+ return _name + ": " + _count;
+ }
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ private static void waitForCompletion(int expected, long wait, Receiver[] receivers) throws InterruptedException
+ {
+ for (int i = 0; i < receivers.length; i++)
+ {
+ waitForCompletion(expected, wait, receivers[i]._counters);
+ }
+ }
+
+ private static void waitForCompletion(int expected, long wait, MessageCounter[] counters) throws InterruptedException
+ {
+ for (int i = 0; i < counters.length; i++)
+ {
+ if (!counters[i].waitUntil(expected, wait))
+ {
+ throw new RuntimeException("Expected: " + expected + " got " + counters[i]);
+ }
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ String broker = (argv.length > 0) ? argv[0] : _defaultBroker;
+
+ MultipleConnectionTest test = new MultipleConnectionTest();
+ test._connectionString = broker;
+ test.test();
+ }
+
+ public void test() throws Exception
+ {
+ String broker = _connectionString;
+ int messages = 10;
+
+ AMQTopic topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, "amq.topic");
+
+ Receiver[] receivers = new Receiver[] { new Receiver(broker, topic, 2), new Receiver(broker, topic, 14) };
+
+ Publisher publisher = new Publisher(broker, topic);
+ for (int i = 0; i < messages; i++)
+ {
+ publisher.send("Message " + (i + 1));
+ }
+
+ try
+ {
+ waitForCompletion(messages, 5000, receivers);
+ _logger.info("All receivers received all expected messages");
+ }
+ finally
+ {
+ publisher.close();
+ for (int i = 0; i < receivers.length; i++)
+ {
+ receivers[i].close();
+ }
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MultipleConnectionTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java
new file mode 100644
index 0000000000..9237555734
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+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.message.JMSObjectMessage;
+import org.apache.qpid.client.transport.TransportConnection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ObjectMessageTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ObjectMessageTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private final List<JMSObjectMessage> received = new ArrayList<JMSObjectMessage>();
+ private final List<Payload> messages = new ArrayList<Payload>();
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ try
+ {
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+ catch (Exception e)
+ {
+ fail("Uable to initialise: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection, randomize("ObjectMessageTest"), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _logger.info("Completed without failure");
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ Payload payload = new Payload("Message " + i);
+ messages.add(payload);
+ producer.send(_session.createObjectMessage(payload));
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ while (received.size() < count)
+ {
+ received.wait();
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ List<Object> actual = new ArrayList<Object>();
+ for (JMSObjectMessage m : received)
+ {
+ actual.add(m.getObject());
+
+ try
+ {
+ m.setObject("Test text");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.setObject("Test text");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ }
+
+ assertEqual(messages.iterator(), actual.iterator());
+
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSObjectMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ private static class Payload implements Serializable
+ {
+ private final String data;
+
+ Payload(String data)
+ {
+ this.data = data;
+ }
+
+ public int hashCode()
+ {
+ return data.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof Payload) && ((Payload) o).data.equals(data);
+ }
+
+ public String toString()
+ {
+ return "Payload[" + data + "]";
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ ObjectMessageTest test = new ObjectMessageTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ if (argv.length > 1)
+ {
+ test._count = Integer.parseInt(argv[1]);
+ }
+
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ObjectMessageTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
new file mode 100644
index 0000000000..dce9667ff2
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java
@@ -0,0 +1,369 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.AMQMessage;
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQShortString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class PropertyValueTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(PropertyValueTest.class);
+
+ private int count = 0;
+ private AMQConnection _connection;
+ private Destination _destination;
+ private AMQSession _session;
+ private final List<JMSTextMessage> received = new ArrayList<JMSTextMessage>();
+ private final List<String> messages = new ArrayList<String>();
+ private int _count = 1;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killVMBroker(1);
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination = new AMQQueue(connection, randomize("PropertyValueTest"), true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void testOnce()
+ {
+ runBatch(1);
+ }
+
+ public void test50()
+ {
+ runBatch(50);
+ }
+
+ private void runBatch(int runSize)
+ {
+ try
+ {
+ int run = 0;
+ while (run < runSize)
+ {
+ _logger.error("Run Number:" + run++);
+ try
+ {
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+ catch (Exception e)
+ {
+ fail("Unable to initialilse connection: " + e);
+ }
+
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _logger.info("Completed without failure");
+
+ Thread.sleep(10);
+ _connection.close();
+
+ _logger.error("End Run Number:" + (run - 1));
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error(e.getMessage(), e);
+ e.printStackTrace();
+ }
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ String text = "Message " + i;
+ messages.add(text);
+ Message m = _session.createTextMessage(text);
+
+ m.setBooleanProperty("Bool", true);
+
+ m.setByteProperty("Byte", (byte) Byte.MAX_VALUE);
+ m.setDoubleProperty("Double", (double) Double.MAX_VALUE);
+ m.setFloatProperty("Float", (float) Float.MAX_VALUE);
+ m.setIntProperty("Int", (int) Integer.MAX_VALUE);
+
+ m.setJMSCorrelationID("Correlation");
+ // fixme the m.setJMSMessage has no effect
+ producer.setPriority(8);
+ m.setJMSPriority(3);
+
+ // Queue
+ Queue q;
+
+ if ((i / 2) == 0)
+ {
+ q = _session.createTemporaryQueue();
+ }
+ else
+ {
+ q = new AMQQueue(_connection, "TestReply");
+ }
+
+ m.setJMSReplyTo(q);
+ m.setStringProperty("TempQueue", q.toString());
+
+ _logger.trace("Message:" + m);
+
+ Assert.assertEquals("Check temp queue has been set correctly", m.getJMSReplyTo().toString(),
+ m.getStringProperty("TempQueue"));
+
+ m.setJMSType("Test");
+ m.setLongProperty("UnsignedInt", (long) 4294967295L);
+ m.setLongProperty("Long", (long) Long.MAX_VALUE);
+
+ m.setShortProperty("Short", (short) Short.MAX_VALUE);
+ m.setStringProperty("String", "Test");
+
+ // AMQP Specific values
+
+ // Timestamp
+ long nano = System.nanoTime();
+ m.setStringProperty("time-str", String.valueOf(nano));
+ ((AMQMessage) m).setTimestampProperty(new AMQShortString("time"), nano);
+
+ // Decimal
+ BigDecimal bd = new BigDecimal(Integer.MAX_VALUE);
+ ((AMQMessage) m).setDecimalProperty(new AMQShortString("decimal"), bd.setScale(Byte.MAX_VALUE));
+
+ bd = new BigDecimal((long) Integer.MAX_VALUE + 1L);
+
+ try
+ {
+ ((AMQMessage) m).setDecimalProperty(new AMQShortString("decimal-bad-value"), bd.setScale(Byte.MAX_VALUE));
+ fail("UnsupportedOperationException should be thrown as value can't be correctly transmitted");
+ }
+ catch (UnsupportedOperationException uoe)
+ {
+ // normal path.
+ }
+
+ try
+ {
+ ((AMQMessage) m).setDecimalProperty(new AMQShortString("decimal-bad-scale"),
+ bd.setScale(Byte.MAX_VALUE + 1));
+ fail("UnsupportedOperationException should be thrown as scale can't be correctly transmitted");
+ }
+ catch (UnsupportedOperationException uoe)
+ {
+ // normal path.
+ }
+
+ // Void
+ ((AMQMessage) m).setVoidProperty(new AMQShortString("void"));
+
+ _logger.debug("Sending Msg:" + m);
+ producer.send(m);
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ while (received.size() < count)
+ {
+ received.wait();
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ List<String> actual = new ArrayList<String>();
+ for (JMSTextMessage m : received)
+ {
+ actual.add(m.getText());
+
+ // Check Properties
+
+ Assert.assertEquals("Check Boolean properties are correctly transported", true, m.getBooleanProperty("Bool"));
+ Assert.assertEquals("Check Byte properties are correctly transported", (byte) Byte.MAX_VALUE,
+ m.getByteProperty("Byte"));
+ Assert.assertEquals("Check Double properties are correctly transported", (double) Double.MAX_VALUE,
+ m.getDoubleProperty("Double"));
+ Assert.assertEquals("Check Float properties are correctly transported", (float) Float.MAX_VALUE,
+ m.getFloatProperty("Float"));
+ Assert.assertEquals("Check Int properties are correctly transported", (int) Integer.MAX_VALUE,
+ m.getIntProperty("Int"));
+ Assert.assertEquals("Check CorrelationID properties are correctly transported", "Correlation",
+ m.getJMSCorrelationID());
+ Assert.assertEquals("Check Priority properties are correctly transported", 8, m.getJMSPriority());
+
+ // Queue
+ Assert.assertEquals("Check ReplyTo properties are correctly transported", m.getStringProperty("TempQueue"),
+ m.getJMSReplyTo().toString());
+
+ Assert.assertEquals("Check Type properties are correctly transported", "Test", m.getJMSType());
+
+ Assert.assertEquals("Check Short properties are correctly transported", (short) Short.MAX_VALUE,
+ m.getShortProperty("Short"));
+ Assert.assertEquals("Check UnsignedInt properties are correctly transported", (long) 4294967295L,
+ m.getLongProperty("UnsignedInt"));
+ Assert.assertEquals("Check Long properties are correctly transported", (long) Long.MAX_VALUE,
+ m.getLongProperty("Long"));
+ Assert.assertEquals("Check String properties are correctly transported", "Test", m.getStringProperty("String"));
+
+ // AMQP Tests Specific values
+
+ Assert.assertEquals("Check Timestamp properties are correctly transported", m.getStringProperty("time-str"),
+ ((AMQMessage) m).getTimestampProperty(new AMQShortString("time")).toString());
+
+ // Decimal
+ BigDecimal bd = new BigDecimal(Integer.MAX_VALUE);
+
+ Assert.assertEquals("Check decimal properties are correctly transported", bd.setScale(Byte.MAX_VALUE),
+ ((AMQMessage) m).getDecimalProperty(new AMQShortString("decimal")));
+
+ // Void
+ ((AMQMessage) m).setVoidProperty(new AMQShortString("void"));
+
+ Assert.assertTrue("Check void properties are correctly transported",
+ ((AMQMessage) m).getPropertyHeaders().containsKey("void"));
+ }
+
+ received.clear();
+
+ assertEqual(messages.iterator(), actual.iterator());
+
+ messages.clear();
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSTextMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ PropertyValueTest test = new PropertyValueTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ if (argv.length > 1)
+ {
+ test._count = Integer.parseInt(argv[1]);
+ }
+
+ test.testOnce();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(PropertyValueTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java
new file mode 100644
index 0000000000..a3d0cf6dcd
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import javax.jms.Connection;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Topic;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class PubSubTwoConnectionTest extends TestCase
+{
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ /**
+ * This tests that a consumer is set up synchronously
+ * @throws Exception
+ */
+ public void testTwoConnections() throws Exception
+ {
+
+ AMQConnection con1 = new AMQConnection("vm://:1", "guest", "guest", "Client1", "test");
+
+ Topic topic = new AMQTopic(con1, "MyTopic");
+
+ Session session1 = con1.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ MessageProducer producer = session1.createProducer(topic);
+
+ Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "Client2", "test");
+ Session session2 = con2.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ MessageConsumer consumer = session2.createConsumer(topic);
+ con2.start();
+ producer.send(session1.createTextMessage("Hello"));
+ TextMessage tm1 = (TextMessage) consumer.receive(2000);
+ assertNotNull(tm1);
+ assertEquals("Hello", tm1.getText());
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.java
new file mode 100644
index 0000000000..668233f356
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import javax.jms.MessageConsumer;
+
+import junit.framework.TestCase;
+
+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.transport.TransportConnection;
+import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException;
+
+public class ReceiveTest extends TestCase
+{
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private MessageConsumer _consumer;
+
+ private static final String VM_BROKER = "vm://:1";
+ public String _connectionString = VM_BROKER;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ if (_connectionString.equals(VM_BROKER))
+ {
+ createVMBroker();
+ String broker = _connectionString;
+ init(new AMQConnection(broker, "guest", "guest", "ReceiveTestClient", "test"));
+ }
+ }
+
+ public void createVMBroker()
+ {
+ try
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ catch (AMQVMBrokerCreationException e)
+ {
+ fail("Unable to create broker: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ if (_connectionString.equals(VM_BROKER))
+ {
+ TransportConnection.killVMBroker(1);
+ }
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection,"ReceiveTest", true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(true, AMQSession.NO_ACKNOWLEDGE);
+ _consumer = _session.createConsumer(_destination);
+ _connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ _consumer.receive(5000);
+ _connection.close();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ ReceiveTest test = new ReceiveTest();
+ test._connectionString = argv.length == 0 ? VM_BROKER : argv[0];
+ test.setUp();
+ test.test();
+ test.tearDown();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ // TODO: note that this test doesn't use the VMBrokerSetup
+ // test helper class to create and tear down its
+ // VMBroker. This is because the main() above seems to
+ // indicate that it's also used outside of the surefire test
+ // framework. If it isn't, then this test should also be
+ // changed to use VMBrokerSetup here.
+ return new junit.framework.TestSuite(ReceiveTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java
new file mode 100644
index 0000000000..40c712c1c9
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java
@@ -0,0 +1,140 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.TestCase;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+public class SelectorTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(SelectorTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private int count;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection, new AMQQueue(connection, randomize("SessionStartTest"), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ connection.start();
+
+ String selector = null;
+ // selector = "Cost = 2 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT;
+ // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT;
+
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ // _session.createConsumer(destination).setMessageListener(this);
+ _session.createConsumer(destination, selector).setMessageListener(this);
+ }
+
+ public synchronized void test() throws JMSException, InterruptedException
+ {
+ try
+ {
+ Message msg = _session.createTextMessage("Message");
+ msg.setJMSPriority(1);
+ msg.setIntProperty("Cost", 2);
+ msg.setJMSType("Special");
+
+ _logger.info("Sending Message:" + msg);
+
+ ((BasicMessageProducer) _session.createProducer(_destination)).send(msg, DeliveryMode.NON_PERSISTENT);
+ _logger.info("Message sent, waiting for response...");
+ wait(1000);
+
+ if (count > 0)
+ {
+ _logger.info("Got message");
+ }
+
+ if (count == 0)
+ {
+ fail("Did not get message!");
+ // throw new RuntimeException("Did not get message!");
+ }
+ }
+ finally
+ {
+ _session.close();
+ _connection.close();
+ }
+ }
+
+ public synchronized void onMessage(Message message)
+ {
+ count++;
+ _logger.info("Got Message:" + message);
+ notify();
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ 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();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(SelectorTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java
new file mode 100644
index 0000000000..cc18169a5b
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.TestCase;
+
+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.framing.AMQShortString;
+import org.apache.qpid.testutil.VMBrokerSetup;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+public class SessionStartTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(SessionStartTest.class);
+
+ private AMQConnection _connection;
+ private AMQDestination _destination;
+ private AMQSession _session;
+ private int count;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ init(connection,
+ new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("SessionStartTest")), true));
+ }
+
+ private void init(AMQConnection connection, AMQDestination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ connection.start();
+
+ _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _session.createConsumer(destination).setMessageListener(this);
+ }
+
+ public synchronized void test() throws JMSException, InterruptedException
+ {
+ try
+ {
+ _session.createProducer(_destination).send(_session.createTextMessage("Message"));
+ _logger.info("Message sent, waiting for response...");
+ wait(1000);
+ if (count > 0)
+ {
+ _logger.info("Got message");
+ }
+ else
+ {
+ throw new RuntimeException("Did not get message!");
+ }
+ }
+ finally
+ {
+ _session.close();
+ _connection.close();
+ }
+ }
+
+ public synchronized void onMessage(Message message)
+ {
+ count++;
+ notify();
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ SessionStartTest test = new SessionStartTest();
+ test._connectionString = (argv.length == 0) ? "localhost:5672" : argv[0];
+ test.setUp();
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new VMBrokerSetup(new junit.framework.TestSuite(SessionStartTest.class));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java
new file mode 100644
index 0000000000..000fb9ab88
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java
@@ -0,0 +1,257 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.basic;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.testutil.VMBrokerSetup;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class TextMessageTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(TextMessageTest.class);
+
+ private AMQConnection _connection;
+ private Destination _destination;
+ private AMQSession _session;
+ private final List<JMSTextMessage> received = new ArrayList<JMSTextMessage>();
+ private final List<String> messages = new ArrayList<String>();
+ private int _count = 100;
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
+ }
+ catch (Exception e)
+ {
+ fail("Unable to initialilse connection: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ private void init(AMQConnection connection) throws Exception
+ {
+ Destination destination =
+ new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("TextMessageTest")), true);
+ init(connection, destination);
+ }
+
+ private void init(AMQConnection connection, Destination destination) throws Exception
+ {
+ _connection = connection;
+ _destination = destination;
+ _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // set up a slow consumer
+ _session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+ }
+
+ public void test() throws Exception
+ {
+ int count = _count;
+ send(count);
+ waitFor(count);
+ check();
+ _logger.info("Completed without failure");
+ _connection.close();
+ }
+
+ void send(int count) throws JMSException
+ {
+ // create a publisher
+ MessageProducer producer = _session.createProducer(_destination);
+ for (int i = 0; i < count; i++)
+ {
+ String text = "Message " + i;
+ messages.add(text);
+ Message m = _session.createTextMessage(text);
+ m.setStringProperty("String", "hello");
+
+ _logger.info("Sending Msg:" + m);
+ producer.send(m);
+ }
+ }
+
+ void waitFor(int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ while (received.size() < count)
+ {
+ received.wait();
+ }
+ }
+ }
+
+ void check() throws JMSException
+ {
+ List<String> actual = new ArrayList<String>();
+ for (JMSTextMessage m : received)
+ {
+ actual.add(m.getText());
+
+ // Check body write status
+ try
+ {
+ m.setText("Test text");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearBody();
+
+ try
+ {
+ m.setText("Test text");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ // Check property write status
+ try
+ {
+ m.setStringProperty("test", "test");
+ Assert.fail("Message should not be writeable");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ // normal execution
+ }
+
+ m.clearProperties();
+
+ try
+ {
+ m.setStringProperty("test", "test");
+ }
+ catch (MessageNotWriteableException mnwe)
+ {
+ Assert.fail("Message should be writeable");
+ }
+
+ }
+
+ assertEqual(messages.iterator(), actual.iterator());
+ }
+
+ private static void assertEqual(Iterator expected, Iterator actual)
+ {
+ List<String> errors = new ArrayList<String>();
+ while (expected.hasNext() && actual.hasNext())
+ {
+ try
+ {
+ assertEqual(expected.next(), actual.next());
+ }
+ catch (Exception e)
+ {
+ errors.add(e.getMessage());
+ }
+ }
+ while (expected.hasNext())
+ {
+ errors.add("Expected " + expected.next() + " but no more actual values.");
+ }
+ while (actual.hasNext())
+ {
+ errors.add("Found " + actual.next() + " but no more expected values.");
+ }
+
+ if (!errors.isEmpty())
+ {
+ throw new RuntimeException(errors.toString());
+ }
+ }
+
+ private static void assertEqual(Object expected, Object actual)
+ {
+ if (!expected.equals(actual))
+ {
+ throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'");
+ }
+ }
+
+ public void onMessage(Message message)
+ {
+ synchronized (received)
+ {
+ received.add((JMSTextMessage) message);
+ received.notify();
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ TextMessageTest test = new TextMessageTest();
+ test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0];
+ test.setUp();
+ if (argv.length > 1)
+ {
+ test._count = Integer.parseInt(argv[1]);
+ }
+
+ test.test();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new VMBrokerSetup(new junit.framework.TestSuite(TextMessageTest.class));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java
new file mode 100644
index 0000000000..690ba7f01b
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.basic.close;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.URLSyntaxException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+public class CloseTests extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(CloseTests.class);
+
+ private static final String BROKER = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.setUp();
+
+ TransportConnection.killVMBroker(1);
+ }
+
+ public void testCloseQueueReceiver() throws AMQException, URLSyntaxException, JMSException
+ {
+ AMQConnection connection = new AMQConnection(BROKER, "guest", "guest", this.getName(), "test");
+
+ Session session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ AMQQueue queue = new AMQQueue(new AMQBindingURL("test-queue"));
+ MessageConsumer consumer = session.createConsumer(queue);
+
+ MessageProducer producer_not_used_but_created_for_testing = session.createProducer(queue);
+
+ connection.start();
+
+ _logger.info("About to close consumer");
+
+ consumer.close();
+
+ _logger.info("Closed Consumer");
+
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java
new file mode 100644
index 0000000000..0e15341615
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java
@@ -0,0 +1,205 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client;
+
+import javax.jms.JMSException;
+import javax.jms.QueueSession;
+import javax.jms.TopicSession;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQShortString;
+
+public class AMQConnectionTest extends TestCase
+{
+ private static AMQConnection _connection;
+ private static AMQTopic _topic;
+ private static AMQQueue _queue;
+ private static QueueSession _queueSession;
+ private static TopicSession _topicSession;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ _connection = new AMQConnection("vm://:1", "guest", "guest", "fred", "test");
+ _topic = new AMQTopic(_connection.getDefaultTopicExchangeName(), new AMQShortString("mytopic"));
+ _queue = new AMQQueue(_connection.getDefaultQueueExchangeName(), new AMQShortString("myqueue"));
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ try
+ {
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ //ignore
+ }
+ TransportConnection.killAllVMBrokers();
+ }
+
+ /**
+ * Simple tests to check we can create TopicSession and QueueSession ok
+ * And that they throw exceptions where appropriate as per JMS spec
+ */
+
+ public void testCreateQueueSession() throws JMSException
+ {
+ _queueSession = _connection.createQueueSession(false, AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ public void testCreateTopicSession() throws JMSException
+ {
+ _topicSession = _connection.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ public void testTopicSessionCreateBrowser() throws JMSException
+ {
+ try
+ {
+ _topicSession.createBrowser(_queue);
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected javax.jms.IllegalStateException, got " + e);
+ }
+ }
+
+ public void testTopicSessionCreateQueue() throws JMSException
+ {
+ try
+ {
+ _topicSession.createQueue("abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected javax.jms.IllegalStateException, got " + e);
+ }
+ }
+
+ public void testTopicSessionCreateTemporaryQueue() throws JMSException
+ {
+ try
+ {
+ _topicSession.createTemporaryQueue();
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected javax.jms.IllegalStateException, got " + e);
+ }
+ }
+
+ public void testQueueSessionCreateTemporaryTopic() throws JMSException
+ {
+ try
+ {
+ _queueSession.createTemporaryTopic();
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected javax.jms.IllegalStateException, got " + e);
+ }
+ }
+
+ public void testQueueSessionCreateTopic() throws JMSException
+ {
+ try
+ {
+ _queueSession.createTopic("abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected javax.jms.IllegalStateException, got " + e);
+ }
+ }
+
+ public void testQueueSessionDurableSubscriber() throws JMSException
+ {
+ try
+ {
+ _queueSession.createDurableSubscriber(_topic, "abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected javax.jms.IllegalStateException, got " + e);
+ }
+ }
+
+ public void testQueueSessionUnsubscribe() throws JMSException
+ {
+ try
+ {
+ _queueSession.unsubscribe("abc");
+ fail("expected exception did not occur");
+ }
+ catch (javax.jms.IllegalStateException s)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected javax.jms.IllegalStateException, got " + e);
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(AMQConnectionTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java
new file mode 100644
index 0000000000..78b7976f55
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client;
+
+import javax.jms.JMSException;
+import javax.jms.QueueReceiver;
+import javax.jms.TopicSubscriber;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.testutil.VMBrokerSetup;
+
+/**
+ * Tests for QueueReceiver and TopicSubscriber creation methods on AMQSession
+ */
+public class AMQSessionTest extends TestCase
+{
+
+ private static AMQSession _session;
+ private static AMQTopic _topic;
+ private static AMQQueue _queue;
+ private static AMQConnection _connection;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = new AMQConnection("vm://:1", "guest", "guest", "fred", "test");
+ _topic = new AMQTopic(_connection,"mytopic");
+ _queue = new AMQQueue(_connection,"myqueue");
+ _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ try
+ {
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ //just close
+ }
+ super.tearDown();
+ }
+
+ public void testCreateSubscriber() throws JMSException
+ {
+ TopicSubscriber subscriber = _session.createSubscriber(_topic);
+ assertEquals("Topic names should match from TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+
+ subscriber = _session.createSubscriber(_topic, "abc", false);
+ assertEquals("Topic names should match from TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+ }
+
+ public void testCreateDurableSubscriber() throws JMSException
+ {
+ TopicSubscriber subscriber = _session.createDurableSubscriber(_topic, "mysubname");
+ assertEquals("Topic names should match from durable TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+
+ subscriber = _session.createDurableSubscriber(_topic, "mysubname", "abc", false);
+ assertEquals("Topic names should match from durable TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName());
+ }
+
+ public void testCreateQueueReceiver() throws JMSException
+ {
+ QueueReceiver receiver = _session.createQueueReceiver(_queue);
+ assertEquals("Queue names should match from QueueReceiver", _queue.getQueueName(), receiver.getQueue().getQueueName());
+
+ receiver = _session.createQueueReceiver(_queue, "abc");
+ assertEquals("Queue names should match from QueueReceiver with selector", _queue.getQueueName(), receiver.getQueue().getQueueName());
+ }
+
+ public void testCreateReceiver() throws JMSException
+ {
+ QueueReceiver receiver = _session.createReceiver(_queue);
+ assertEquals("Queue names should match from QueueReceiver", _queue.getQueueName(), receiver.getQueue().getQueueName());
+
+ receiver = _session.createReceiver(_queue, "abc");
+ assertEquals("Queue names should match from QueueReceiver with selector", _queue.getQueueName(), receiver.getQueue().getQueueName());
+ }
+
+ public static void stopVmBrokers()
+ {
+ _queue = null;
+ _topic = null;
+ _session = null;
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new VMBrokerSetup(new junit.framework.TestSuite(AMQSessionTest.class));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java
new file mode 100644
index 0000000000..e513f25e3d
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.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.test.unit.client.BrokerDetails;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQBrokerDetails;
+import org.apache.qpid.url.URLSyntaxException;
+
+public class BrokerDetailsTest extends TestCase
+{
+
+ public void testMultiParameters() throws URLSyntaxException
+ {
+ String url = "tcp://localhost:5672?timeout='200',immediatedelivery='true'";
+
+ AMQBrokerDetails broker = new AMQBrokerDetails(url);
+
+ assertTrue(broker.getOption("timeout").equals("200"));
+ assertTrue(broker.getOption("immediatedelivery").equals("true"));
+ }
+
+ public void testVMBroker() throws URLSyntaxException
+ {
+ String url = "vm://:2";
+
+ AMQBrokerDetails broker = new AMQBrokerDetails(url);
+ assertTrue(broker.getTransport().equals("vm"));
+ assertEquals(broker.getPort(), 2);
+ }
+
+ public void testTransportsDefaultToTCP() throws URLSyntaxException
+ {
+ String url = "localhost:5672";
+
+ AMQBrokerDetails broker = new AMQBrokerDetails(url);
+ assertTrue(broker.getTransport().equals("tcp"));
+ }
+
+ public void testCheckDefaultPort() throws URLSyntaxException
+ {
+ String url = "tcp://localhost";
+
+ AMQBrokerDetails broker = new AMQBrokerDetails(url);
+ assertTrue(broker.getPort() == AMQBrokerDetails.DEFAULT_PORT);
+ }
+
+ public void testBothDefaults() throws URLSyntaxException
+ {
+ String url = "localhost";
+
+ AMQBrokerDetails broker = new AMQBrokerDetails(url);
+
+ assertTrue(broker.getTransport().equals("tcp"));
+ assertTrue(broker.getPort() == AMQBrokerDetails.DEFAULT_PORT);
+ }
+
+ public void testWrongOptionSeparatorInBroker()
+ {
+ String url = "tcp://localhost:5672+option='value'";
+ try
+ {
+ new AMQBrokerDetails(url);
+ }
+ catch (URLSyntaxException urise)
+ {
+ assertTrue(urise.getReason().equals("Illegal character in port number"));
+ }
+
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(BrokerDetailsTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
new file mode 100644
index 0000000000..575d542633
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.client.channelclose;
+
+import org.apache.qpid.AMQChannelClosedException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQInvalidArgumentException;
+import org.apache.qpid.AMQInvalidRoutingKeyException;
+import org.apache.qpid.client.AMQNoConsumersException;
+import org.apache.qpid.client.AMQNoRouteException;
+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.AMQShortString;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandlerNoCloseOk.class);
+
+ private static ChannelCloseMethodHandlerNoCloseOk _handler = new ChannelCloseMethodHandlerNoCloseOk();
+
+ public static ChannelCloseMethodHandlerNoCloseOk getInstance()
+ {
+ return _handler;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt)
+ throws AMQException
+ {
+ _logger.debug("ChannelClose method received");
+ ChannelCloseBody method = (ChannelCloseBody) evt.getMethod();
+
+ AMQConstant errorCode = AMQConstant.getConstant(method.replyCode);
+ AMQShortString reason = method.replyText;
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason);
+ }
+
+ // For this test Method Handler .. don't send Close-OK
+ // // TODO: Be aware of possible changes to parameter order as versions change.
+ // AMQFrame frame = ChannelCloseOkBody.createAMQFrame(evt.getChannelId(), method.getMajor(), method.getMinor());
+ // protocolSession.writeFrame(frame);
+ if (errorCode != AMQConstant.REPLY_SUCCESS)
+ {
+ _logger.error("Channel close received with errorCode " + errorCode + ", and reason " + reason);
+ if (errorCode == AMQConstant.NO_CONSUMERS)
+ {
+ throw new AMQNoConsumersException("Error: " + reason, null);
+ }
+ else if (errorCode == AMQConstant.NO_ROUTE)
+ {
+ throw new AMQNoRouteException("Error: " + reason, null);
+ }
+ else if (errorCode == AMQConstant.INVALID_ARGUMENT)
+ {
+ _logger.debug("Broker responded with Invalid Argument.");
+
+ throw new AMQInvalidArgumentException(String.valueOf(reason));
+ }
+ else if (errorCode == AMQConstant.INVALID_ROUTING_KEY)
+ {
+ _logger.debug("Broker responded with Invalid Routing Key.");
+
+ throw new AMQInvalidRoutingKeyException(String.valueOf(reason));
+ }
+ else
+ {
+ throw new AMQChannelClosedException(errorCode, "Error: " + reason);
+ }
+
+ }
+
+ protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java
new file mode 100644
index 0000000000..559e9a4741
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java
@@ -0,0 +1,235 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.channelclose;
+
+import junit.framework.TestCase;
+
+import junit.textui.TestRunner;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.transport.TransportConnection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Due to bizarre exception handling all sessions are closed if you get
+ * a channel close request and no exception listener is registered.
+ * <p/>
+ * JIRA issue IBTBLZ-10.
+ * <p/>
+ * Simulate by:
+ * <p/>
+ * 0. Create two sessions with no exception listener.
+ * 1. Publish message to queue/topic that does not exist (wrong routing key).
+ * 2. This will cause a channel close.
+ * 3. Since client does not have an exception listener, currently all sessions are
+ * closed.
+ */
+public class ChannelCloseOkTest extends TestCase
+{
+ private AMQConnection _connection;
+ private Destination _destination1;
+ private Destination _destination2;
+ private Session _session1;
+ private Session _session2;
+ private final List<Message> _received1 = new ArrayList<Message>();
+ private final List<Message> _received2 = new ArrayList<Message>();
+
+ private static final Logger _log = LoggerFactory.getLogger(ChannelCloseOkTest.class);
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ TransportConnection.createVMBroker(1);
+ _connection = new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test");
+
+ _destination1 = new AMQQueue(_connection, "q1", true);
+ _destination2 = new AMQQueue(_connection, "q2", true);
+ _session1 = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _session1.createConsumer(_destination1).setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _log.debug("consumer 1 got message [" + getTextMessage(message) + "]");
+ synchronized (_received1)
+ {
+ _received1.add(message);
+ _received1.notify();
+ }
+ }
+ });
+ _session2 = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _session2.createConsumer(_destination2).setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ _log.debug("consumer 2 got message [" + getTextMessage(message) + "]");
+ synchronized (_received2)
+ {
+ _received2.add(message);
+ _received2.notify();
+ }
+ }
+ });
+
+ _connection.start();
+ }
+
+ private String getTextMessage(Message message)
+ {
+ TextMessage tm = (TextMessage) message;
+ try
+ {
+ return tm.getText();
+ }
+ catch (JMSException e)
+ {
+ return "oops " + e;
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ closeConnection();
+ TransportConnection.killAllVMBrokers();
+ super.tearDown();
+ }
+
+ public void closeConnection() throws JMSException
+ {
+ if (_connection != null)
+ {
+ _log.info(">>>>>>>>>>>>>>.. closing");
+ _connection.close();
+ }
+ }
+
+ public void testWithoutExceptionListener() throws Exception
+ {
+ doTest();
+ }
+
+ public void testWithExceptionListener() throws Exception
+ {
+ _connection.setExceptionListener(new ExceptionListener()
+ {
+ public void onException(JMSException jmsException)
+ {
+ _log.warn("onException - " + jmsException.getMessage());
+ }
+ });
+
+ doTest();
+ }
+
+ public void doTest() throws Exception
+ {
+ // Check both sessions are ok.
+ sendAndWait(_session1, _destination1, "first", _received1, 1);
+ sendAndWait(_session2, _destination2, "second", _received2, 1);
+ assertEquals(1, _received1.size());
+ assertEquals(1, _received2.size());
+
+ // Now send message to incorrect destination on session 1.
+ Destination destination = new AMQQueue(_connection, "incorrect");
+ send(_session1, destination, "third"); // no point waiting as message will never be received.
+
+ // Ensure both sessions are still ok.
+ // Send a bunch of messages as this give time for the sessions to be erroneously closed.
+ final int num = 300;
+ for (int i = 0; i < num; ++i)
+ {
+ send(_session1, _destination1, "" + i);
+ send(_session2, _destination2, "" + i);
+ }
+
+ waitFor(_received1, num + 1);
+ waitFor(_received2, num + 1);
+
+ // Note that the third message is never received as it is sent to an incorrect destination.
+ assertEquals(num + 1, _received1.size());
+ assertEquals(num + 1, _received2.size());
+ }
+
+ private void sendAndWait(Session session, Destination destination, String message, List<Message> received, int count)
+ throws JMSException, InterruptedException
+ {
+ send(session, destination, message);
+ waitFor(received, count);
+ }
+
+ private void send(Session session, Destination destination, String message) throws JMSException
+ {
+ _log.debug("sending message " + message);
+ MessageProducer producer1 = session.createProducer(destination);
+ producer1.send(session.createTextMessage(message));
+ }
+
+ private void waitFor(List<Message> received, int count) throws InterruptedException
+ {
+ synchronized (received)
+ {
+ while (received.size() < count)
+ {
+ try
+ {
+ received.wait();
+ }
+ catch (InterruptedException e)
+ {
+ _log.info("Interrupted: " + e);
+ throw e;
+ }
+ }
+ }
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static void main(String[] args)
+ {
+ TestRunner.run(ChannelCloseOkTest.class);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ChannelCloseOkTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java
new file mode 100644
index 0000000000..f1099ca5ab
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.client.channelclose;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQTimeoutException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.client.failover.FailoverProtectedOperation;
+import org.apache.qpid.client.failover.FailoverRetrySupport;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.client.protocol.AMQProtocolHandler;
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.framing.ChannelOpenBody;
+import org.apache.qpid.framing.ChannelOpenOkBody;
+import org.apache.qpid.framing.ExchangeDeclareBody;
+import org.apache.qpid.framing.ExchangeDeclareOkBody;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.url.URLSyntaxException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+public class ChannelCloseTest extends TestCase implements ExceptionListener, ConnectionListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseTest.class);
+
+ Connection _connection;
+ private String _brokerlist = "vm://:1";
+ private Session _session;
+ private static final long SYNC_TIMEOUT = 5000;
+ private int TEST = 0;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ /*
+ close channel, use chanel with same id ensure error.
+ */
+ public void testReusingChannelAfterFullClosure() throws Exception
+ {
+ _connection = newConnection();
+
+ // Create Producer
+ try
+ {
+ _connection.start();
+
+ createChannelAndTest(1);
+
+ // Cause it to close
+ try
+ {
+ _logger.info("Testing invalid exchange");
+ declareExchange(1, "", "name_that_will_lookup_to_null", false);
+ fail("Exchange name is empty so this should fail ");
+ }
+ catch (AMQException e)
+ {
+ assertEquals("Exchange should not be found", AMQConstant.NOT_FOUND, e.getErrorCode());
+ }
+
+ // Check that
+ try
+ {
+ _logger.info("Testing valid exchange should fail");
+ declareExchange(1, "topic", "amq.topic", false);
+ fail("This should not succeed as the channel should be closed ");
+ }
+ catch (AMQException e)
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Exception occured was:" + e.getErrorCode());
+ }
+
+ assertEquals("Connection should be closed", AMQConstant.CHANNEL_ERROR, e.getErrorCode());
+
+ _connection = newConnection();
+ }
+
+ checkSendingMessage();
+
+ _session.close();
+ _connection.close();
+
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ /*
+ close channel and send guff then send ok no errors
+ */
+ public void testSendingMethodsAfterClose() throws Exception
+ {
+ try
+ {
+ _connection = new AMQConnection("amqp://guest:guest@CCTTest/test?brokerlist='" + _brokerlist + "'");
+
+ ((AMQConnection) _connection).setConnectionListener(this);
+
+ _connection.setExceptionListener(this);
+
+ // Change the StateManager for one that doesn't respond with Close-OKs
+ AMQStateManager oldStateManager = ((AMQConnection) _connection).getProtocolHandler().getStateManager();
+
+ _session = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ _connection.start();
+
+ // Test connection
+ checkSendingMessage();
+
+ // Set StateManager to manager that ignores Close-oks
+ AMQProtocolSession protocolSession = ((AMQConnection) _connection).getProtocolHandler().getProtocolSession();
+ AMQStateManager newStateManager = new NoCloseOKStateManager(protocolSession);
+ newStateManager.changeState(oldStateManager.getCurrentState());
+
+ ((AMQConnection) _connection).getProtocolHandler().setStateManager(newStateManager);
+
+ final int TEST_CHANNEL = 1;
+ _logger.info("Testing Channel(" + TEST_CHANNEL + ") Creation");
+
+ createChannelAndTest(TEST_CHANNEL);
+
+ // Cause it to close
+ try
+ {
+ _logger.info("Closing Channel - invalid exchange");
+ declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", false);
+ fail("Exchange name is empty so this should fail ");
+ }
+ catch (AMQException e)
+ {
+ assertEquals("Exchange should not be found", AMQConstant.NOT_FOUND, e.getErrorCode());
+ }
+
+ try
+ {
+ // Send other methods that should be ignored
+ // send them no wait as server will ignore them
+ _logger.info("Tested known exchange - should ignore");
+ declareExchange(TEST_CHANNEL, "topic", "amq.topic", true);
+
+ _logger.info("Tested known invalid exchange - should ignore");
+ declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", true);
+
+ _logger.info("Tested known invalid exchange - should ignore");
+ declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", true);
+
+ // Send sync .. server will igore and timy oue
+ _logger.info("Tested known invalid exchange - should ignore");
+ declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", false);
+ }
+ catch (AMQTimeoutException te)
+ {
+ assertEquals("Request should timeout", AMQConstant.REQUEST_TIMEOUT, te.getErrorCode());
+ }
+ catch (AMQException e)
+ {
+ fail("This should not fail as all requests should be ignored");
+ }
+
+ _logger.info("Sending Close");
+ // Send Close-ok
+ sendClose(TEST_CHANNEL);
+
+ _logger.info("Re-opening channel");
+
+ createChannelAndTest(TEST_CHANNEL);
+
+ // Test connection is still ok
+
+ checkSendingMessage();
+
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+
+ }
+ catch (URLSyntaxException e)
+ {
+ fail(e.getMessage());
+ }
+ finally
+ {
+ try
+ {
+ _session.close();
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+ }
+
+ private void createChannelAndTest(int channel) throws FailoverException
+ {
+ // Create A channel
+ try
+ {
+ createChannel(channel);
+ }
+ catch (AMQException e)
+ {
+ fail(e.getMessage());
+ }
+
+ // Test it is ok
+ try
+ {
+ declareExchange(channel, "topic", "amq.topic", false);
+ _logger.info("Tested known exchange");
+ }
+ catch (AMQException e)
+ {
+ fail("This should not fail as this is the default exchange details");
+ }
+ }
+
+ private void sendClose(int channel)
+ {
+ AMQFrame frame =
+ ChannelCloseOkBody.createAMQFrame(channel,
+ ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(),
+ ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion());
+
+ ((AMQConnection) _connection).getProtocolHandler().writeFrame(frame);
+ }
+
+ private void checkSendingMessage() throws JMSException
+ {
+ TEST++;
+ _logger.info("Test creating producer which will use channel id 1");
+
+ Queue queue = _session.createTemporaryQueue();
+
+ MessageConsumer consumer = _session.createConsumer(queue);
+
+ MessageProducer producer = _session.createProducer(queue);
+
+ final String MESSAGE = "CCT_Test_Message";
+ producer.send(_session.createTextMessage(MESSAGE));
+
+ Message msg = consumer.receive(2000);
+
+ assertNotNull("Received messages should not be null.", msg);
+ assertEquals("Message received not what we sent", MESSAGE, ((TextMessage) msg).getText());
+ }
+
+ private Connection newConnection()
+ {
+ AMQConnection connection = null;
+ try
+ {
+ connection = new AMQConnection("amqp://guest:guest@CCTTest/test?brokerlist='" + _brokerlist + "'");
+
+ connection.setConnectionListener(this);
+
+ _session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ connection.start();
+
+ }
+ catch (JMSException e)
+ {
+ fail("Creating new connection when:" + e.getMessage());
+ }
+ catch (AMQException e)
+ {
+ fail("Creating new connection when:" + e.getMessage());
+ }
+ catch (URLSyntaxException e)
+ {
+ fail("Creating new connection when:" + e.getMessage());
+ }
+
+ return connection;
+ }
+
+ private void declareExchange(final int channelId, final String _type, final String _name, final boolean nowait)
+ throws AMQException, FailoverException
+ {
+// new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()
+// {
+// public Object execute() throws AMQException, FailoverException
+// {
+
+ AMQProtocolHandler protocolHandler = ((AMQConnection) _connection).getProtocolHandler();
+
+ AMQFrame exchangeDeclare =
+ ExchangeDeclareBody.createAMQFrame(channelId,
+ protocolHandler.getProtocolMajorVersion(),
+ protocolHandler.getProtocolMinorVersion(), null, // arguments
+ false, // autoDelete
+ false, // durable
+ new AMQShortString(_name), // exchange
+ false, // internal
+ nowait, // nowait
+ true, // passive
+ 0, // ticket
+ new AMQShortString(_type)); // type
+
+ if (nowait)
+ {
+ protocolHandler.writeFrame(exchangeDeclare);
+ }
+ else
+ {
+ protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class, SYNC_TIMEOUT);
+ }
+
+// return null;
+// }
+// }, (AMQConnection)_connection).execute();
+
+ }
+
+ private void createChannel(int channelId) throws AMQException, FailoverException
+ {
+ ((AMQConnection) _connection).getProtocolHandler().syncWrite(ChannelOpenBody.createAMQFrame(channelId,
+ ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(),
+ ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion(), null), // outOfBand
+ ChannelOpenOkBody.class);
+
+ }
+
+ public void onException(JMSException jmsException)
+ {
+ // _logger.info("CCT" + jmsException);
+ fail(jmsException.getMessage());
+ }
+
+ public void bytesSent(long count)
+ {
+ }
+
+ public void bytesReceived(long count)
+ {
+ }
+
+ public boolean preFailover(boolean redirect)
+ {
+ return false;
+ }
+
+ public boolean preResubscribe()
+ {
+ return false;
+ }
+
+ public void failoverComplete()
+ {
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java
new file mode 100644
index 0000000000..9600d1e9d3
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java
@@ -0,0 +1,87 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.channelclose;
+
+import javax.jms.Connection;
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class CloseWithBlockingReceiveTest extends TestCase
+{
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+
+ public void testReceiveReturnsNull() throws Exception
+ {
+ final AMQConnection connection = new AMQConnection("vm://:1", "guest", "guest",
+ "fred", "test");
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageConsumer consumer = session.createConsumer(new AMQTopic(connection, "banana"));
+ connection.start();
+
+ Runnable r = new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+ Thread.sleep(1000);
+ connection.close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ };
+ long startTime = System.currentTimeMillis();
+ new Thread(r).start();
+ consumer.receive(10000);
+ assertTrue(System.currentTimeMillis() - startTime < 10000);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(CloseWithBlockingReceiveTest.class);
+ }
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java
new file mode 100644
index 0000000000..d128f30727
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.client.channelclose;
+
+import org.apache.qpid.client.state.AMQStateManager;
+import org.apache.qpid.client.state.AMQState;
+import org.apache.qpid.client.handler.ConnectionStartMethodHandler;
+import org.apache.qpid.client.handler.ConnectionCloseMethodHandler;
+import org.apache.qpid.client.handler.ConnectionTuneMethodHandler;
+import org.apache.qpid.client.handler.ConnectionSecureMethodHandler;
+import org.apache.qpid.client.handler.ConnectionOpenOkMethodHandler;
+import org.apache.qpid.client.handler.ChannelCloseOkMethodHandler;
+import org.apache.qpid.client.handler.BasicDeliverMethodHandler;
+import org.apache.qpid.client.handler.BasicReturnMethodHandler;
+import org.apache.qpid.client.handler.BasicCancelOkMethodHandler;
+import org.apache.qpid.client.handler.ChannelFlowOkMethodHandler;
+import org.apache.qpid.client.handler.QueueDeleteOkMethodHandler;
+import org.apache.qpid.client.handler.ExchangeBoundOkMethodHandler;
+import org.apache.qpid.client.protocol.AMQProtocolSession;
+import org.apache.qpid.framing.ConnectionStartBody;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.ConnectionTuneBody;
+import org.apache.qpid.framing.ConnectionSecureBody;
+import org.apache.qpid.framing.ConnectionOpenOkBody;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.framing.BasicReturnBody;
+import org.apache.qpid.framing.BasicCancelOkBody;
+import org.apache.qpid.framing.ChannelFlowOkBody;
+import org.apache.qpid.framing.QueueDeleteOkBody;
+import org.apache.qpid.framing.ExchangeBoundOkBody;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class NoCloseOKStateManager extends AMQStateManager
+{
+ public NoCloseOKStateManager(AMQProtocolSession protocolSession)
+ {
+ super(protocolSession);
+ }
+
+ protected void registerListeners()
+ {
+ Map frame2handlerMap = new HashMap();
+
+ // we need to register a map for the null (i.e. all state) handlers otherwise you get
+ // a stack overflow in the handler searching code when you present it with a frame for which
+ // no handlers are registered
+ //
+ _state2HandlersMap.put(null, frame2handlerMap);
+
+ frame2handlerMap = new HashMap();
+ frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap();
+ frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap();
+ frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap);
+
+ //
+ // ConnectionOpen handlers
+ //
+ frame2handlerMap = new HashMap();
+ // Use Test Handler for Close methods to not send Close-OKs
+ frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandlerNoCloseOk.getInstance());
+
+ frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance());
+ frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance());
+ frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance());
+ frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance());
+ frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance());
+ frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap);
+ }
+
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java
new file mode 100644
index 0000000000..1d108b9c5c
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.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.test.unit.client.connection;
+
+import junit.framework.TestCase;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.transport.TransportConnection;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * ConnectionStartTest:
+ * This test verifies that a fresh connection is not started and no messages are delivered until the connection is
+ * started.
+ *
+ * After the connection is started then the message should be there, and the connection started.
+ *
+ * This Test verifies that using receive() and a messageListener does not cause message delivery before start is called.
+ *
+ */
+public class ConnectionStartTest extends TestCase
+{
+
+ String _broker = "vm://:1";
+
+ AMQConnection _connection;
+ private Session _consumerSess;
+ private MessageConsumer _consumer;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+
+ try
+ {
+ // Create Consumer Connection
+ _connection = new AMQConnection(_broker, "guest", "guest", "fred", "test");
+
+ _consumerSess = _connection.createSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+
+ Queue queue = _consumerSess.createQueue("ConnectionStartTest");
+
+ _consumer = _consumerSess.createConsumer(queue);
+
+
+ // Create Producer Connection to send message
+ AMQConnection pubCon = new AMQConnection(_broker, "guest", "guest", "fred", "test");
+
+ Session pubSess = pubCon.createSession(false, AMQSession.AUTO_ACKNOWLEDGE);
+
+ MessageProducer pub = pubSess.createProducer(queue);
+
+ pub.send(pubSess.createTextMessage("Initial Message"));
+
+ pubCon.close();
+
+ }
+ catch (Exception e)
+ {
+ fail("Connection to " + _broker + " should succeed. Reason: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _connection.close();
+ TransportConnection.killVMBroker(1);
+ super.tearDown();
+ }
+
+ public void testSimpleReceiveConnection()
+ {
+ try
+ {
+ assertTrue("Connection should not be started", !_connection.started());
+ //Note that this next line will start the dispatcher in the session
+ // should really not be called before _connection start
+ assertNull("There should not be messages waiting for the consumer", _consumer.receiveNoWait());
+ _connection.start();
+ assertNotNull("There should be messages waiting for the consumer", _consumer.receive(1000));
+ assertTrue("Connection should be started", _connection.started());
+
+ }
+ catch (JMSException e)
+ {
+ fail("An error occured during test because:" + e);
+ }
+
+ }
+
+ public void testMessageListenerConnection()
+ {
+ final CountDownLatch _gotMessage = new CountDownLatch(1);
+
+ try
+ {
+ assertTrue("Connection should not be started", !_connection.started());
+ _consumerSess.setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ try
+ {
+ assertTrue("Connection should be started", _connection.started());
+ assertEquals("Mesage Received", "Initial Message", ((TextMessage) message).getText());
+ _gotMessage.countDown();
+ }
+ catch (JMSException e)
+ {
+ fail("Couldn't get message text because:" + e.getCause());
+ }
+ }
+ });
+
+ // Ensure that setting a ML doesn't start the connection
+ assertTrue("Connection should not be started", !_connection.started());
+ // Ensure that the message wasn't delivered while the connection was stopped.
+ assertEquals("Message latch should still be set",1,_gotMessage.getCount());
+
+ _connection.start();
+ assertTrue("Connection should be started", _connection.started());
+
+ try
+ {
+ _gotMessage.await(1000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException e)
+ {
+ fail("Timed out awaiting message via onMessage");
+ }
+
+ }
+ catch (JMSException e)
+ {
+ fail("Failed because:" + e.getCause());
+ }
+
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ConnectionStartTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
new file mode 100644
index 0000000000..56394fee27
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java
@@ -0,0 +1,194 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.connection;
+
+import org.apache.qpid.AMQConnectionFailureException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQUnresolvedAddressException;
+import org.apache.qpid.client.AMQAuthenticationException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jms.Session;
+
+import junit.framework.TestCase;
+
+import javax.jms.Connection;
+import javax.jms.QueueSession;
+import javax.jms.TopicSession;
+
+public class ConnectionTest extends TestCase
+{
+
+ String _broker = "vm://:1";
+ String _broker_NotRunning = "vm://:2";
+ String _broker_BadDNS = "tcp://hg3sgaaw4lgihjs";
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ TransportConnection.killVMBroker(1);
+ }
+
+ public void testSimpleConnection()
+ {
+ try
+ {
+ AMQConnection conn = new AMQConnection(_broker, "guest", "guest", "fred", "test");
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Connection to " + _broker + " should succeed. Reason: " + e);
+ }
+ }
+
+
+ public void testDefaultExchanges()
+ {
+ try
+ {
+ AMQConnection conn = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='"
+ + _broker
+ + "?retries='1''&defaultQueueExchange='test.direct'"
+ + "&defaultTopicExchange='test.topic'"
+ + "&temporaryQueueExchange='tmp.direct'"
+ + "&temporaryTopicExchange='tmp.topic'");
+
+ QueueSession queueSession = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ AMQQueue queue = (AMQQueue) queueSession.createQueue("MyQueue");
+
+ assertEquals(queue.getExchangeName().toString(), "test.direct");
+
+ AMQQueue tempQueue = (AMQQueue) queueSession.createTemporaryQueue();
+
+ assertEquals(tempQueue.getExchangeName().toString(), "tmp.direct");
+
+
+ queueSession.close();
+
+
+ TopicSession topicSession = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ AMQTopic topic = (AMQTopic) topicSession.createTopic("silly.topic");
+
+ assertEquals(topic.getExchangeName().toString(), "test.topic");
+
+ AMQTopic tempTopic = (AMQTopic) topicSession.createTemporaryTopic();
+
+ assertEquals(tempTopic.getExchangeName().toString(), "tmp.topic");
+
+ topicSession.close();
+
+
+ conn.close();
+ }
+ catch (Exception e)
+ {
+ fail("Connection to " + _broker + " should succeed. Reason: " + e);
+ }
+ }
+
+ //fixme AMQAuthenticationException is not propogaged
+ public void PasswordFailureConnection() throws Exception
+ {
+ try
+ {
+ 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))
+ {
+ fail("Correct exception not thrown. Excpected 'AMQAuthenticationException' got: " + amqe);
+ }
+ }
+ }
+
+ public void testConnectionFailure() throws Exception
+ {
+ try
+ {
+ new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_NotRunning + "?retries='0''");
+ fail("Connection should not be established");
+ }
+ catch (AMQException amqe)
+ {
+ if (!(amqe instanceof AMQConnectionFailureException))
+ {
+ fail("Correct exception not thrown. Excpected 'AMQConnectionException' got: " + amqe);
+ }
+ }
+
+ }
+
+ public void testUnresolvedHostFailure() throws Exception
+ {
+ try
+ {
+ new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_BadDNS + "?retries='0''");
+ fail("Connection should not be established");
+ }
+ catch (AMQException amqe)
+ {
+ if (!(amqe instanceof AMQUnresolvedAddressException))
+ {
+ fail("Correct exception not thrown. Excpected 'AMQUnresolvedAddressException' got: " + amqe);
+ }
+ }
+ }
+
+ public void testClientIdCannotBeChanged() throws Exception
+ {
+ Connection connection = new AMQConnection(_broker, "guest", "guest",
+ "fred", "test");
+ try
+ {
+ connection.setClientID("someClientId");
+ fail("No IllegalStateException thrown when resetting clientid");
+ }
+ catch (javax.jms.IllegalStateException e)
+ {
+ // PASS
+ }
+ }
+
+ public void testClientIdIsPopulatedAutomatically() throws Exception
+ {
+ Connection connection = new AMQConnection(_broker, "guest", "guest",
+ null, "test");
+ assertNotNull(connection.getClientID());
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ConnectionTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java
new file mode 100644
index 0000000000..bfbba61913
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java
@@ -0,0 +1,481 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.connectionurl;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQBrokerDetails;
+import org.apache.qpid.client.AMQConnectionURL;
+import org.apache.qpid.jms.BrokerDetails;
+import org.apache.qpid.jms.ConnectionURL;
+import org.apache.qpid.url.URLSyntaxException;
+
+public class ConnectionURLTest extends TestCase
+{
+
+ public void testFailoverURL() throws URLSyntaxException
+ {
+ String url = "amqp://ritchiem:bob@/test?brokerlist='tcp://localhost:5672;tcp://fancyserver:3000/',failover='roundrobin'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod().equals("roundrobin"));
+ assertTrue(connectionurl.getUsername().equals("ritchiem"));
+ assertTrue(connectionurl.getPassword().equals("bob"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+ assertTrue(connectionurl.getBrokerCount() == 2);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+
+ service = connectionurl.getBrokerDetails(1);
+
+ assertTrue(service.getTransport().equals("tcp"));
+ assertTrue(service.getHost().equals("fancyserver"));
+ assertTrue(service.getPort() == 3000);
+
+ }
+
+ public void testSingleTransportUsernamePasswordURL() throws URLSyntaxException
+ {
+ String url = "amqp://ritchiem:bob@/test?brokerlist='tcp://localhost:5672'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("ritchiem"));
+ assertTrue(connectionurl.getPassword().equals("bob"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+ }
+
+ public void testSingleTransportUsernameBlankPasswordURL() throws URLSyntaxException
+ {
+ String url = "amqp://ritchiem:@/test?brokerlist='tcp://localhost:5672'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("ritchiem"));
+ assertTrue(connectionurl.getPassword().equals(""));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+ }
+
+ public void testFailedURLNullPassword()
+ {
+ String url = "amqp://ritchiem@/test?brokerlist='tcp://localhost:5672'";
+
+ try
+ {
+ new AMQConnectionURL(url);
+ fail("URL has null password");
+ }
+ catch (URLSyntaxException e)
+ {
+ assertTrue(e.getReason().equals("Null password in user information not allowed."));
+ assertTrue(e.getIndex() == 7);
+ }
+ }
+
+
+ public void testSingleTransportURL() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+ }
+
+ public void testSingleTransportWithClientURLURL() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@clientname/test?brokerlist='tcp://localhost:5672'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+ assertTrue(connectionurl.getClientName().equals("clientname"));
+
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+ }
+
+ public void testSingleTransport1OptionURL() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672',routingkey='jim'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+ assertTrue(connectionurl.getOption("routingkey").equals("jim"));
+ }
+
+ public void testSingleTransportDefaultedBroker() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='localhost'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+ }
+
+ public void testSingleTransportDefaultedBrokerWithPort() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='localhost:1234'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 1234);
+ }
+
+ public void testSingleTransportDefaultedBrokerWithIP() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='127.0.0.1'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+
+ assertTrue(service.getHost().equals("127.0.0.1"));
+ assertTrue(service.getPort() == 5672);
+ }
+
+ public void testSingleTransportDefaultedBrokerWithIPandPort() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='127.0.0.1:1234'";
+
+// ConnectionURL connectionurl = new AMQConnectionURL(url);
+//
+// assertTrue(connectionurl.getFailoverMethod() == null);
+// assertTrue(connectionurl.getUsername().equals("guest"));
+// assertTrue(connectionurl.getPassword().equals("guest"));
+// assertTrue(connectionurl.getVirtualHost().equals("/temp"));
+//
+//
+// assertTrue(connectionurl.getBrokerCount() == 1);
+//
+// BrokerDetails service = connectionurl.getBrokerDetails(0);
+//
+// assertTrue(service.getTransport().equals("tcp"));
+//
+// assertTrue(service.getHost().equals("127.0.0.1"));
+// assertTrue(service.getPort() == 1234);
+ }
+
+
+ public void testSingleTransportMultiOptionURL() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672',routingkey='jim',timeout='200',immediatedelivery='true'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("tcp"));
+
+ assertTrue(service.getHost().equals("localhost"));
+ assertTrue(service.getPort() == 5672);
+
+ assertTrue(connectionurl.getOption("routingkey").equals("jim"));
+ assertTrue(connectionurl.getOption("timeout").equals("200"));
+ assertTrue(connectionurl.getOption("immediatedelivery").equals("true"));
+ }
+
+ public void testSinglevmURL() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='vm://:2'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod() == null);
+ assertTrue(connectionurl.getUsername().equals("guest"));
+ assertTrue(connectionurl.getPassword().equals("guest"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("vm"));
+ assertTrue(service.getHost().equals(""));
+ assertTrue(service.getPort() == 2);
+
+ }
+
+ public void testFailoverVMURL() throws URLSyntaxException
+ {
+ String url = "amqp://ritchiem:bob@/test?brokerlist='vm://:2;vm://:3',failover='roundrobin'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getFailoverMethod().equals("roundrobin"));
+ assertTrue(connectionurl.getUsername().equals("ritchiem"));
+ assertTrue(connectionurl.getPassword().equals("bob"));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+ assertTrue(connectionurl.getBrokerCount() == 2);
+
+ BrokerDetails service = connectionurl.getBrokerDetails(0);
+
+ assertTrue(service.getTransport().equals("vm"));
+ assertTrue(service.getHost().equals(""));
+ assertTrue(service.getPort() == 2);
+
+ service = connectionurl.getBrokerDetails(1);
+ assertTrue(service.getTransport().equals("vm"));
+ assertTrue(service.getHost().equals(""));
+ assertTrue(service.getPort() == 3);
+ }
+
+
+ public void testNoVirtualHostURL()
+ {
+ String url = "amqp://user@?brokerlist='tcp://localhost:5672'";
+
+ try
+ {
+ new AMQConnectionURL(url);
+ fail("URL has no virtual host should not parse");
+ }
+ catch (URLSyntaxException e)
+ {
+ // This should occur.
+ }
+ }
+
+ public void testNoClientID() throws URLSyntaxException
+ {
+ String url = "amqp://user:@/test?brokerlist='tcp://localhost:5672'";
+
+ ConnectionURL connectionurl = new AMQConnectionURL(url);
+
+ assertTrue(connectionurl.getUsername().equals("user"));
+ assertTrue(connectionurl.getPassword().equals(""));
+ assertTrue(connectionurl.getVirtualHost().equals("/test"));
+
+ assertTrue(connectionurl.getBrokerCount() == 1);
+ }
+
+
+ public void testWrongOptionSeparatorInOptions()
+ {
+ String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672;tcp://localhost:5673'+failover='roundrobin'";
+ try
+ {
+ new AMQConnectionURL(url);
+ fail("URL Should not parse");
+ }
+ catch (URLSyntaxException urise)
+ {
+ assertTrue(urise.getReason().equals("Unterminated option. Possible illegal option separator:'+'"));
+ }
+
+ }
+
+
+ public void testNoUserDetailsProvidedWithClientID()
+
+ {
+ String url = "amqp://clientID/test?brokerlist='tcp://localhost:5672;tcp://localhost:5673'";
+ try
+ {
+ new AMQConnectionURL(url);
+ fail("URL Should not parse");
+ }
+ catch (URLSyntaxException urise)
+ {
+ assertTrue(urise.getMessage().startsWith("User information not found on url"));
+ }
+
+ }
+
+ public void testNoUserDetailsProvidedNOClientID()
+
+ {
+ String url = "amqp:///test?brokerlist='tcp://localhost:5672;tcp://localhost:5673'";
+ try
+ {
+ new AMQConnectionURL(url);
+ fail("URL Should not parse");
+ }
+ catch (URLSyntaxException urise)
+ {
+ assertTrue(urise.getMessage().startsWith("User information not found on url"));
+ }
+
+ }
+
+ public void testCheckVirtualhostFormat() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/t.-_+!=:?brokerlist='tcp://localhost:5672'";
+
+ AMQConnectionURL connection = new AMQConnectionURL(url);
+ assertTrue(connection.getVirtualHost().equals("/t.-_+!=:"));
+ }
+
+ public void testCheckDefaultPort() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@/test=:?brokerlist='tcp://localhost'";
+
+ AMQConnectionURL connection = new AMQConnectionURL(url);
+
+ BrokerDetails broker = connection.getBrokerDetails(0);
+ assertTrue(broker.getPort() == AMQBrokerDetails.DEFAULT_PORT);
+
+ }
+
+ public void testCheckMissingFinalQuote() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@id/test" + "?brokerlist='tcp://localhost:5672";
+
+ try
+ {
+ new AMQConnectionURL(url);
+ }
+ catch (URLSyntaxException e)
+ {
+ assertEquals(e.getMessage(), "Unterminated option at index 32: brokerlist='tcp://localhost:5672");
+ }
+ }
+
+
+ public void testDefaultExchanges() throws URLSyntaxException
+ {
+ String url = "amqp://guest:guest@id/test" + "?defaultQueueExchange='test.direct'&defaultTopicExchange='test.topic'&temporaryQueueExchange='tmp.direct'&temporaryTopicExchange='tmp.topic'";
+
+ AMQConnectionURL conn = new AMQConnectionURL(url);
+
+ assertEquals(conn.getDefaultQueueExchangeName(),"test.direct");
+
+ assertEquals(conn.getDefaultTopicExchangeName(),"test.topic");
+
+ assertEquals(conn.getTemporaryQueueExchangeName(),"tmp.direct");
+
+ assertEquals(conn.getTemporaryTopicExchangeName(),"tmp.topic");
+
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ConnectionURLTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java
new file mode 100644
index 0000000000..66be1ebc73
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.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.test.unit.client.destinationurl;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.URLSyntaxException;
+
+public class DestinationURLTest extends TestCase
+{
+ public void testFullURL() throws URLSyntaxException
+ {
+
+ String url = "exchange.Class://exchangeName/Destination/Queue";
+
+ AMQBindingURL dest = new AMQBindingURL(url);
+
+ assertTrue(url.equals(dest.toString()));
+
+ assertTrue(dest.getExchangeClass().equals("exchange.Class"));
+ assertTrue(dest.getExchangeName().equals("exchangeName"));
+ assertTrue(dest.getDestinationName().equals("Destination"));
+ assertTrue(dest.getQueueName().equals("Queue"));
+ }
+
+ public void testQueue() throws URLSyntaxException
+ {
+
+ String url = "exchangeClass://exchangeName//Queue";
+
+ AMQBindingURL dest = new AMQBindingURL(url);
+
+ assertTrue(url.equals(dest.toString()));
+
+ assertTrue(dest.getExchangeClass().equals("exchangeClass"));
+ assertTrue(dest.getExchangeName().equals("exchangeName"));
+ assertTrue(dest.getDestinationName().equals(""));
+ assertTrue(dest.getQueueName().equals("Queue"));
+ }
+
+ public void testQueueWithOption() throws URLSyntaxException
+ {
+
+ String url = "exchangeClass://exchangeName//Queue?option='value'";
+
+ AMQBindingURL dest = new AMQBindingURL(url);
+
+ assertTrue(url.equals(dest.toString()));
+
+ assertTrue(dest.getExchangeClass().equals("exchangeClass"));
+ assertTrue(dest.getExchangeName().equals("exchangeName"));
+ assertTrue(dest.getDestinationName().equals(""));
+ assertTrue(dest.getQueueName().equals("Queue"));
+ assertTrue(dest.getOption("option").equals("value"));
+ }
+
+
+ public void testDestination() throws URLSyntaxException
+ {
+
+ String url = "exchangeClass://exchangeName/Destination/";
+
+ AMQBindingURL dest = new AMQBindingURL(url);
+
+ assertTrue(url.equals(dest.toString()));
+
+ assertTrue(dest.getExchangeClass().equals("exchangeClass"));
+ assertTrue(dest.getExchangeName().equals("exchangeName"));
+ assertTrue(dest.getDestinationName().equals("Destination"));
+ assertTrue(dest.getQueueName().equals(""));
+ }
+
+ public void testDestinationWithOption() throws URLSyntaxException
+ {
+
+ String url = "exchangeClass://exchangeName/Destination/?option='value'";
+
+ AMQBindingURL dest = new AMQBindingURL(url);
+
+ assertTrue(url.equals(dest.toString()));
+
+ assertTrue(dest.getExchangeClass().equals("exchangeClass"));
+ assertTrue(dest.getExchangeName().equals("exchangeName"));
+ assertTrue(dest.getDestinationName().equals("Destination"));
+ assertTrue(dest.getQueueName().equals(""));
+
+ assertTrue(dest.getOption("option").equals("value"));
+ }
+
+ public void testDestinationWithMultiOption() throws URLSyntaxException
+ {
+
+ String url = "exchangeClass://exchangeName/Destination/?option='value',option2='value2'";
+
+ AMQBindingURL dest = new AMQBindingURL(url);
+
+ assertTrue(dest.getExchangeClass().equals("exchangeClass"));
+ assertTrue(dest.getExchangeName().equals("exchangeName"));
+ assertTrue(dest.getDestinationName().equals("Destination"));
+ assertTrue(dest.getQueueName().equals(""));
+
+ assertTrue(dest.getOption("option").equals("value"));
+ assertTrue(dest.getOption("option2").equals("value2"));
+ }
+
+ public void testDestinationWithNoExchangeDefaultsToDirect() throws URLSyntaxException
+ {
+
+ String url = "IBMPerfQueue1?durable='true'";
+
+ AMQBindingURL dest = new AMQBindingURL(url);
+
+ assertTrue(dest.getExchangeClass().equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS));
+ assertTrue(dest.getExchangeName().equals(""));
+ assertTrue(dest.getDestinationName().equals(""));
+ assertTrue(dest.getQueueName().equals("IBMPerfQueue1"));
+
+ assertTrue(dest.getOption("durable").equals("true"));
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(DestinationURLTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java
new file mode 100644
index 0000000000..19ef612bcc
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java
@@ -0,0 +1,123 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.forwardall;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.framing.AMQShortString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+/**
+ * Declare a private temporary response queue,
+ * send a message to amq.direct with a well known routing key with the
+ * private response queue as the reply-to destination
+ * consume responses.
+ */
+public class Client implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(Client.class);
+
+ private final AMQConnection _connection;
+ private final AMQSession _session;
+ private final int _expected;
+ private int _count;
+
+ Client(String broker, int expected) throws Exception
+ {
+ this(connect(broker), expected);
+ }
+
+ Client(AMQConnection connection, int expected) throws Exception
+ {
+ _connection = connection;
+ _expected = expected;
+ _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ AMQQueue response =
+ new AMQQueue(_connection.getDefaultQueueExchangeName(), new AMQShortString("ResponseQueue"), true);
+ _session.createConsumer(response).setMessageListener(this);
+ _connection.start();
+ AMQQueue service = new SpecialQueue(_connection, "ServiceQueue");
+ Message request = _session.createTextMessage("Request!");
+ request.setJMSReplyTo(response);
+ _session.createProducer(service).send(request);
+ }
+
+ void shutdownWhenComplete() throws Exception
+ {
+ waitUntilComplete();
+ _connection.close();
+ }
+
+ public synchronized void onMessage(Message response)
+ {
+
+ _logger.info("Received " + (++_count) + " of " + _expected + " responses.");
+ if (_count == _expected)
+ {
+
+ notifyAll();
+ }
+
+ }
+
+ synchronized void waitUntilComplete() throws Exception
+ {
+
+ if (_count < _expected)
+ {
+ wait(10000L);
+ }
+
+ if (_count < _expected)
+ {
+ throw new Exception("Didn't receive all messages... got " + _count + " expected " + _expected);
+ }
+ }
+
+ static AMQConnection connect(String broker) throws Exception
+ {
+ return new AMQConnection(broker, "guest", "guest", "Client" + System.currentTimeMillis(), "test");
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ final String connectionString;
+ final int expected;
+ if (argv.length == 0)
+ {
+ connectionString = "localhost:5672";
+ expected = 100;
+ }
+ else
+ {
+ connectionString = argv[0];
+ expected = Integer.parseInt(argv[1]);
+ }
+
+ new Client(connect(connectionString), expected).shutdownWhenComplete();
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java
new file mode 100644
index 0000000000..9cde24dd92
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.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.test.unit.client.forwardall;
+
+import junit.framework.TestCase;
+import org.apache.qpid.testutil.VMBrokerSetup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Runs the Service's and Client parts of the test in the same process
+ * as the broker
+ */
+public class CombinedTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(CombinedTest.class);
+ private int run = 0;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ ServiceCreator.closeAll();
+ super.tearDown();
+ }
+
+ public void testForwardAll() throws Exception
+ {
+ while (run < 10)
+ {
+ int services = 2;
+ ServiceCreator.start("vm://:1", services);
+
+ _logger.info("Starting " + ++run + " client...");
+
+ new Client("vm://:1", services).shutdownWhenComplete();
+
+
+ _logger.info("Completed " + run + " successfully!");
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new VMBrokerSetup(new junit.framework.TestSuite(CombinedTest.class));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java
new file mode 100644
index 0000000000..6593f7d86a
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.forwardall;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+
+/**
+ * Declare a queue and bind it to amq.direct with a 'well known' routing key,
+ * register a consumer for this queue and send a response to every message received.
+ */
+public class Service implements MessageListener
+{
+ private final AMQConnection _connection;
+ private final AMQSession _session;
+
+ Service(String broker) throws Exception
+ {
+ this(connect(broker));
+ }
+
+ Service(AMQConnection connection) throws Exception
+ {
+ _connection = connection;
+ AMQQueue queue = new SpecialQueue(connection, "ServiceQueue");
+ _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _session.createConsumer(queue).setMessageListener(this);
+ _connection.start();
+ }
+
+ public void onMessage(Message request)
+ {
+ try
+ {
+ Message response = _session.createTextMessage("Response!");
+ Destination replyTo = request.getJMSReplyTo();
+ _session.createProducer(replyTo).send(response);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ public void close() throws JMSException
+ {
+ _connection.close();
+ }
+
+ static AMQConnection connect(String broker) throws Exception
+ {
+ return new AMQConnection(broker, "guest", "guest", "Client" + System.currentTimeMillis(), "test");
+ }
+
+// public static void main(String[] argv) throws Exception
+// {
+// String broker = argv.length == 0? "localhost:5672" : argv[0];
+// new Service(broker);
+// }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java
new file mode 100644
index 0000000000..be16f6b7ae
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java
@@ -0,0 +1,112 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.forwardall;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+
+public class ServiceCreator implements Runnable
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ServiceCreator.class);
+
+ private static Thread[] threads;
+ private static ServiceCreator[] _services;
+
+ private final String broker;
+ private Service service;
+
+ ServiceCreator(String broker)
+ {
+ this.broker = broker;
+ }
+
+ public void run()
+ {
+ try
+ {
+ service = new Service(broker);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ public void closeSC() throws JMSException
+ {
+ service.close();
+ }
+
+ static void closeAll()
+ {
+ for (int i = 0; i < _services.length; i++)
+ {
+ try
+ {
+ _services[i].closeSC();
+ }
+ catch (JMSException e)
+ {
+ // ignore
+ }
+ }
+ }
+
+ static void start(String broker, int services) throws InterruptedException
+ {
+ threads = new Thread[services];
+ _services = new ServiceCreator[services];
+ ServiceCreator runner = new ServiceCreator(broker);
+ // start services
+ _logger.info("Starting " + services + " services...");
+ for (int i = 0; i < services; i++)
+ {
+ threads[i] = new Thread(runner);
+ _services[i] = runner;
+ threads[i].start();
+ }
+
+ for (int i = 0; i < threads.length; i++)
+ {
+ threads[i].join();
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ final String connectionString;
+ final int services;
+ if (argv.length == 0)
+ {
+ connectionString = "localhost:5672";
+ services = 100;
+ }
+ else
+ {
+ connectionString = argv[0];
+ services = Integer.parseInt(argv[1]);
+ }
+
+ start(connectionString, services);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java
new file mode 100644
index 0000000000..27371b0397
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.forwardall;
+
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.framing.AMQShortString;
+
+/**
+ * Queue that allows several private queues to be registered and bound
+ * to an exchange with the same routing key.
+ *
+ */
+class SpecialQueue extends AMQQueue
+{
+ private final AMQShortString name;
+
+ SpecialQueue(AMQConnection con, String name)
+ {
+ super(con, name, true);
+ this.name = new AMQShortString(name);
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return name;
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java
new file mode 100644
index 0000000000..bbabf0b57d
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java
@@ -0,0 +1,569 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.message;
+
+import java.util.HashMap;
+
+import javax.jms.MessageEOFException;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageNotReadableException;
+import javax.jms.MessageNotWriteableException;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.message.JMSBytesMessage;
+import org.apache.qpid.client.message.TestMessageHelper;
+
+public class BytesMessageTest extends TestCase
+{
+ /**
+ * Tests that on creation a call to getBodyLength() throws an exception
+ * if null was passed in during creation
+ */
+ public void testNotReadableOnCreationWithNull() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.getBodyLength();
+ fail("expected exception did not occur");
+ }
+ catch (MessageNotReadableException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageNotReadableException, got " + e);
+ }
+ }
+
+ public void testResetMakesReadble() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeInt(10);
+ bm.reset();
+ bm.writeInt(12);
+ fail("expected exception did not occur");
+ }
+ catch (MessageNotWriteableException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageNotWriteableException, got " + e);
+ }
+ }
+
+ public void testClearBodyMakesWritable() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeInt(10);
+ bm.reset();
+ bm.clearBody();
+ bm.writeInt(10);
+ }
+
+ public void testWriteBoolean() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeBoolean(true);
+ bm.writeBoolean(false);
+ bm.reset();
+ boolean val = bm.readBoolean();
+ assertEquals(true, val);
+ val = bm.readBoolean();
+ assertEquals(false, val);
+ }
+
+ public void testWriteInt() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeInt(10);
+ bm.reset();
+ long len = bm.getBodyLength();
+ assertTrue(len == 4);
+ int val = bm.readInt();
+ assertTrue(val == 10);
+ }
+
+ public void testWriteString() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeUTF("Bananas");
+ bm.reset();
+ String res = bm.readUTF();
+ assertEquals("Bananas", res);
+ }
+
+ public void testWriteBytes() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ byte[] bytes = {1,2,3,4};
+ bm.writeBytes(bytes, 1, 2);
+ bm.reset();
+ bytes = new byte[2];
+ bm.readBytes(bytes);
+ assertEquals(2, bytes[0]);
+ assertEquals(3, bytes[1]);
+ }
+
+ public void testWriteObject() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeObject(new Boolean(true));
+ bm.writeObject(new Boolean(false));
+ bm.writeObject(new Byte((byte)2));
+ bm.writeObject(new byte[]{1,2,3,4});
+ bm.writeObject(new Character('g'));
+ bm.writeObject(new Short((short) 29));
+ bm.writeObject(new Integer(101));
+ bm.writeObject(new Long(50003222L));
+ bm.writeObject("Foobar");
+ bm.writeObject(new Float(1.7f));
+ bm.writeObject(new Double(8.7d));
+ bm.reset();
+ assertTrue(bm.readBoolean());
+ assertTrue(!bm.readBoolean());
+ assertEquals((byte)2, bm.readByte());
+ byte[] bytes = new byte[4];
+ bm.readBytes(bytes);
+ assertEquals('g', bm.readChar());
+ assertEquals((short) 29, bm.readShort());
+ assertEquals(101, bm.readInt());
+ assertEquals(50003222L, bm.readLong());
+ assertEquals("Foobar", bm.readUTF());
+ assertEquals(1.7f, bm.readFloat());
+ assertEquals(8.7d, bm.readDouble());
+ }
+
+ public void testWriteObjectRejectsNonPrimitives() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeObject(new HashMap());
+ fail("expected MessageFormatException was not thrown");
+ }
+ catch (MessageFormatException e)
+ {
+ // pass
+ }
+ }
+
+ public void testWriteObjectThrowsNPE() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeObject(null);
+ fail("expected exception did not occur");
+ }
+ catch (NullPointerException n)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected NullPointerException, got " + e);
+ }
+ }
+
+ public void testReadBoolean() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeBoolean(true);
+ bm.reset();
+ boolean result = bm.readBoolean();
+ assertTrue(result);
+ }
+
+ public void testReadUnsignedByte() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeByte((byte) 9);
+ bm.reset();
+ int result = bm.readUnsignedByte();
+ assertEquals(9, result);
+ }
+
+ public void testReadUnsignedShort() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeShort((byte) 9);
+ bm.reset();
+ int result = bm.readUnsignedShort();
+ assertEquals(9, result);
+ }
+
+ public void testReadBytesChecksNull() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.readBytes(null);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.readBytes(null, 1);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+
+ public void testReadBytesChecksMaxSize() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ byte[] bytes = new byte[100];
+ bm.readBytes(bytes, 120);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+
+ public void testReadBytesReturnsCorrectLengths() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ byte[] bytes = {2, 3};
+ bm.writeBytes(bytes);
+ bm.reset();
+ int len = bm.readBytes(bytes);
+ assertEquals(2, len);
+ len = bm.readBytes(bytes);
+ assertEquals(-1, len);
+ len = bm.readBytes(bytes, 2);
+ assertEquals(-1, len);
+ bm.reset();
+ len = bm.readBytes(bytes, 2);
+ assertEquals(2, len);
+ bm.reset();
+ len = bm.readBytes(bytes, 1);
+ assertEquals(1, len);
+
+ }
+
+ public void testEOFByte() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeByte((byte)1);
+ bm.reset();
+ bm.readByte();
+ // should throw
+ bm.readByte();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFUnsignedByte() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeByte((byte)1);
+ bm.reset();
+ bm.readByte();
+ // should throw
+ bm.readUnsignedByte();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFBoolean() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeBoolean(true);
+ bm.reset();
+ bm.readBoolean();
+ // should throw
+ bm.readBoolean();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFChar() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeChar('A');
+ bm.reset();
+ bm.readChar();
+ // should throw
+ bm.readChar();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFDouble() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeDouble(1.3d);
+ bm.reset();
+ bm.readDouble();
+ // should throw
+ bm.readDouble();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFFloat() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeFloat(1.3f);
+ bm.reset();
+ bm.readFloat();
+ // should throw
+ bm.readFloat();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFInt() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeInt(99);
+ bm.reset();
+ bm.readInt();
+ // should throw
+ bm.readInt();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFLong() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeLong(4L);
+ bm.reset();
+ bm.readLong();
+ // should throw
+ bm.readLong();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFShort() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeShort((short)4);
+ bm.reset();
+ bm.readShort();
+ // should throw
+ bm.readShort();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFUnsignedShort() throws Exception
+ {
+ try
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeShort((short)4);
+ bm.reset();
+ bm.readUnsignedShort();
+ // should throw
+ bm.readUnsignedShort();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ /**
+ * Tests that the readBytes() method populates the passed in array
+ * correctly
+ * @throws Exception
+ */
+ public void testReadBytes() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeByte((byte)3);
+ bm.writeByte((byte)4);
+ bm.reset();
+ byte[] result = new byte[2];
+ int count = bm.readBytes(result);
+ assertEquals((byte)3, result[0]);
+ assertEquals((byte)4, result[1]);
+ assertEquals(2, count);
+ }
+
+ public void testReadBytesEOF() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeByte((byte)3);
+ bm.writeByte((byte)4);
+ bm.reset();
+ byte[] result = new byte[2];
+ bm.readBytes(result);
+ int count = bm.readBytes(result);
+ assertEquals(-1, count);
+ }
+
+ public void testReadBytesWithLargerArray() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeByte((byte)3);
+ bm.writeByte((byte)4);
+ bm.reset();
+ byte[] result = new byte[3];
+ int count = bm.readBytes(result);
+ assertEquals(2, count);
+ assertEquals((byte)3, result[0]);
+ assertEquals((byte)4, result[1]);
+ assertEquals((byte)0, result[2]);
+ }
+
+ public void testReadBytesWithCount() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.writeByte((byte)3);
+ bm.writeByte((byte)4);
+ bm.writeByte((byte)5);
+ bm.reset();
+ byte[] result = new byte[3];
+ int count = bm.readBytes(result, 2);
+ assertEquals(2, count);
+ assertEquals((byte)3, result[0]);
+ assertEquals((byte)4, result[1]);
+ assertEquals((byte)0, result[2]);
+ }
+
+ public void testToBodyStringWithNull() throws Exception
+ {
+ JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage();
+ bm.reset();
+ String result = bm.toBodyString();
+ assertNull(result);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(BytesMessageTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java
new file mode 100644
index 0000000000..3e04c36b38
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.client.message;
+
+import javax.jms.JMSException;
+import javax.jms.MessageFormatException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.message.JMSMapMessage;
+import org.apache.qpid.client.message.TestMessageHelper;
+
+
+public class MapMessageTest extends TestCase
+{
+
+ //Test Lookups
+
+ public void testBooleanLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+
+ mm.setBoolean("value", true);
+ Assert.assertEquals(true, mm.getBoolean("value"));
+ Assert.assertEquals("true", mm.getString("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testByteLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setByte("value", Byte.MAX_VALUE);
+
+ Assert.assertEquals(Byte.MAX_VALUE, mm.getByte("value"));
+ Assert.assertEquals((short) Byte.MAX_VALUE, mm.getShort("value"));
+ Assert.assertEquals(Byte.MAX_VALUE, mm.getInt("value"));
+ Assert.assertEquals((long) Byte.MAX_VALUE, mm.getLong("value"));
+ Assert.assertEquals("" + Byte.MAX_VALUE, mm.getString("value"));
+
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testShortLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setShort("value", Short.MAX_VALUE);
+ Assert.assertEquals(Short.MAX_VALUE, mm.getShort("value"));
+ Assert.assertEquals((int) Short.MAX_VALUE, mm.getInt("value"));
+ Assert.assertEquals((long) Short.MAX_VALUE, mm.getLong("value"));
+ Assert.assertEquals("" + Short.MAX_VALUE, mm.getString("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+
+ public void testCharLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+
+ mm.setChar("value", 'c');
+ Assert.assertEquals('c', mm.getChar("value"));
+ Assert.assertEquals("c", mm.getString("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+
+ mm.setString("value", null);
+ mm.getChar("value");
+ fail("Expected NullPointerException");
+
+ }
+ catch (NullPointerException e)
+ {
+ ; // pass
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+
+
+
+ }
+
+ public void testDoubleLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setDouble("value", Double.MAX_VALUE);
+ Assert.assertEquals(Double.MAX_VALUE, mm.getDouble("value"));
+ Assert.assertEquals("" + Double.MAX_VALUE, mm.getString("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testFloatLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setFloat("value", Float.MAX_VALUE);
+ Assert.assertEquals(Float.MAX_VALUE, mm.getFloat("value"));
+ Assert.assertEquals((double) Float.MAX_VALUE, mm.getDouble("value"));
+ Assert.assertEquals("" + Float.MAX_VALUE, mm.getString("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testIntLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setInt("value", Integer.MAX_VALUE);
+ Assert.assertEquals(Integer.MAX_VALUE, mm.getInt("value"));
+ Assert.assertEquals((long) Integer.MAX_VALUE, mm.getLong("value"));
+ Assert.assertEquals("" + Integer.MAX_VALUE, mm.getString("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testLongLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setLong("value", Long.MAX_VALUE);
+ Assert.assertEquals(Long.MAX_VALUE, mm.getLong("value"));
+ Assert.assertEquals("" + Long.MAX_VALUE, mm.getString("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testBytesLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ byte[] bytes = {99, 98, 97, 96, 95};
+ mm.setBytes("bytes", bytes);
+ assertBytesEqual(bytes, mm.getBytes("bytes"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ // Failed Lookups
+
+ public void testFailedBooleanLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ Assert.assertEquals(false, mm.getBoolean("int"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testFailedByteLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getByte("random");
+ Assert.fail("NumberFormatException expected");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+
+ }
+
+ public void testFailedBytesLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getBytes("random");
+ Assert.fail("MessageFormatException expected");
+ }
+ catch (MessageFormatException mfe)
+ {
+ //normal path
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedCharLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getChar("random");
+ Assert.fail("MessageFormatException expected");
+ }
+ catch (MessageFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedDoubleLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getDouble("random");
+ Assert.fail("NullPointerException should be received.");
+ }
+ catch (NullPointerException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedFloatLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getFloat("random");
+ Assert.fail("NullPointerException should be received.");
+ }
+ catch (NullPointerException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedIntLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getInt("random");
+ Assert.fail("NumberFormatException should be received.");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedLongLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getLong("random");
+ Assert.fail("NumberFormatException should be received.");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedShortLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getShort("random");
+ Assert.fail("NumberFormatException should be received.");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+
+ private void assertBytesEqual(byte[] expected, byte[] actual)
+ {
+ Assert.assertEquals(expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ Assert.assertEquals(expected[index], actual[index]);
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(MapMessageTest.class);
+ }
+
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java
new file mode 100644
index 0000000000..cd03b523d1
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java
@@ -0,0 +1,345 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.message;
+
+import junit.framework.TestCase;
+
+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.testutil.VMBrokerSetup;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class ObjectMessageTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ObjectMessageTest.class);
+
+ private AMQConnection connection;
+ private AMQDestination destination;
+ private AMQSession session;
+ private MessageProducer producer;
+ private Serializable[] data;
+ private volatile boolean waiting;
+ private int received;
+ private final ArrayList items = new ArrayList();
+
+ private String _broker = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ connection = new AMQConnection(_broker, "guest", "guest", randomize("Client"), "test");
+ destination = new AMQQueue(connection, randomize("LatencyTest"), true);
+ session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ // set up a consumer
+ session.createConsumer(destination).setMessageListener(this);
+ connection.start();
+
+ // create a publisher
+ producer = session.createProducer(destination, false, false, true);
+ A a1 = new A(1, "A");
+ A a2 = new A(2, "a");
+ B b = new B(1, "B");
+ C c = new C();
+ c.put("A1", a1);
+ c.put("a2", a2);
+ c.put("B", b);
+ c.put("String", "String");
+
+ data = new Serializable[] { a1, a2, b, c, "Hello World!", new Integer(1001) };
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public ObjectMessageTest()
+ { }
+
+ ObjectMessageTest(String broker) throws Exception
+ {
+ _broker = broker;
+ }
+
+ public void testSendAndReceive() throws Exception
+ {
+ try
+ {
+ send();
+ waitUntilReceived(data.length);
+ check();
+ _logger.info("All " + data.length + " items matched.");
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ fail("This Test should succeed but failed due to: " + e);
+ }
+ finally
+ {
+ close();
+ }
+ }
+
+ public void testSetObjectPropertyForString() throws Exception
+ {
+ String testStringProperty = "TestStringProperty";
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestStringProperty", testStringProperty);
+ assertEquals(testStringProperty, msg.getObjectProperty("TestStringProperty"));
+ }
+
+ public void testSetObjectPropertyForBoolean() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestBooleanProperty", Boolean.TRUE);
+ assertEquals(Boolean.TRUE, msg.getObjectProperty("TestBooleanProperty"));
+ }
+
+ public void testSetObjectPropertyForByte() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestByteProperty", Byte.MAX_VALUE);
+ assertEquals(Byte.MAX_VALUE, msg.getObjectProperty("TestByteProperty"));
+ }
+
+ public void testSetObjectPropertyForShort() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestShortProperty", Short.MAX_VALUE);
+ assertEquals(Short.MAX_VALUE, msg.getObjectProperty("TestShortProperty"));
+ }
+
+ public void testSetObjectPropertyForInteger() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestIntegerProperty", Integer.MAX_VALUE);
+ assertEquals(Integer.MAX_VALUE, msg.getObjectProperty("TestIntegerProperty"));
+ }
+
+ public void testSetObjectPropertyForDouble() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestDoubleProperty", Double.MAX_VALUE);
+ assertEquals(Double.MAX_VALUE, msg.getObjectProperty("TestDoubleProperty"));
+ }
+
+ public void testSetObjectPropertyForFloat() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestFloatProperty", Float.MAX_VALUE);
+ assertEquals(Float.MAX_VALUE, msg.getObjectProperty("TestFloatProperty"));
+ }
+
+ public void testSetObjectPropertyForByteArray() throws Exception
+ {
+ byte[] array = { 1, 2, 3, 4, 5 };
+ ObjectMessage msg = session.createObjectMessage(data[0]);
+ msg.setObjectProperty("TestByteArrayProperty", array);
+ assertTrue(Arrays.equals(array, (byte[]) msg.getObjectProperty("TestByteArrayProperty")));
+ }
+
+ public void testSetObjectForNull() throws Exception
+ {
+ ObjectMessage msg = session.createObjectMessage();
+ msg.setObject(null);
+ assertNull(msg.getObject());
+ }
+
+ private void send() throws Exception
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ ObjectMessage msg;
+ if ((i % 2) == 0)
+ {
+ msg = session.createObjectMessage(data[i]);
+ }
+ else
+ {
+ msg = session.createObjectMessage();
+ msg.setObject(data[i]);
+ }
+
+ producer.send(msg);
+ }
+ }
+
+ public void check() throws Exception
+ {
+ Object[] actual = (Object[]) items.toArray();
+ if (actual.length != data.length)
+ {
+ throw new Exception("Expected " + data.length + " objects, got " + actual.length);
+ }
+
+ for (int i = 0; i < data.length; i++)
+ {
+ if (actual[i] instanceof Exception)
+ {
+ throw new Exception("Error on receive of " + data[i], ((Exception) actual[i]));
+ }
+
+ if (actual[i] == null)
+ {
+ throw new Exception("Expected " + data[i] + " got null");
+ }
+
+ if (!data[i].equals(actual[i]))
+ {
+ throw new Exception("Expected " + data[i] + " got " + actual[i]);
+ }
+ }
+ }
+
+ private void close() throws Exception
+ {
+ session.close();
+ connection.close();
+ }
+
+ private synchronized void waitUntilReceived(int count) throws InterruptedException
+ {
+ waiting = true;
+ while (received < count)
+ {
+ wait();
+ }
+
+ waiting = false;
+ }
+
+ public void onMessage(Message message)
+ {
+
+ try
+ {
+ if (message instanceof ObjectMessage)
+ {
+ items.add(((ObjectMessage) message).getObject());
+ }
+ else
+ {
+ _logger.error("ERROR: Got " + message.getClass().getName() + " not ObjectMessage");
+ items.add(message);
+ }
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ items.add(e);
+ }
+
+ synchronized (this)
+ {
+ received++;
+ notify();
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ String broker = (argv.length > 0) ? argv[0] : "vm://:1";
+ if ("-help".equals(broker))
+ {
+ System.out.println("Usage: <broker>");
+ }
+
+ new ObjectMessageTest(broker).testSendAndReceive();
+ }
+
+ private static class A implements Serializable
+ {
+ private String sValue;
+ private int iValue;
+
+ A(int i, String s)
+ {
+ sValue = s;
+ iValue = i;
+ }
+
+ public int hashCode()
+ {
+ return iValue;
+ }
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof A) && equals((A) o);
+ }
+
+ protected boolean equals(A a)
+ {
+ return areEqual(a.sValue, sValue) && (a.iValue == iValue);
+ }
+ }
+
+ private static class B extends A
+ {
+ private long time;
+
+ B(int i, String s)
+ {
+ super(i, s);
+ time = System.currentTimeMillis();
+ }
+
+ protected boolean equals(A a)
+ {
+ return super.equals(a) && (a instanceof B) && (time == ((B) a).time);
+ }
+ }
+
+ private static class C extends HashMap implements Serializable
+ { }
+
+ private static boolean areEqual(Object a, Object b)
+ {
+ return (a == null) ? (b == null) : a.equals(b);
+ }
+
+ private static String randomize(String in)
+ {
+ return in + System.currentTimeMillis();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new VMBrokerSetup(new junit.framework.TestSuite(ObjectMessageTest.class));
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java
new file mode 100644
index 0000000000..802f1e6c2e
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java
@@ -0,0 +1,623 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.message;
+
+import java.util.HashMap;
+
+import javax.jms.JMSException;
+import javax.jms.MessageEOFException;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageNotReadableException;
+import javax.jms.MessageNotWriteableException;
+import javax.jms.StreamMessage;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.message.JMSStreamMessage;
+import org.apache.qpid.client.message.TestMessageHelper;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class StreamMessageTest extends TestCase
+{
+ /**
+ * Tests that on creation a call to getBodyLength() throws an exception
+ * if null was passed in during creation
+ */
+ public void testNotReadableOnCreationWithNull() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.readByte();
+ fail("expected exception did not occur");
+ }
+ catch (MessageNotReadableException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageNotReadableException, got " + e);
+ }
+ }
+
+ public void testResetMakesReadble() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeInt(10);
+ bm.reset();
+ bm.writeInt(12);
+ fail("expected exception did not occur");
+ }
+ catch (MessageNotWriteableException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageNotWriteableException, got " + e);
+ }
+ }
+
+ public void testClearBodyMakesWritable() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeInt(10);
+ bm.reset();
+ bm.clearBody();
+ bm.writeInt(10);
+ }
+
+ public void testWriteBoolean() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeBoolean(true);
+ bm.writeBoolean(false);
+ bm.reset();
+ boolean val = bm.readBoolean();
+ assertEquals(true, val);
+ val = bm.readBoolean();
+ assertEquals(false, val);
+ }
+
+ public void testWriteInt() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeInt(10);
+ bm.reset();
+ int val = bm.readInt();
+ assertTrue(val == 10);
+ }
+
+ public void testWriteString() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeString("Bananas");
+ bm.reset();
+ String res = bm.readString();
+ assertEquals("Bananas", res);
+ }
+
+ public void testWriteBytes() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ byte[] bytes = {1,2,3,4};
+ bm.writeBytes(bytes, 1, 2);
+ bm.reset();
+ bytes = new byte[2];
+ bm.readBytes(bytes);
+ assertEquals(2, bytes[0]);
+ assertEquals(3, bytes[1]);
+ }
+
+ public void testWriteObject() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeObject(new Boolean(true));
+ bm.writeObject(new Boolean(false));
+ bm.writeObject(new Byte((byte)2));
+ bm.writeObject(new byte[]{1,2,3,4});
+ bm.writeObject(new Character('g'));
+ bm.writeObject(new Short((short) 29));
+ bm.writeObject(new Integer(101));
+ bm.writeObject(new Long(50003222L));
+ bm.writeObject("Foobar");
+ bm.writeObject(new Float(1.7f));
+ bm.writeObject(new Double(8.7d));
+ bm.reset();
+ assertTrue(bm.readBoolean());
+ assertTrue(!bm.readBoolean());
+ assertEquals((byte)2, bm.readByte());
+ byte[] bytes = new byte[4];
+ bm.readBytes(bytes);
+ assertEquals('g', bm.readChar());
+ assertEquals((short) 29, bm.readShort());
+ assertEquals(101, bm.readInt());
+ assertEquals(50003222L, bm.readLong());
+ assertEquals("Foobar", bm.readString());
+ assertEquals(1.7f, bm.readFloat());
+ assertEquals(8.7d, bm.readDouble());
+ }
+
+ public void testWriteObjectRejectsNonPrimitives() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeObject(new HashMap());
+ fail("expected MessageFormatException was not thrown");
+ }
+ catch (MessageFormatException e)
+ {
+ // pass
+ }
+ }
+
+ public void testReadBoolean() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeBoolean(true);
+ bm.reset();
+ boolean result = bm.readBoolean();
+ assertTrue(result);
+ }
+
+ public void testReadBytesChecksNull() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.readBytes(null);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+
+ public void testReadBytesReturnsCorrectLengths() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ byte[] bytes = {2, 3};
+ bm.writeBytes(bytes);
+ bm.writeBytes(null);
+ bm.writeBytes(new byte[]{});
+ bm.reset();
+ int len = bm.readBytes(bytes);
+ assertEquals(2, len);
+ len = bm.readBytes(bytes);
+ assertEquals(-1, len);
+ len = bm.readBytes(bytes);
+ assertEquals(-1, len);
+ len = bm.readBytes(bytes);
+ assertEquals(0, len);
+ }
+
+ public void testReadBytesFollowedByPrimitive() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeBytes(new byte[]{2, 3, 4, 5, 6, 7, 8});
+ bm.writeBytes(new byte[]{2, 3, 4, 5, 6, 7});
+ bm.writeString("Foo");
+ bm.reset();
+ int len;
+ do
+ {
+ len = bm.readBytes(new byte[2]);
+ }
+ while (len == 2);
+
+ do
+ {
+ len = bm.readBytes(new byte[2]);
+ }
+ while (len == 2);
+
+ assertEquals("Foo", bm.readString());
+ }
+
+ public void testReadMultipleByteArrays() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ byte[] bytes = {2, 3, 4};
+ bm.writeBytes(bytes);
+ bm.writeBytes(bytes);
+ bm.reset();
+ byte[] result = new byte[2];
+ int len = bm.readBytes(result);
+ assertEquals(2, len);
+ len = bm.readBytes(result);
+ assertEquals(1, len);
+ len = bm.readBytes(result);
+ assertEquals(2, len);
+ }
+
+ public void testEOFByte() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeByte((byte)1);
+ bm.reset();
+ bm.readByte();
+ // should throw
+ bm.readByte();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFBoolean() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeBoolean(true);
+ bm.reset();
+ bm.readBoolean();
+ // should throw
+ bm.readBoolean();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFChar() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeChar('A');
+ bm.reset();
+ bm.readChar();
+ // should throw
+ bm.readChar();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFDouble() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeDouble(1.3d);
+ bm.reset();
+ bm.readDouble();
+ // should throw
+ bm.readDouble();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFFloat() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeFloat(1.3f);
+ bm.reset();
+ bm.readFloat();
+ // should throw
+ bm.readFloat();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFInt() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeInt(99);
+ bm.reset();
+ bm.readInt();
+ // should throw
+ bm.readInt();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFLong() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeLong(4L);
+ bm.reset();
+ bm.readLong();
+ // should throw
+ bm.readLong();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testEOFShort() throws Exception
+ {
+ try
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeShort((short)4);
+ bm.reset();
+ bm.readShort();
+ // should throw
+ bm.readShort();
+ fail("expected exception did not occur");
+ }
+ catch (MessageEOFException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected MessageEOFException, got " + e);
+ }
+ }
+
+ public void testToBodyStringWithNull() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.reset();
+ String result = bm.toBodyString();
+ assertNull(result);
+ }
+
+ private void checkConversionsFail(StreamMessage sm, int[] conversions) throws JMSException
+ {
+ for (int conversion : conversions)
+ {
+ try
+ {
+ switch (conversion)
+ {
+ case 0:
+ sm.readBoolean();
+ break;
+ case 1:
+ sm.readByte();
+ break;
+ case 2:
+ sm.readShort();
+ break;
+ case 3:
+ sm.readChar();
+ break;
+ case 4:
+ sm.readInt();
+ break;
+ case 5:
+ sm.readLong();
+ break;
+ case 6:
+ sm.readFloat();
+ break;
+ case 7:
+ sm.readDouble();
+ break;
+ case 8:
+ sm.readString();
+ break;
+ case 9:
+ sm.readBytes(new byte[3]);
+ break;
+ }
+ fail("MessageFormatException was not thrown");
+ }
+ catch (MessageFormatException e)
+ {
+ // PASS
+ }
+ sm.reset();
+ }
+ }
+ public void testBooleanConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeBoolean(true);
+ bm.reset();
+ String result = bm.readString();
+ assertEquals("true", result);
+ bm.reset();
+ checkConversionsFail(bm, new int[]{1,2,3,4,5,6,7,9});
+ }
+
+ public void testByteConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeByte((byte) 43);
+ bm.reset();
+ assertEquals(43, bm.readShort());
+ bm.reset();
+ assertEquals(43, bm.readInt());
+ bm.reset();
+ assertEquals(43, bm.readLong());
+ bm.reset();
+ String result = bm.readString();
+ assertEquals("43", result);
+ bm.reset();
+ checkConversionsFail(bm, new int[]{0, 3, 6, 7, 9});
+ }
+
+ public void testShortConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeShort((short) 87);
+ bm.reset();
+ assertEquals(87, bm.readInt());
+ bm.reset();
+ assertEquals(87, bm.readLong());
+ bm.reset();
+ assertEquals("87", bm.readString());
+ bm.reset();
+ checkConversionsFail(bm, new int[]{0, 1, 3, 6, 7, });
+ }
+
+ public void testCharConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeChar('d');
+ bm.reset();
+ assertEquals("d", bm.readString());
+ bm.reset();
+ checkConversionsFail(bm, new int[]{0, 1, 2, 4, 5, 6, 7, 9});
+ }
+
+ public void testIntConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeInt(167);
+ bm.reset();
+ assertEquals(167, bm.readLong());
+ bm.reset();
+ assertEquals("167", bm.readString());
+ bm.reset();
+ checkConversionsFail(bm, new int[]{0, 1, 2, 3, 6, 7, 9});
+ }
+
+ public void testLongConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeLong(1678);
+ bm.reset();
+ assertEquals("1678", bm.readString());
+ bm.reset();
+ checkConversionsFail(bm, new int[]{0, 1, 2, 3, 4, 6, 7, 9});
+ }
+
+ public void testFloatConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeFloat(6.2f);
+ bm.reset();
+ assertEquals(6.2d, bm.readDouble(), 0.01);
+ bm.reset();
+ assertEquals("6.2", bm.readString());
+ bm.reset();
+ checkConversionsFail(bm, new int[]{0, 1, 2, 3, 4, 5, 9});
+ }
+
+ public void testDoubleConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeDouble(88.35d);
+ bm.reset();
+ assertEquals("88.35", bm.readString());
+ bm.reset();
+ checkConversionsFail(bm, new int[]{0, 1, 2, 3, 4, 5, 6, 9});
+ }
+
+ public void testStringConversions() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeString("true");
+ bm.reset();
+ assertEquals(true, bm.readBoolean());
+ bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeString("2");
+ bm.reset();
+ assertEquals((byte)2, bm.readByte());
+ bm.reset();
+ assertEquals((short)2, bm.readShort());
+ bm.reset();
+ assertEquals(2, bm.readInt());
+ bm.reset();
+ assertEquals((long)2, bm.readLong());
+ bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeString("5.7");
+ bm.reset();
+ assertEquals(5.7f, bm.readFloat());
+ bm.reset();
+ assertEquals(5.7d, bm.readDouble());
+ }
+
+ public void testNulls() throws Exception
+ {
+ JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage();
+ bm.writeString(null);
+ bm.writeObject(null);
+ bm.reset();
+ assertNull(bm.readObject());
+ assertNull(bm.readObject());
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(StreamMessageTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java
new file mode 100644
index 0000000000..30f3b0b4eb
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java
@@ -0,0 +1,300 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.message;
+
+import javax.jms.JMSException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.message.JMSMapMessage;
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.client.message.TestMessageHelper;
+
+public class TextMessageTest extends TestCase
+{
+ public void testTextOnConstruction() throws Exception
+ {
+ JMSTextMessage tm = TestMessageHelper.newJMSTextMessage();
+ tm.setText("pies");
+ String val = tm.getText();
+ assertEquals(val, "pies");
+ }
+
+ public void testClearBody() throws Exception
+ {
+ JMSTextMessage tm = TestMessageHelper.newJMSTextMessage();
+ tm.setText("pies");
+ tm.clearBody();
+ String val = tm.getText();
+ assertNull(val);
+ tm.setText("Banana");
+ val = tm.getText();
+ assertEquals(val, "Banana");
+ }
+
+
+ public void testBooleanPropertyLookup()
+ {
+ try
+ {
+ JMSTextMessage tm = TestMessageHelper.newJMSTextMessage();
+
+ tm.setBooleanProperty("value", true);
+ Assert.assertEquals(true, tm.getBooleanProperty("value"));
+ Assert.assertEquals("true", tm.getStringProperty("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testBytePropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setByteProperty("value", Byte.MAX_VALUE);
+
+ Assert.assertEquals(Byte.MAX_VALUE, mm.getByteProperty("value"));
+ Assert.assertEquals((short) Byte.MAX_VALUE, mm.getShortProperty("value"));
+ Assert.assertEquals(Byte.MAX_VALUE, mm.getIntProperty("value"));
+ Assert.assertEquals((long) Byte.MAX_VALUE, mm.getLongProperty("value"));
+ Assert.assertEquals("" + Byte.MAX_VALUE, mm.getStringProperty("value"));
+
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testShortPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setShortProperty("value", Short.MAX_VALUE);
+ Assert.assertEquals(Short.MAX_VALUE, mm.getShortProperty("value"));
+ Assert.assertEquals((int) Short.MAX_VALUE, mm.getIntProperty("value"));
+ Assert.assertEquals((long) Short.MAX_VALUE, mm.getLongProperty("value"));
+ Assert.assertEquals("" + Short.MAX_VALUE, mm.getStringProperty("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testDoublePropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setDoubleProperty("value", Double.MAX_VALUE);
+ Assert.assertEquals(Double.MAX_VALUE, mm.getDoubleProperty("value"));
+ Assert.assertEquals("" + Double.MAX_VALUE, mm.getStringProperty("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testFloatPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setFloatProperty("value", Float.MAX_VALUE);
+ Assert.assertEquals(Float.MAX_VALUE, mm.getFloatProperty("value"));
+ Assert.assertEquals((double) Float.MAX_VALUE, mm.getDoubleProperty("value"));
+ Assert.assertEquals("" + Float.MAX_VALUE, mm.getStringProperty("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testIntPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setIntProperty("value", Integer.MAX_VALUE);
+ Assert.assertEquals(Integer.MAX_VALUE, mm.getIntProperty("value"));
+ Assert.assertEquals((long) Integer.MAX_VALUE, mm.getLongProperty("value"));
+ Assert.assertEquals("" + Integer.MAX_VALUE, mm.getStringProperty("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testLongPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.setLongProperty("value", Long.MAX_VALUE);
+ Assert.assertEquals(Long.MAX_VALUE, mm.getLongProperty("value"));
+ Assert.assertEquals("" + Long.MAX_VALUE, mm.getStringProperty("value"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+
+ // Failed Lookups
+
+ public void testFailedBooleanPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ Assert.assertEquals(false, mm.getBooleanProperty("int"));
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received." + e);
+ }
+ }
+
+ public void testFailedBytePropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getByteProperty("random");
+ Assert.fail("NumberFormatException expected");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+
+ }
+
+ public void testFailedDoublePropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getDoubleProperty("random");
+ Assert.fail("NullPointerException should be received.");
+ }
+ catch (NullPointerException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedFloatPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getFloatProperty("random");
+ Assert.fail("NullPointerException should be received.");
+ }
+ catch (NullPointerException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedIntPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getIntProperty("random");
+ Assert.fail("NumberFormatException should be received.");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedLongPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getLongProperty("random");
+ Assert.fail("NumberFormatException should be received.");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+ public void testFailedShortPropertyLookup()
+ {
+ try
+ {
+ JMSMapMessage mm = TestMessageHelper.newJMSMapMessage();
+ mm.getShortProperty("random");
+ Assert.fail("NumberFormatException should be received.");
+ }
+ catch (NumberFormatException e)
+ {
+ //normal execution
+ }
+ catch (JMSException e)
+ {
+ Assert.fail("JMSException received:" + e);
+ }
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TextMessageTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.java
new file mode 100644
index 0000000000..d14e0b771f
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.java
@@ -0,0 +1,104 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.client.protocol;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoService;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.IoSessionConfig;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.common.support.BaseIoSession;
+
+public class TestIoSession extends BaseIoSession {
+
+ private String _stringLocalAddress;
+ private int _localPort;
+
+ public SocketAddress getLocalAddress()
+ {
+ //create a new address for testing purposes using member variables
+ return new InetSocketAddress(_stringLocalAddress,_localPort);
+ }
+
+ protected void updateTrafficMask() {
+ //dummy
+ }
+
+ public IoService getService() {
+ return null;
+ }
+
+ public IoServiceConfig getServiceConfig() {
+ return null;
+ }
+
+ public IoHandler getHandler() {
+ return null;
+ }
+
+ public IoSessionConfig getConfig() {
+ return null;
+ }
+
+ public IoFilterChain getFilterChain() {
+ return null;
+ }
+
+ public TransportType getTransportType() {
+ return null;
+ }
+
+ public SocketAddress getRemoteAddress() {
+ return null;
+ }
+
+ public SocketAddress getServiceAddress() {
+ return null;
+ }
+
+ public int getScheduledWriteRequests() {
+ return 0;
+ }
+
+ public int getScheduledWriteBytes() {
+ return 0;
+ }
+
+ public String getStringLocalAddress() {
+ return _stringLocalAddress;
+ }
+
+ public void setStringLocalAddress(String _stringLocalAddress) {
+ this._stringLocalAddress = _stringLocalAddress;
+ }
+
+ public int getLocalPort() {
+ return _localPort;
+ }
+
+ public void setLocalPort(int _localPort) {
+ this._localPort = _localPort;
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java
new file mode 100644
index 0000000000..241c9177a8
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java
@@ -0,0 +1,232 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.test.unit.client.temporaryqueue;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TemporaryQueue;
+import javax.jms.TextMessage;
+import javax.jms.Queue;
+
+import junit.framework.TestCase;
+import junit.framework.Assert;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.url.URLSyntaxException;
+
+import java.util.List;
+import java.util.LinkedList;
+
+public class TemporaryQueueTest extends TestCase
+{
+
+ String _broker = "vm://:1";
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+
+ protected Connection createConnection() throws AMQException, URLSyntaxException
+ {
+ return new AMQConnection(_broker, "guest", "guest",
+ "fred", "test");
+ }
+
+ public void testTempoaryQueue() throws Exception
+ {
+ Connection conn = createConnection();
+ Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ TemporaryQueue queue = session.createTemporaryQueue();
+ assertNotNull(queue);
+ MessageProducer producer = session.createProducer(queue);
+ MessageConsumer consumer = session.createConsumer(queue);
+ conn.start();
+ producer.send(session.createTextMessage("hello"));
+ TextMessage tm = (TextMessage) consumer.receive(2000);
+ assertNotNull(tm);
+ assertEquals("hello", tm.getText());
+
+ try
+ {
+ queue.delete();
+ fail("Expected JMSException : should not be able to delete while there are active consumers");
+ }
+ catch (JMSException je)
+ {
+ ; //pass
+ }
+
+ consumer.close();
+
+ try
+ {
+ queue.delete();
+ }
+ catch (JMSException je)
+ {
+ fail("Unexpected Exception: " + je.getMessage());
+ }
+
+ conn.close();
+ }
+
+ public void tUniqueness() throws JMSException, AMQException, URLSyntaxException
+ {
+ int numProcs = Runtime.getRuntime().availableProcessors();
+ final int threadsProc = 5;
+
+ runUniqueness(1, 10);
+ runUniqueness(numProcs * threadsProc, 10);
+ runUniqueness(numProcs * threadsProc, 100);
+ runUniqueness(numProcs * threadsProc, 500);
+ }
+
+ void runUniqueness(int makers, int queues) throws JMSException, AMQException, URLSyntaxException
+ {
+ Connection connection = createConnection();
+
+ Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ List<TempQueueMaker> tqList = new LinkedList<TempQueueMaker>();
+
+ //Create Makers
+ for (int m = 0; m < makers; m++)
+ {
+ tqList.add(new TempQueueMaker(session, queues));
+ }
+
+
+ List<Thread> threadList = new LinkedList<Thread>();
+
+ //Create Makers
+ for (TempQueueMaker maker : tqList)
+ {
+ threadList.add(new Thread(maker));
+ }
+
+ //Start threads
+ for (Thread thread : threadList)
+ {
+ thread.start();
+ }
+
+ // Join Threads
+ for (Thread thread : threadList)
+ {
+ try
+ {
+ thread.join();
+ }
+ catch (InterruptedException e)
+ {
+ fail("Couldn't correctly join threads");
+ }
+ }
+
+
+ List<AMQQueue> list = new LinkedList<AMQQueue>();
+
+ // Test values
+ for (TempQueueMaker maker : tqList)
+ {
+ check(maker, list);
+ }
+
+ Assert.assertEquals("Not enough queues made.", makers * queues, list.size());
+
+ connection.close();
+ }
+
+ private void check(TempQueueMaker tq, List<AMQQueue> list)
+ {
+ for (AMQQueue q : tq.getList())
+ {
+ if (list.contains(q))
+ {
+ fail(q + " already exists.");
+ }
+ else
+ {
+ list.add(q);
+ }
+ }
+ }
+
+
+ class TempQueueMaker implements Runnable
+ {
+ List<AMQQueue> _queues;
+ Session _session;
+ private int _count;
+
+
+ TempQueueMaker(Session session, int queues) throws JMSException
+ {
+ _queues = new LinkedList<AMQQueue>();
+
+ _count = queues;
+
+ _session = session;
+ }
+
+ public void run()
+ {
+ int i = 0;
+ try
+ {
+ for (; i < _count; i++)
+ {
+ _queues.add((AMQQueue) _session.createTemporaryQueue());
+ }
+ }
+ catch (JMSException jmse)
+ {
+ //stop
+ }
+ }
+
+ List<AMQQueue> getList()
+ {
+ return _queues;
+ }
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TemporaryQueueTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java
new file mode 100644
index 0000000000..5a61480f6a
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.close;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+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 javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.Session;
+
+/**
+ * This test forces the situation where a session is closed whilst a message consumer is still in its onMessage method.
+ * Running in AUTO_ACK mode, the close call ought to wait until the onMessage method completes, and the ack is sent
+ * before closing the connection.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Check that
+ * closing a connection whilst handling a message, blocks till completion of the handler. </table>
+ */
+public class CloseBeforeAckTest extends TestCase
+{
+ private static final Logger log = LoggerFactory.getLogger(CloseBeforeAckTest.class);
+
+ Connection connection;
+ Session session;
+ public static final String TEST_QUEUE_NAME = "TestQueue";
+ private int TEST_COUNT = 25;
+
+ class TestThread1 extends TestRunnable implements MessageListener
+ {
+ public void runWithExceptions() throws Exception
+ {
+ // Set this up to listen for message on the test session.
+ session.createConsumer(session.createQueue(TEST_QUEUE_NAME)).setMessageListener(this);
+ }
+
+ public void onMessage(Message message)
+ {
+ // Give thread 2 permission to close the session.
+ allow(new int[] { 1 });
+
+ // Wait until thread 2 has closed the connection, or is blocked waiting for this to complete.
+ waitFor(new int[] { 1 }, true);
+ }
+ }
+
+ TestThread1 testThread1 = new TestThread1();
+
+ TestRunnable testThread2 =
+ new TestRunnable()
+ {
+ public void runWithExceptions() throws Exception
+ {
+ // Send a message to be picked up by thread 1.
+ session.createProducer(null).send(session.createQueue(TEST_QUEUE_NAME),
+ session.createTextMessage("Hi there thread 1!"));
+
+ // Wait for thread 1 to pick up the message and give permission to continue.
+ waitFor(new int[] { 0 }, false);
+
+ // Close the connection.
+ session.close();
+
+ // Allow thread 1 to continue to completion, if it is erronously still waiting.
+ allow(new int[] { 1 });
+ }
+ };
+
+ public void testCloseBeforeAutoAck_QPID_397() throws Exception
+ {
+ // Create a session in auto acknowledge mode. This problem shows up in auto acknowledge if the client acks
+ // message at the end of the onMessage method, after a close has been sent.
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ ThreadTestCoordinator tt = new ThreadTestCoordinator(2);
+
+ tt.addTestThread(testThread1, 0);
+ tt.addTestThread(testThread2, 1);
+ tt.setDeadlockTimeout(500);
+ tt.run();
+
+ String errorMessage = tt.joinAndRetrieveMessages();
+
+ // Print any error messages or exceptions.
+ log.debug(errorMessage);
+
+ if (!tt.getExceptions().isEmpty())
+ {
+ for (Exception e : tt.getExceptions())
+ {
+ log.debug("Exception thrown during test thread: ", e);
+ }
+ }
+
+ Assert.assertTrue(errorMessage, "".equals(errorMessage));
+ }
+
+ public void closeBeforeAutoAckManyTimes() throws Exception
+ {
+ for (int i = 0; i < TEST_COUNT; i++)
+ {
+ testCloseBeforeAutoAck_QPID_397();
+ }
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+
+ connection = new AMQConnection("vm://:1", "guest", "guest", getName(), "test");
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killVMBroker(1);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java
new file mode 100644
index 0000000000..bf8802c803
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java
@@ -0,0 +1,389 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.close;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.message.AbstractJMSMessage;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.testutil.QpidClientConnection;
+import org.apache.qpid.url.URLSyntaxException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.UUID;
+
+public class MessageRequeueTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(MessageRequeueTest.class);
+
+ protected static AtomicInteger consumerIds = new AtomicInteger(0);
+ protected final Integer numTestMessages = 150;
+
+ protected final int consumeTimeout = 3000;
+
+
+ protected String payload = "Message:";
+
+ protected final String BROKER = "vm://:1";
+ private boolean testReception = true;
+
+ private long[] receieved = new long[numTestMessages + 1];
+ private boolean passed = false;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+
+
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+
+ TransportConnection.killVMBroker(1);
+ }
+
+ /**
+ * multiple consumers
+ *
+ * @throws javax.jms.JMSException if a JMS problem occurs
+ * @throws InterruptedException on timeout
+ */
+ public void testDrain() throws JMSException, InterruptedException
+ {
+ final String queueName = "direct://amq.direct//queue" + UUID.randomUUID().toString();
+
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+ // clear queue
+ conn.consume(queueName, consumeTimeout);
+ // load test data
+ _logger.info("creating test data, " + numTestMessages + " messages");
+ conn.put(queueName, payload, numTestMessages);
+ // close this connection
+ conn.disconnect();
+
+ conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+
+ _logger.info("consuming queue " + queueName);
+ Queue q = conn.getSession().createQueue(queueName);
+
+ final MessageConsumer consumer = conn.getSession().createConsumer(q);
+ int messagesReceived = 0;
+
+ long[] messageLog = new long[numTestMessages + 1];
+
+ _logger.info("consuming...");
+ Message msg = consumer.receive(1000);
+ while (msg != null)
+ {
+ messagesReceived++;
+
+ long dt = ((AbstractJMSMessage) msg).getDeliveryTag();
+
+ int msgindex = msg.getIntProperty("index");
+ if (messageLog[msgindex] != 0)
+ {
+ _logger.error("Received Message(" + msgindex + ":" + ((AbstractJMSMessage) msg).getDeliveryTag()
+ + ") more than once.");
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Received Message(" + System.identityHashCode(msgindex) + ") " + "DT:" + dt + "IN:" + msgindex);
+ }
+
+ if (dt == 0)
+ {
+ _logger.error("DT is zero for msg:" + msgindex);
+ }
+
+ messageLog[msgindex] = dt;
+
+ // get Next message
+ msg = consumer.receive(1000);
+ }
+
+ conn.getSession().commit();
+ consumer.close();
+ assertEquals("number of consumed messages does not match initial data", (int) numTestMessages, messagesReceived);
+
+ int index = 0;
+ StringBuilder list = new StringBuilder();
+ list.append("Failed to receive:");
+ int failed = 0;
+
+ for (long b : messageLog)
+ {
+ if ((b == 0) && (index != 0)) // delivery tag of zero shouldn't exist
+ {
+ _logger.error("Index: " + index + " was not received.");
+ list.append(" ");
+ list.append(index);
+ list.append(":");
+ list.append(b);
+ failed++;
+ }
+
+ index++;
+ }
+
+ assertEquals(list.toString(), 0, failed);
+ _logger.info("consumed: " + messagesReceived);
+ conn.disconnect();
+ passed = true;
+
+ }
+
+
+
+ /** multiple consumers
+ * Based on code subbmitted by client FT-304
+ */
+ public void testCompetingConsumers() throws JMSException, InterruptedException
+ {
+ final String queueName = "direct://amq.direct//queue" + UUID.randomUUID().toString();
+
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+ // clear queue
+ conn.consume(queueName, consumeTimeout);
+ // load test data
+ _logger.info("creating test data, " + numTestMessages + " messages");
+ conn.put(queueName, payload, numTestMessages);
+ // close this connection
+ conn.disconnect();
+
+ Consumer c1 = new Consumer(queueName);
+ Consumer c2 = new Consumer(queueName);
+ Consumer c3 = new Consumer(queueName);
+ Consumer c4 = new Consumer(queueName);
+
+ Thread t1 = new Thread(c1);
+ Thread t2 = new Thread(c2);
+ Thread t3 = new Thread(c3);
+ Thread t4 = new Thread(c4);
+
+ t1.start();
+ t2.start();
+ t3.start();
+ t4.start();
+
+ try
+ {
+ t1.join();
+ t2.join();
+ t3.join();
+ t4.join();
+ }
+ catch (InterruptedException e)
+ {
+ fail("Uanble to join to Consumer theads");
+ }
+
+ _logger.info("consumer 1 count is " + c1.getCount());
+ _logger.info("consumer 2 count is " + c2.getCount());
+ _logger.info("consumer 3 count is " + c3.getCount());
+ _logger.info("consumer 4 count is " + c4.getCount());
+
+ Integer totalConsumed = c1.getCount() + c2.getCount() + c3.getCount() + c4.getCount();
+
+ // Check all messages were correctly delivered
+ int index = 0;
+ StringBuilder list = new StringBuilder();
+ list.append("Failed to receive:");
+ int failed = 0;
+
+ for (long b : receieved)
+ {
+ if ((b == 0) && (index != 0)) // delivery tag of zero shouldn't exist (and we don't have msg 0)
+ {
+ fail("Index: " + index + " was not received.");
+ list.append(" ");
+ list.append(index);
+ list.append(":");
+ list.append(b);
+ failed++;
+ }
+
+ index++;
+ }
+
+ assertEquals(list.toString() + "-" + numTestMessages + "-" + totalConsumed, 0, failed);
+ assertTrue("number of consumed messages does not match initial data: " + totalConsumed, numTestMessages <= totalConsumed);
+
+ }
+
+ class Consumer implements Runnable
+ {
+ private Integer count = 0;
+ private Integer id;
+ private final String _queueName;
+
+ public Consumer(String queueName)
+ {
+ _queueName = queueName;
+ id = consumerIds.addAndGet(1);
+ }
+
+ public void run()
+ {
+ try
+ {
+ _logger.info("consumer-" + id + ": starting");
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+
+ _logger.info("consumer-" + id + ": connected, consuming...");
+ Message result;
+ do
+ {
+ result = conn.getNextMessage(_queueName, consumeTimeout);
+ if (result != null)
+ {
+
+ long dt = ((AbstractJMSMessage) result).getDeliveryTag();
+
+ if (testReception)
+ {
+ int msgindex = result.getIntProperty("index");
+ if (receieved[msgindex] != 0)
+ {
+ _logger.error("Received Message(" + msgindex + ":"
+ + ((AbstractJMSMessage) result).getDeliveryTag() + ") more than once.");
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Received Message(" + System.identityHashCode(msgindex) + ") " + "DT:" + dt
+ + "IN:" + msgindex);
+ }
+
+ if (dt == 0)
+ {
+ _logger.error("DT is zero for msg:" + msgindex);
+ }
+
+ receieved[msgindex] = dt;
+ }
+
+ count++;
+ if ((count % 100) == 0)
+ {
+ _logger.info("consumer-" + id + ": got " + result + ", new count is " + count);
+ }
+ }
+ }
+ while (result != null);
+
+ _logger.info("consumer-" + id + ": complete");
+ conn.disconnect();
+
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public Integer getCount()
+ {
+ return count;
+ }
+
+ public Integer getId()
+ {
+ return id;
+ }
+ }
+
+ public void testRequeue() throws JMSException, AMQException, URLSyntaxException, InterruptedException
+ {
+ final String queue = "direct://amq.direct//queue" + UUID.randomUUID().toString();
+
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
+
+ conn.connect();
+ // clear queue
+ conn.consume(queue, consumeTimeout);
+ // load test data
+ _logger.info("creating test data, " + numTestMessages + " messages");
+ conn.put(queue, payload, numTestMessages);
+ // close this connection
+ conn.disconnect();
+
+ int run = 0;
+ // while (run < 10)
+ {
+ run++;
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("testRequeue run " + run);
+ }
+
+ String virtualHost = "/test";
+ String brokerlist = BROKER;
+ String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'";
+
+ AMQConnection amqConn = new AMQConnection(brokerUrl);
+ Session session = amqConn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue q = session.createQueue(queue);
+
+ _logger.debug("Create Consumer");
+ MessageConsumer consumer = session.createConsumer(q);
+
+ amqConn.start();
+
+ _logger.debug("Receiving msg");
+ Message msg = consumer.receive(2000);
+
+ assertNotNull("Message should not be null", msg);
+
+ // As we have not ack'd message will be requeued.
+ _logger.debug("Close Consumer");
+ consumer.close();
+
+ _logger.debug("Close Connection");
+ amqConn.close();
+ }
+ }
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java
new file mode 100644
index 0000000000..5e2703d5a5
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */package org.apache.qpid.test.unit.close;
+
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class TopicPublisherCloseTest extends TestCase
+{
+
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testAllMethodsThrowAfterConnectionClose() throws Exception
+ {
+ AMQConnection connection = new AMQConnection(_connectionString, "guest", "guest", "Client", "test");
+
+ Topic destination1 = new AMQTopic(connection, "t1");
+ TopicSession session1 = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TopicPublisher pub = session1.createPublisher(destination1);
+ connection.close();
+ try
+ {
+ pub.getDeliveryMode();
+ fail("Expected exception not thrown");
+ }
+ catch (javax.jms.IllegalStateException e)
+ {
+ // PASS
+ }
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java
new file mode 100644
index 0000000000..a1d9af558c
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.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.test.unit.message;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQShortString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class JMSDestinationTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(JMSDestinationTest.class);
+
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testJMSDestination() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false,
+ true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+
+ Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+
+ TextMessage sentMsg = producerSession.createTextMessage("hello");
+ assertNull(sentMsg.getJMSDestination());
+
+ producer.send(sentMsg);
+
+ assertEquals(sentMsg.getJMSDestination(), queue);
+
+ con2.close();
+
+ con.start();
+
+ TextMessage rm = (TextMessage) consumer.receive();
+ assertNotNull(rm);
+
+ assertEquals(rm.getJMSDestination(), queue);
+ con.close();
+ }
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
new file mode 100644
index 0000000000..3012909daa
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java
@@ -0,0 +1,108 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.message;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.message.NonQpidObjectMessage;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQShortString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Destination;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class JMSPropertiesTest extends TestCase
+{
+
+ private static final Logger _logger = LoggerFactory.getLogger(JMSPropertiesTest.class);
+
+ public String _connectionString = "vm://:1";
+
+ public static final String JMS_CORR_ID = "QPIDID_01";
+ public static final int JMS_DELIV_MODE = 1;
+ public static final String JMS_TYPE = "test.jms.type";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testJMSProperties() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue =
+ new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false,
+ true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+
+ AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+ Destination JMS_REPLY_TO = new AMQQueue(con2, "my.replyto");
+ // create a test message to send
+ ObjectMessage sentMsg = new NonQpidObjectMessage();
+ sentMsg.setJMSCorrelationID(JMS_CORR_ID);
+ sentMsg.setJMSDeliveryMode(JMS_DELIV_MODE);
+ sentMsg.setJMSType(JMS_TYPE);
+ sentMsg.setJMSReplyTo(JMS_REPLY_TO);
+
+ // send it
+ producer.send(sentMsg);
+
+ con2.close();
+
+ con.start();
+
+ // get message and check JMS properties
+ ObjectMessage rm = (ObjectMessage) consumer.receive();
+ assertNotNull(rm);
+
+ assertEquals("JMS Correlation ID mismatch", sentMsg.getJMSCorrelationID(), rm.getJMSCorrelationID());
+ // TODO: Commented out as always overwritten by send delivery mode value - prob should not set in conversion
+ // 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());
+
+ con.close();
+ }
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java
new file mode 100644
index 0000000000..fd425b9930
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java
@@ -0,0 +1,138 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.message;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.TextMessage;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.message.AbstractJMSMessage;
+import org.apache.qpid.client.message.JMSMapMessage;
+import org.apache.qpid.client.message.JMSTextMessage;
+import org.apache.qpid.client.message.MessageConverter;
+import org.apache.qpid.exchange.ExchangeDefaults;
+
+
+public class MessageConverterTest extends TestCase
+{
+
+ public static final String JMS_CORR_ID = "QPIDID_01";
+ public static final int JMS_DELIV_MODE = 1;
+ public static final String JMS_TYPE = "test.jms.type";
+ public static final Destination JMS_REPLY_TO = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME,"my.replyto");
+
+ protected JMSTextMessage testTextMessage;
+
+ protected JMSMapMessage testMapMessage;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ testTextMessage = new JMSTextMessage();
+
+ //Set Message Text
+ testTextMessage.setText("testTextMessage text");
+ setMessageProperties(testTextMessage);
+
+ testMapMessage = new JMSMapMessage();
+ testMapMessage.setString("testMapString", "testMapStringValue");
+ testMapMessage.setDouble("testMapDouble", Double.MAX_VALUE);
+ }
+
+ public void testSetProperties() throws Exception
+ {
+ AbstractJMSMessage newMessage = new MessageConverter((TextMessage) testTextMessage).getConvertedMessage();
+ mesagePropertiesTest(testTextMessage, newMessage);
+ }
+
+ public void testJMSTextMessageConversion() throws Exception
+ {
+ AbstractJMSMessage newMessage = new MessageConverter((TextMessage) testTextMessage).getConvertedMessage();
+ assertEquals("Converted message text mismatch", ((JMSTextMessage) newMessage).getText(), testTextMessage.getText());
+ }
+
+ public void testJMSMapMessageConversion() throws Exception
+ {
+ AbstractJMSMessage newMessage = new MessageConverter((MapMessage) testMapMessage).getConvertedMessage();
+ assertEquals("Converted map message String mismatch", ((JMSMapMessage) newMessage).getString("testMapString"),
+ testMapMessage.getString("testMapString"));
+ assertEquals("Converted map message Double mismatch", ((JMSMapMessage) newMessage).getDouble("testMapDouble"),
+ testMapMessage.getDouble("testMapDouble"));
+
+ }
+
+ public void testMessageConversion() throws Exception
+ {
+ Message newMessage = new NonQpidMessage();
+ setMessageProperties(newMessage);
+ mesagePropertiesTest(testTextMessage, newMessage);
+ }
+
+ private void setMessageProperties(Message message) throws JMSException
+ {
+ message.setJMSCorrelationID(JMS_CORR_ID);
+ message.setJMSDeliveryMode(JMS_DELIV_MODE);
+ message.setJMSType(JMS_TYPE);
+ message.setJMSReplyTo(JMS_REPLY_TO);
+
+ //Add non-JMS properties
+ message.setStringProperty("testProp1", "testValue1");
+ message.setDoubleProperty("testProp2", Double.MIN_VALUE);
+ }
+
+
+ private void mesagePropertiesTest(Message expectedMessage, Message actualMessage)
+ {
+ try
+ {
+ //check JMS prop values on newMessage match
+ assertEquals("JMS Correlation ID mismatch", expectedMessage.getJMSCorrelationID(), actualMessage.getJMSCorrelationID());
+ assertEquals("JMS Delivery mode mismatch", expectedMessage.getJMSDeliveryMode(), actualMessage.getJMSDeliveryMode());
+ assertEquals("JMS Type mismatch", expectedMessage.getJMSType(), actualMessage.getJMSType());
+ assertEquals("JMS Reply To mismatch", expectedMessage.getJMSReplyTo(), actualMessage.getJMSReplyTo());
+
+ //check non-JMS standard props ok too
+ assertEquals("Test String prop value mismatch", expectedMessage.getStringProperty("testProp1"),
+ actualMessage.getStringProperty("testProp1"));
+
+ assertEquals("Test Double prop value mismatch", expectedMessage.getDoubleProperty("testProp2"),
+ actualMessage.getDoubleProperty("testProp2"));
+ }
+ catch (JMSException e)
+ {
+ fail("An error occured testing the property values" + e.getCause());
+ e.printStackTrace();
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ testTextMessage = null;
+ }
+
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java
new file mode 100644
index 0000000000..df53c796b2
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java
@@ -0,0 +1,411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.test.unit.message;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+public class NonQpidMessage implements Message
+{
+ private String _JMSMessageID;
+ private long _JMSTimestamp;
+ private byte[] _JMSCorrelationIDAsBytes;
+ private String _JMSCorrelationID;
+ private Destination _JMSReplyTo;
+ private Destination _JMSDestination;
+ private int _JMSDeliveryMode;
+ private boolean _JMSRedelivered;
+ private String _JMSType;
+ private long _JMSExpiration;
+ private int _JMSPriority;
+ private Hashtable _properties;
+
+ public NonQpidMessage()
+ {
+ _properties = new Hashtable();
+ _JMSPriority = javax.jms.Message.DEFAULT_PRIORITY;
+ _JMSDeliveryMode = javax.jms.Message.DEFAULT_DELIVERY_MODE;
+ }
+
+ public String getJMSMessageID() throws JMSException
+ {
+ return _JMSMessageID;
+ }
+
+ public void setJMSMessageID(String string) throws JMSException
+ {
+ _JMSMessageID = string;
+ }
+
+ public long getJMSTimestamp() throws JMSException
+ {
+ return _JMSTimestamp;
+ }
+
+ public void setJMSTimestamp(long l) throws JMSException
+ {
+ _JMSTimestamp = l;
+ }
+
+ public byte[] getJMSCorrelationIDAsBytes() throws JMSException
+ {
+ return _JMSCorrelationIDAsBytes;
+ }
+
+ public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException
+ {
+ _JMSCorrelationIDAsBytes = bytes;
+ }
+
+ public void setJMSCorrelationID(String string) throws JMSException
+ {
+ _JMSCorrelationID = string;
+ }
+
+ public String getJMSCorrelationID() throws JMSException
+ {
+ return _JMSCorrelationID;
+ }
+
+ public Destination getJMSReplyTo() throws JMSException
+ {
+ return _JMSReplyTo;
+ }
+
+ public void setJMSReplyTo(Destination destination) throws JMSException
+ {
+ _JMSReplyTo = destination;
+ }
+
+ public Destination getJMSDestination() throws JMSException
+ {
+ return _JMSDestination;
+ }
+
+ public void setJMSDestination(Destination destination) throws JMSException
+ {
+ _JMSDestination = destination;
+ }
+
+ public int getJMSDeliveryMode() throws JMSException
+ {
+ return _JMSDeliveryMode;
+ }
+
+ public void setJMSDeliveryMode(int i) throws JMSException
+ {
+ _JMSDeliveryMode = i;
+ }
+
+ public boolean getJMSRedelivered() throws JMSException
+ {
+ return _JMSRedelivered;
+ }
+
+ public void setJMSRedelivered(boolean b) throws JMSException
+ {
+ _JMSRedelivered = b;
+ }
+
+ public String getJMSType() throws JMSException
+ {
+ return _JMSType;
+ }
+
+ public void setJMSType(String string) throws JMSException
+ {
+ _JMSType = string;
+ }
+
+ public long getJMSExpiration() throws JMSException
+ {
+ return _JMSExpiration;
+ }
+
+ public void setJMSExpiration(long l) throws JMSException
+ {
+ _JMSExpiration = l;
+ }
+
+ public int getJMSPriority() throws JMSException
+ {
+ return _JMSPriority;
+ }
+
+ public void setJMSPriority(int i) throws JMSException
+ {
+ _JMSPriority = i;
+ }
+
+ public void clearProperties() throws JMSException
+ {
+ _properties.clear();
+ }
+
+ public boolean propertyExists(String string) throws JMSException
+ {
+ return _properties.containsKey(string);
+ }
+
+ public boolean getBooleanProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Boolean)
+ {
+ return (Boolean) o;
+ }
+ else
+ {
+ return Boolean.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public byte getByteProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Byte)
+ {
+ return (Byte) o;
+ }
+ else
+ {
+ return Byte.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public short getShortProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Short)
+ {
+ return (Short) o;
+ }
+ else
+ {
+ return Short.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public int getIntProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Integer)
+ {
+ return (Integer) o;
+ }
+ else
+ {
+ return Integer.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public long getLongProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Long)
+ {
+ return (Long) o;
+ }
+ else
+ {
+ return Long.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public float getFloatProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Float)
+ {
+ return (Float) o;
+ }
+ else
+ {
+ return Float.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public double getDoubleProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Double)
+ {
+ return (Double) o;
+ }
+ else
+ {
+ return Double.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public String getStringProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof String)
+ {
+ return (String) o;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public Object getObjectProperty(String string) throws JMSException
+ {
+ if (propertyExists(string))
+ {
+ Object o = _properties.get(string);
+ if (o instanceof Boolean)
+ {
+ return (Boolean) o;
+ }
+ else
+ {
+ return Boolean.valueOf(null);
+ }
+ }
+ else
+ {
+ throw new JMSException("property does not exist: " + string);
+ }
+ }
+
+ public Enumeration getPropertyNames() throws JMSException
+ {
+ return _properties.keys();
+ }
+
+ public void setBooleanProperty(String string, boolean b) throws JMSException
+ {
+ _properties.put(string, b);
+ }
+
+ public void setByteProperty(String string, byte b) throws JMSException
+ {
+ _properties.put(string, b);
+ }
+
+ public void setShortProperty(String string, short i) throws JMSException
+ {
+ _properties.put(string, i);
+ }
+
+ public void setIntProperty(String string, int i) throws JMSException
+ {
+ _properties.put(string, i);
+ }
+
+ public void setLongProperty(String string, long l) throws JMSException
+ {
+ _properties.put(string, l);
+ }
+
+ public void setFloatProperty(String string, float v) throws JMSException
+ {
+ _properties.put(string, v);
+ }
+
+ public void setDoubleProperty(String string, double v) throws JMSException
+ {
+ _properties.put(string, v);
+ }
+
+ public void setStringProperty(String string, String string1) throws JMSException
+ {
+ _properties.put(string, string1);
+ }
+
+ public void setObjectProperty(String string, Object object) throws JMSException
+ {
+ _properties.put(string, object);
+ }
+
+ public void acknowledge() throws JMSException
+ {
+
+ }
+
+ public void clearBody() throws JMSException
+ {
+
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java
new file mode 100644
index 0000000000..9c4f2af107
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java
@@ -0,0 +1,160 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.message;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQHeadersExchange;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.BindingURL;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageEOFException;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.StreamMessage;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class StreamMessageTest extends TestCase
+{
+
+ private static final Logger _logger = LoggerFactory.getLogger(StreamMessageTest.class);
+
+ public String _connectionString = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testStreamMessageEOF() throws Exception
+ {
+ Connection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ AMQHeadersExchange queue =
+ new AMQHeadersExchange(new AMQBindingURL(
+ ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME
+ + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'"));
+ FieldTable ft = new FieldTable();
+ ft.setString("F1000", "1");
+ MessageConsumer consumer =
+ consumerSession.createConsumer(queue, AMQSession.DEFAULT_PREFETCH_LOW_MARK,
+ AMQSession.DEFAULT_PREFETCH_HIGH_MARK, false, false, (String) null, ft);
+
+ // force synch to ensure the consumer has resulted in a bound queue
+ // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
+ // This is the default now
+
+ Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+
+ AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ // Need to start the "producer" connection in order to receive bounced messages
+ _logger.info("Starting producer connection");
+ con2.start();
+
+ MessageProducer mandatoryProducer = producerSession.createProducer(queue);
+
+ // Third test - should be routed
+ _logger.info("Sending isBound message");
+ StreamMessage msg = producerSession.createStreamMessage();
+
+ msg.setStringProperty("F1000", "1");
+
+ msg.writeByte((byte) 42);
+
+ mandatoryProducer.send(msg);
+
+ _logger.info("Starting consumer connection");
+ con.start();
+
+ StreamMessage msg2 = (StreamMessage) consumer.receive();
+
+ msg2.readByte();
+ try
+ {
+ msg2.readByte();
+ }
+ catch (Exception e)
+ {
+ assertTrue("Expected MessageEOFException: " + e, e instanceof MessageEOFException);
+ }
+ }
+
+ public void testModifyReceivedMessageExpandsBuffer() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ AMQQueue queue = new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("testQ"));
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ consumer.setMessageListener(new MessageListener()
+ {
+
+ public void onMessage(Message message)
+ {
+ StreamMessage sm = (StreamMessage) message;
+ try
+ {
+ sm.clearBody();
+ sm.writeString("dfgjshfslfjshflsjfdlsjfhdsljkfhdsljkfhsd");
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Error when writing large string to received msg: " + e, e);
+ fail("Error when writing large string to received msg" + e);
+ }
+ }
+ });
+
+ Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+ AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer mandatoryProducer = producerSession.createProducer(queue);
+ con.start();
+ StreamMessage sm = producerSession.createStreamMessage();
+ sm.writeInt(42);
+ mandatoryProducer.send(sm);
+ Thread.sleep(2000);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
new file mode 100644
index 0000000000..a0a8eb10ed
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
@@ -0,0 +1,192 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.topic;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.url.URLSyntaxException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.jms.TopicSubscriber;
+
+public class DurableSubscriptionTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class);
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testUnsubscribe() throws AMQException, JMSException, URLSyntaxException
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con, "MyTopic");
+ _logger.info("Create Session 1");
+ Session session1 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _logger.info("Create Consumer on Session 1");
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+ _logger.info("Create Producer on Session 1");
+ MessageProducer producer = session1.createProducer(topic);
+
+ _logger.info("Create Session 2");
+ Session session2 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _logger.info("Create Durable Subscriber on Session 2");
+ TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription");
+
+ _logger.info("Starting connection");
+ con.start();
+
+ _logger.info("Producer sending message A");
+ producer.send(session1.createTextMessage("A"));
+
+ Message msg;
+ _logger.info("Receive message on consumer 1:expecting A");
+ msg = consumer1.receive();
+ assertEquals("A", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(1000);
+ assertEquals(null, msg);
+
+ _logger.info("Receive message on consumer 1:expecting A");
+ msg = consumer2.receive();
+ assertEquals("A", ((TextMessage) msg).getText());
+ msg = consumer2.receive(1000);
+ _logger.info("Receive message on consumer 1 :expecting null");
+ assertEquals(null, msg);
+
+ _logger.info("Unsubscribe session2/consumer2");
+ session2.unsubscribe("MySubscription");
+
+ _logger.info("Producer sending message B");
+ producer.send(session1.createTextMessage("B"));
+
+ _logger.info("Receive message on consumer 1 :expecting B");
+ msg = consumer1.receive();
+ assertEquals("B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(1000);
+ assertEquals(null, msg);
+
+ _logger.info("Receive message on consumer 2 :expecting null");
+ msg = consumer2.receive(1000);
+ assertEquals(null, msg);
+
+ _logger.info("Close connection");
+ con.close();
+ }
+
+ public void testDurabilityNOACK() throws AMQException, JMSException, URLSyntaxException
+ {
+ durabilityImpl(AMQSession.NO_ACKNOWLEDGE);
+ }
+
+ public void testDurabilityAUTOACK() throws AMQException, JMSException, URLSyntaxException
+ {
+ durabilityImpl(Session.AUTO_ACKNOWLEDGE);
+ }
+
+ private void durabilityImpl(int ackMode) throws AMQException, JMSException, URLSyntaxException
+ {
+
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con, "MyTopic");
+ Session session1 = con.createSession(false, ackMode);
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+
+ Session sessionProd = con.createSession(false, ackMode);
+ MessageProducer producer = sessionProd.createProducer(topic);
+
+ Session session2 = con.createSession(false, ackMode);
+ TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription");
+
+ con.start();
+
+ producer.send(session1.createTextMessage("A"));
+
+ Message msg;
+ msg = consumer1.receive(500);
+ assertNotNull("Message should be available", msg);
+ assertEquals("Message Text doesn't match", "A", ((TextMessage) msg).getText());
+
+ msg = consumer1.receive(500);
+ assertNull("There should be no more messages for consumption on consumer1.", msg);
+
+ msg = consumer2.receive();
+ assertNotNull(msg);
+ assertEquals("Consumer 2 should also received the first msg.", "A", ((TextMessage) msg).getText());
+ msg = consumer2.receive(500);
+ assertNull("There should be no more messages for consumption on consumer2.", msg);
+
+ consumer2.close();
+
+ Session session3 = con.createSession(false, ackMode);
+ MessageConsumer consumer3 = session3.createDurableSubscriber(topic, "MySubscription");
+
+ producer.send(session1.createTextMessage("B"));
+
+ _logger.info("Receive message on consumer 1 :expecting B");
+ msg = consumer1.receive(500);
+ assertNotNull("Consumer 1 should get message 'B'.", msg);
+ assertEquals("Incorrect Message recevied on consumer1.", "B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 1 :expecting null");
+ msg = consumer1.receive(500);
+ assertNull("There should be no more messages for consumption on consumer1.", msg);
+
+ _logger.info("Receive message on consumer 3 :expecting B");
+ msg = consumer3.receive(500);
+ assertNotNull("Consumer 3 should get message 'B'.", msg);
+ assertEquals("Incorrect Message recevied on consumer4.", "B", ((TextMessage) msg).getText());
+ _logger.info("Receive message on consumer 3 :expecting null");
+ msg = consumer3.receive(500);
+ assertNull("There should be no more messages for consumption on consumer3.", msg);
+
+ consumer1.close();
+ consumer3.close();
+
+ con.close();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(DurableSubscriptionTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java
new file mode 100644
index 0000000000..929e2799a9
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.topic;
+
+import javax.jms.MessageConsumer;
+import javax.jms.TextMessage;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+
+/**
+ * @author Apache Software Foundation
+ */
+public class TopicPublisherTest extends TestCase
+{
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ public void testUnidentifiedProducer() throws Exception
+ {
+
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con,"MyTopic");
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicPublisher publisher = session1.createPublisher(null);
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+ con.start();
+ publisher.publish(topic, session1.createTextMessage("Hello"));
+ TextMessage m = (TextMessage) consumer1.receive(2000);
+ assertNotNull(m);
+ try
+ {
+ publisher.publish(session1.createTextMessage("Goodbye"));
+ fail("Did not throw UnsupportedOperationException");
+ }
+ catch (UnsupportedOperationException e)
+ {
+ // PASS
+ }
+
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TopicPublisherTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
new file mode 100644
index 0000000000..065b06a87d
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.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.test.unit.topic;
+
+import javax.jms.InvalidDestinationException;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.Session;
+import javax.jms.TemporaryTopic;
+import javax.jms.TextMessage;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.client.transport.TransportConnection;
+
+
+/** @author Apache Software Foundation */
+public class TopicSessionTest extends TestCase
+{
+ private static final String BROKER = "vm://:1";
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+
+ public void testTopicSubscriptionUnsubscription() throws Exception
+ {
+
+ AMQConnection con = new AMQConnection(BROKER+"?retries='0'", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con.getDefaultTopicExchangeName(), "MyTopic");
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0");
+ TopicPublisher publisher = session1.createPublisher(topic);
+
+ con.start();
+
+ TextMessage tm = session1.createTextMessage("Hello");
+ publisher.publish(tm);
+
+ tm = (TextMessage) sub.receive(2000);
+ assertNotNull(tm);
+
+ session1.unsubscribe("subscription0");
+
+ try
+ {
+ session1.unsubscribe("not a subscription");
+ fail("expected InvalidDestinationException when unsubscribing from unknown subscription");
+ }
+ catch (InvalidDestinationException e)
+ {
+ ; // PASS
+ }
+ catch (Exception e)
+ {
+ fail("expected InvalidDestinationException when unsubscribing from unknown subscription, got: " + e);
+ }
+
+ con.close();
+ }
+
+ public void testSubscriptionNameReuseForDifferentTopicSingleConnection() throws Exception
+ {
+ subscriptionNameReuseForDifferentTopic(false);
+ }
+
+ public void testSubscriptionNameReuseForDifferentTopicTwoConnections() throws Exception
+ {
+ subscriptionNameReuseForDifferentTopic(true);
+ }
+
+ private void subscriptionNameReuseForDifferentTopic(boolean shutdown) throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con, "MyTopic1" + String.valueOf(shutdown));
+ AMQTopic topic2 = new AMQTopic(con, "MyOtherTopic1" + String.valueOf(shutdown));
+
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0");
+ TopicPublisher publisher = session1.createPublisher(null);
+
+ con.start();
+
+ publisher.publish(topic, session1.createTextMessage("hello"));
+ TextMessage m = (TextMessage) sub.receive(2000);
+ assertNotNull(m);
+
+ if (shutdown)
+ {
+ session1.close();
+ con.close();
+ con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test");
+ con.start();
+ session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ publisher = session1.createPublisher(null);
+ }
+ TopicSubscriber sub2 = session1.createDurableSubscriber(topic2, "subscription0");
+ publisher.publish(topic, session1.createTextMessage("hello"));
+ if (!shutdown)
+ {
+ m = (TextMessage) sub.receive(2000);
+ assertNull(m);
+ }
+ publisher.publish(topic2, session1.createTextMessage("goodbye"));
+ m = (TextMessage) sub2.receive(2000);
+ assertNotNull(m);
+ assertEquals("goodbye", m.getText());
+ con.close();
+ }
+
+ public void testUnsubscriptionAfterConnectionClose() throws Exception
+ {
+ AMQConnection con1 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con1, "MyTopic3");
+
+ TopicSession session1 = con1.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicPublisher publisher = session1.createPublisher(topic);
+
+ AMQConnection con2 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test2", "test");
+ TopicSession session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSubscriber sub = session2.createDurableSubscriber(topic, "subscription0");
+
+ con2.start();
+
+ publisher.publish(session1.createTextMessage("Hello"));
+ TextMessage tm = (TextMessage) sub.receive(2000);
+ assertNotNull(tm);
+ con2.close();
+ publisher.publish(session1.createTextMessage("Hello2"));
+ con2 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test2", "test");
+ session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ sub = session2.createDurableSubscriber(topic, "subscription0");
+ con2.start();
+ tm = (TextMessage) sub.receive(2000);
+ assertNotNull(tm);
+ assertEquals("Hello2", tm.getText());
+ con1.close();
+ con2.close();
+ }
+
+ public void testTextMessageCreation() throws Exception
+ {
+
+ AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con, "MyTopic4");
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicPublisher publisher = session1.createPublisher(topic);
+ MessageConsumer consumer1 = session1.createConsumer(topic);
+ con.start();
+ TextMessage tm = session1.createTextMessage("Hello");
+ publisher.publish(tm);
+ tm = (TextMessage) consumer1.receive(200000L);
+ assertNotNull(tm);
+ String msgText = tm.getText();
+ assertEquals("Hello", msgText);
+ tm = session1.createTextMessage();
+ msgText = tm.getText();
+ assertNull(msgText);
+ publisher.publish(tm);
+ tm = (TextMessage) consumer1.receive(20000000L);
+ assertNotNull(tm);
+ msgText = tm.getText();
+ assertNull(msgText);
+ tm.clearBody();
+ tm.setText("Now we are not null");
+ publisher.publish(tm);
+ tm = (TextMessage) consumer1.receive(2000);
+ assertNotNull(tm);
+ msgText = tm.getText();
+ assertEquals("Now we are not null", msgText);
+
+ tm = session1.createTextMessage("");
+ msgText = tm.getText();
+ assertEquals("Empty string not returned", "", msgText);
+ publisher.publish(tm);
+ tm = (TextMessage) consumer1.receive(2000);
+ assertNotNull(tm);
+ assertEquals("Empty string not returned", "", msgText);
+ con.close();
+ }
+
+ public void testSendingSameMessage() throws Exception
+ {
+ AMQConnection conn = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test");
+ TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TemporaryTopic topic = session.createTemporaryTopic();
+ assertNotNull(topic);
+ TopicPublisher producer = session.createPublisher(topic);
+ MessageConsumer consumer = session.createConsumer(topic);
+ conn.start();
+ TextMessage sentMessage = session.createTextMessage("Test Message");
+ producer.send(sentMessage);
+ TextMessage receivedMessage = (TextMessage) consumer.receive(2000);
+ assertNotNull(receivedMessage);
+ assertEquals(sentMessage.getText(), receivedMessage.getText());
+ producer.send(sentMessage);
+ receivedMessage = (TextMessage) consumer.receive(2000);
+ assertNotNull(receivedMessage);
+ assertEquals(sentMessage.getText(), receivedMessage.getText());
+
+ conn.close();
+
+ }
+
+ public void testTemporaryTopic() throws Exception
+ {
+ AMQConnection conn = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test");
+ TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ TemporaryTopic topic = session.createTemporaryTopic();
+ assertNotNull(topic);
+ TopicPublisher producer = session.createPublisher(topic);
+ MessageConsumer consumer = session.createConsumer(topic);
+ conn.start();
+ producer.send(session.createTextMessage("hello"));
+ TextMessage tm = (TextMessage) consumer.receive(2000);
+ assertNotNull(tm);
+ assertEquals("hello", tm.getText());
+
+ try
+ {
+ topic.delete();
+ fail("Expected JMSException : should not be able to delete while there are active consumers");
+ }
+ catch (JMSException je)
+ {
+ ; //pass
+ }
+
+ consumer.close();
+
+ try
+ {
+ topic.delete();
+ }
+ catch (JMSException je)
+ {
+ fail("Unexpected Exception: " + je.getMessage());
+ }
+
+ TopicSession session2 = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ try
+ {
+ session2.createConsumer(topic);
+ fail("Expected a JMSException when subscribing to a temporary topic created on adifferent session");
+ }
+ catch (JMSException je)
+ {
+ ; // pass
+ }
+
+
+ conn.close();
+ }
+
+
+ public void testNoLocal() throws Exception
+ {
+
+ AMQConnection con = new AMQConnection(BROKER + "?retries='0'", "guest", "guest", "test", "test");
+
+ AMQTopic topic = new AMQTopic(con, "testNoLocal");
+
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSubscriber noLocal = session1.createDurableSubscriber(topic, "noLocal", "", true);
+ TopicSubscriber select = session1.createDurableSubscriber(topic, "select", "Selector = 'select'", false);
+ TopicSubscriber normal = session1.createDurableSubscriber(topic, "normal");
+
+ TopicPublisher publisher = session1.createPublisher(topic);
+
+ con.start();
+ TextMessage m;
+ TextMessage message;
+
+ //send message to all consumers
+ publisher.publish(session1.createTextMessage("hello-new2"));
+
+ //test normal subscriber gets message
+ m = (TextMessage) normal.receive(5000);
+ assertNotNull(m);
+
+ //test selector subscriber doesn't message
+ m = (TextMessage) select.receive(2000);
+ assertNull(m);
+
+ //test nolocal subscriber doesn't message
+ m = (TextMessage) noLocal.receive(2000);
+ if (m != null)
+ {
+ System.out.println("Message:" + m.getText());
+ }
+ assertNull(m);
+
+ //send message to all consumers
+ message = session1.createTextMessage("hello2");
+ message.setStringProperty("Selector", "select");
+
+ publisher.publish(message);
+
+ //test normal subscriber gets message
+ m = (TextMessage) normal.receive(5000);
+ assertNotNull(m);
+
+ //test selector subscriber does get message
+ m = (TextMessage) select.receive(2000);
+ assertNotNull(m);
+
+ //test nolocal subscriber doesn't message
+ m = (TextMessage) noLocal.receive(1000);
+ assertNull(m);
+
+ AMQConnection con2 = new AMQConnection(BROKER + "?retries='0'", "guest", "guest", "test2", "test");
+ TopicSession session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicPublisher publisher2 = session2.createPublisher(topic);
+
+
+ message = session2.createTextMessage("hello2");
+ message.setStringProperty("Selector", "select");
+
+ publisher2.publish(message);
+
+ //test normal subscriber gets message
+ m = (TextMessage) normal.receive(2000);
+ assertNotNull(m);
+
+ //test selector subscriber does get message
+ m = (TextMessage) select.receive(2000);
+ assertNotNull(m);
+
+ //test nolocal subscriber does message
+ m = (TextMessage) noLocal.receive(2000);
+ assertNotNull(m);
+
+
+ con.close();
+ con2.close();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TopicSessionTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java
new file mode 100644
index 0000000000..224463a446
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.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.test.unit.transacted;
+
+import junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.url.URLSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+/**
+ * This class tests a number of commits and roll back scenarios
+ *
+ * Assumptions; - Assumes empty Queue
+ */
+public class CommitRollbackTest extends TestCase
+{
+ protected AMQConnection conn;
+ protected String queue = "direct://amq.direct//Qpid.Client.Transacted.CommitRollback.queue";
+ protected static int testMethod = 0;
+ protected String payload = "xyzzy";
+ private Session _session;
+ private MessageProducer _publisher;
+ private Session _pubSession;
+ private MessageConsumer _consumer;
+ Queue _jmsQueue;
+
+ private static final Logger _logger = LoggerFactory.getLogger(CommitRollbackTest.class);
+ private static final String BROKER = "vm://:1";
+ private boolean _gotone = false;
+ private boolean _gottwo = false;
+ private boolean _gottwoRedelivered = false;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ if (BROKER.startsWith("vm"))
+ {
+ TransportConnection.createVMBroker(1);
+ }
+
+ testMethod++;
+ queue += testMethod;
+
+ newConnection();
+ }
+
+ private void newConnection() throws AMQException, URLSyntaxException, JMSException
+ {
+ conn = new AMQConnection("amqp://guest:guest@client/test?brokerlist='" + BROKER + "'");
+
+ _session = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE);
+
+ _jmsQueue = _session.createQueue(queue);
+ _consumer = _session.createConsumer(_jmsQueue);
+
+ _pubSession = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE);
+
+ _publisher = _pubSession.createProducer(_pubSession.createQueue(queue));
+
+ conn.start();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ conn.close();
+ if (BROKER.startsWith("vm"))
+ {
+ TransportConnection.killVMBroker(1);
+ }
+ }
+
+ /**
+ * PUT a text message, disconnect before commit, confirm it is gone.
+ *
+ * @throws Exception On error
+ */
+ public void testPutThenDisconnect() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testPutThenDisconnect";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _logger.info("reconnecting without commit");
+ conn.close();
+
+ newConnection();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(1000);
+
+ // commit to ensure message is removed from queue
+ _session.commit();
+
+ assertNull("test message was put and disconnected before commit, but is still present", result);
+ }
+
+ /**
+ * PUT a text message, disconnect before commit, confirm it is gone.
+ *
+ * @throws Exception On error
+ */
+ public void testPutThenCloseDisconnect() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testPutThenDisconnect";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _logger.info("closing publisher without commit");
+ _publisher.close();
+
+ _logger.info("reconnecting without commit");
+ conn.close();
+
+ newConnection();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(1000);
+
+ // commit to ensure message is removed from queue
+ _session.commit();
+
+ assertNull("test message was put and disconnected before commit, but is still present", result);
+ }
+
+ /**
+ * PUT a text message, rollback, confirm message is gone. The consumer is on the same connection but different
+ * session as producer
+ *
+ * @throws Exception On error
+ */
+ public void testPutThenRollback() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testPutThenRollback";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _logger.info("rolling back");
+ _pubSession.rollback();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(1000);
+
+ assertNull("test message was put and rolled back, but is still present", result);
+ }
+
+ /**
+ * GET a text message, disconnect before commit, confirm it is still there. The consumer is on a new connection
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenDisconnect() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenDisconnect";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(5000);
+ assertNotNull("retrieved message is null", msg);
+
+ _logger.info("closing connection");
+ conn.close();
+
+ newConnection();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(5000);
+
+ _session.commit();
+
+ assertNotNull("test message was consumed and disconnected before commit, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ }
+
+ /**
+ * GET a text message, close consumer, disconnect before commit, confirm it is still there. The consumer is on the
+ * same connection but different session as producer
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenCloseDisconnect() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenCloseDisconnect";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(5000);
+ assertNotNull("retrieved message is null", msg);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText());
+
+ _logger.info("reconnecting without commit");
+ _consumer.close();
+ conn.close();
+
+ newConnection();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(5000);
+
+ _session.commit();
+
+ assertNotNull("test message was consumed and disconnected before commit, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ }
+
+ /**
+ * GET a text message, rollback, confirm it is still there. The consumer is on the same connection but differnt
+ * session to the producer
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenRollback() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenRollback";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(1000);
+
+ assertNotNull("retrieved message is null", msg);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText());
+
+ _logger.info("rolling back");
+
+ _session.rollback();
+
+ _logger.info("receiving result");
+
+ Message result = _consumer.receive(1000);
+
+ _session.commit();
+ assertNotNull("test message was consumed and rolled back, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ assertTrue("Messasge is not marked as redelivered", result.getJMSRedelivered());
+ }
+
+ /**
+ * GET a text message, close message producer, rollback, confirm it is still there. The consumer is on the same
+ * connection but different session as producer
+ *
+ * @throws Exception On error
+ */
+ public void testGetThenCloseRollback() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testGetThenCloseRollback";
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+
+ Message msg = _consumer.receive(5000);
+
+ assertNotNull("retrieved message is null", msg);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText());
+
+ _logger.info("Closing consumer");
+ _consumer.close();
+
+ _logger.info("rolling back");
+ _session.rollback();
+
+ _logger.info("receiving result");
+
+ _consumer = _session.createConsumer(_jmsQueue);
+
+ Message result = _consumer.receive(5000);
+
+ _session.commit();
+ assertNotNull("test message was consumed and rolled back, but is gone", result);
+ assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText());
+ assertTrue("Messasge is not marked as redelivered", result.getJMSRedelivered());
+ }
+
+ /**
+ * Test that rolling back a session purges the dispatcher queue, and the messages arrive in the correct order
+ *
+ * @throws Exception On error
+ */
+ public void testSend2ThenRollback() throws Exception
+ {
+ int run = 0;
+ while (run < 10)
+ {
+ run++;
+ _logger.info("Run:" + run);
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending two test messages");
+ _publisher.send(_pubSession.createTextMessage("1"));
+ _publisher.send(_pubSession.createTextMessage("2"));
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+ assertEquals("1", ((TextMessage) _consumer.receive(1000)).getText());
+
+ _logger.info("rolling back");
+ _session.rollback();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(5000);
+
+ assertNotNull("test message was consumed and rolled back, but is gone", result);
+
+ // Message Order is:
+
+ // Send 1 , 2
+ // Retrieve 1 and then rollback
+ // Receieve 1 (redelivered) , 2 (may or may not be redelivered??)
+
+ verifyMessages(result);
+
+ // Occassionally get message 2 first!
+// assertEquals("Should get message one first", "1", ((TextMessage) result).getText());
+// assertTrue("Message is not marked as redelivered", result.getJMSRedelivered());
+//
+// result = _consumer.receive(1000);
+// assertEquals("Second message should be message 2", "2", ((TextMessage) result).getText());
+// assertTrue("Message is not marked as redelivered", result.getJMSRedelivered());
+//
+// result = _consumer.receive(1000);
+// assertNull("There should be no more messages", result);
+
+ _session.commit();
+ }
+ }
+
+ private void verifyMessages(Message result) throws JMSException
+ {
+
+ if (result == null)
+ {
+ assertTrue("Didn't receive redelivered message one", _gotone);
+ assertTrue("Didn't receive message two at all", _gottwo | _gottwoRedelivered);
+ _gotone = false;
+ _gottwo = false;
+ _gottwoRedelivered = false;
+ return;
+ }
+
+ if (((TextMessage) result).getText().equals("1"))
+ {
+ _logger.info("Got 1 redelivered");
+ assertTrue("Message is not marked as redelivered", result.getJMSRedelivered());
+ _gotone = true;
+
+ }
+ else
+ {
+ assertEquals("2", ((TextMessage) result).getText());
+
+ if (result.getJMSRedelivered())
+ {
+ _logger.info("Got 2 redelivered, message was prefetched");
+ _gottwoRedelivered = true;
+
+ }
+ else
+ {
+ _logger.warn("Got 2, message prefetched wasn't cleared or messages was in transit when rollback occured");
+ assertFalse("Already received message two", _gottwo);
+ assertFalse("Already received message redelivered two", _gottwoRedelivered);
+
+ _gottwo = true;
+ }
+ }
+
+ verifyMessages(_consumer.receive(1000));
+ }
+
+ /**
+ * This test sends two messages receives on of them but doesn't ack it.
+ * The consumer is then closed
+ * the first message should be returned as redelivered.
+ * the second message should be delivered normally.
+ * @throws Exception
+ */
+ public void testSend2ThenCloseAfter1andTryAgain() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending two test messages");
+ _publisher.send(_pubSession.createTextMessage("1"));
+ _publisher.send(_pubSession.createTextMessage("2"));
+ _pubSession.commit();
+
+ _logger.info("getting test message");
+ Message result = _consumer.receive(5000);
+
+ assertNotNull("Message received should not be null", result);
+ assertEquals("1", ((TextMessage) result).getText());
+ assertTrue("Messasge is marked as redelivered" + result, !result.getJMSRedelivered());
+
+ _logger.info("Closing Consumer");
+ _consumer.close();
+
+ _logger.info("Creating New consumer");
+ _consumer = _session.createConsumer(_jmsQueue);
+
+ _logger.info("receiving result");
+
+// NOTE: Both msg 1 & 2 will be marked as redelivered as they have both will have been rejected.
+// Only the occasion where it is not rejected will it mean it hasn't arrived at the client yet.
+ result = _consumer.receive(5000);
+ assertNotNull("test message was consumed and rolled back, but is gone", result);
+
+// The first message back will be either 1 or 2 being redelivered
+ if (result.getJMSRedelivered())
+ {
+ assertTrue("Messasge is not marked as redelivered" + result, result.getJMSRedelivered());
+ }
+ else // or it will be msg 2 arriving the first time due to latency.
+ {
+ _logger.info("Message 2 wasn't prefetched so wasn't rejected");
+ assertEquals("2", ((TextMessage) result).getText());
+ }
+
+ Message result2 = _consumer.receive(5000);
+ assertNotNull("test message was consumed and rolled back, but is gone", result2);
+
+ // if this is message 1 then it should be marked as redelivered
+ if("1".equals(((TextMessage) result2).getText()))
+ {
+ assertTrue("Messasge is not marked as redelivered" + result2, result2.getJMSRedelivered());
+ }
+
+ assertNotSame("Messages should not have the same content",((TextMessage) result2).getText(), ((TextMessage) result).getText() );
+
+ result = _consumer.receive(1000);
+ assertNull("test message should be null:" + result, result);
+
+ _session.commit();
+
+ }
+
+ public void testPutThenRollbackThenGet() throws Exception
+ {
+ assertTrue("session is not transacted", _session.getTransacted());
+ assertTrue("session is not transacted", _pubSession.getTransacted());
+
+ _logger.info("sending test message");
+ String MESSAGE_TEXT = "testPutThenRollbackThenGet";
+
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+ _pubSession.commit();
+
+ assertNotNull(_consumer.receive(5000));
+
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _logger.info("rolling back");
+ _pubSession.rollback();
+
+ _logger.info("receiving result");
+ Message result = _consumer.receive(5000);
+ assertNull("test message was put and rolled back, but is still present", result);
+
+ _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT));
+
+ _pubSession.commit();
+
+ assertNotNull(_consumer.receive(5000));
+
+ }
+
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java b/Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java
new file mode 100644
index 0000000000..678474a18b
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java
@@ -0,0 +1,313 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.transacted;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.Session;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.TextMessage;
+
+public class TransactedTest extends TestCase
+{
+ private AMQQueue queue1;
+ private AMQQueue queue2;
+
+ private AMQConnection con;
+ private Session session;
+ private MessageConsumer consumer1;
+ private MessageProducer producer2;
+
+ private AMQConnection prepCon;
+ private Session prepSession;
+ private MessageProducer prepProducer1;
+
+ private AMQConnection testCon;
+ private Session testSession;
+ private MessageConsumer testConsumer1;
+ private MessageConsumer testConsumer2;
+ private static final Logger _logger = LoggerFactory.getLogger(TransactedTest.class);
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ _logger.info("Create Connection");
+ con = new AMQConnection("vm://:1", "guest", "guest", "TransactedTest", "test");
+
+ _logger.info("Create Session");
+ session = con.createSession(true, Session.SESSION_TRANSACTED);
+ _logger.info("Create Q1");
+ queue1 =
+ new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q1"), new AMQShortString("Q1"), false,
+ true);
+ _logger.info("Create Q2");
+ queue2 = new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q2"), false);
+
+ _logger.info("Create Consumer of Q1");
+ consumer1 = session.createConsumer(queue1);
+ // Dummy just to create the queue.
+ _logger.info("Create Consumer of Q2");
+ MessageConsumer consumer2 = session.createConsumer(queue2);
+ _logger.info("Close Consumer of Q2");
+ consumer2.close();
+
+ _logger.info("Create producer to Q2");
+ producer2 = session.createProducer(queue2);
+
+ _logger.info("Start Connection");
+ con.start();
+
+ _logger.info("Create prep connection");
+ prepCon = new AMQConnection("vm://:1", "guest", "guest", "PrepConnection", "test");
+
+ _logger.info("Create prep session");
+ prepSession = prepCon.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ _logger.info("Create prep producer to Q1");
+ prepProducer1 = prepSession.createProducer(queue1);
+
+ _logger.info("Create prep connection start");
+ prepCon.start();
+
+ _logger.info("Create test connection");
+ testCon = new AMQConnection("vm://:1", "guest", "guest", "TestConnection", "test");
+ _logger.info("Create test session");
+ testSession = testCon.createSession(false, AMQSession.NO_ACKNOWLEDGE);
+ _logger.info("Create test consumer of q2");
+ testConsumer2 = testSession.createConsumer(queue2);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _logger.info("Close connection");
+ con.close();
+ _logger.info("Close test connection");
+ testCon.close();
+ _logger.info("Close prep connection");
+ prepCon.close();
+ _logger.info("Kill broker");
+ TransportConnection.killAllVMBrokers();
+ super.tearDown();
+ }
+
+ public void testCommit() throws Exception
+ {
+ // add some messages
+ _logger.info("Send prep A");
+ prepProducer1.send(prepSession.createTextMessage("A"));
+ _logger.info("Send prep B");
+ prepProducer1.send(prepSession.createTextMessage("B"));
+ _logger.info("Send prep C");
+ prepProducer1.send(prepSession.createTextMessage("C"));
+
+ // send and receive some messages
+ _logger.info("Send X to Q2");
+ producer2.send(session.createTextMessage("X"));
+ _logger.info("Send Y to Q2");
+ producer2.send(session.createTextMessage("Y"));
+ _logger.info("Send Z to Q2");
+ producer2.send(session.createTextMessage("Z"));
+
+ _logger.info("Read A from Q1");
+ expect("A", consumer1.receive(1000));
+ _logger.info("Read B from Q1");
+ expect("B", consumer1.receive(1000));
+ _logger.info("Read C from Q1");
+ expect("C", consumer1.receive(1000));
+
+ // commit
+ _logger.info("session commit");
+ session.commit();
+ _logger.info("Start test Connection");
+ testCon.start();
+
+ // ensure sent messages can be received and received messages are gone
+ _logger.info("Read X from Q2");
+ expect("X", testConsumer2.receive(1000));
+ _logger.info("Read Y from Q2");
+ expect("Y", testConsumer2.receive(1000));
+ _logger.info("Read Z from Q2");
+ expect("Z", testConsumer2.receive(1000));
+
+ _logger.info("create test session on Q1");
+ testConsumer1 = testSession.createConsumer(queue1);
+ _logger.info("Read null from Q1");
+ assertTrue(null == testConsumer1.receive(1000));
+ _logger.info("Read null from Q2");
+ assertTrue(null == testConsumer2.receive(1000));
+ }
+
+ public void testRollback() throws Exception
+ {
+ // add some messages
+ _logger.info("Send prep RB_A");
+ prepProducer1.send(prepSession.createTextMessage("RB_A"));
+ _logger.info("Send prep RB_B");
+ prepProducer1.send(prepSession.createTextMessage("RB_B"));
+ _logger.info("Send prep RB_C");
+ prepProducer1.send(prepSession.createTextMessage("RB_C"));
+
+ _logger.info("Sending RB_X RB_Y RB_Z");
+ producer2.send(session.createTextMessage("RB_X"));
+ producer2.send(session.createTextMessage("RB_Y"));
+ producer2.send(session.createTextMessage("RB_Z"));
+ _logger.info("Receiving RB_A RB_B");
+ expect("RB_A", consumer1.receive(1000));
+ expect("RB_B", consumer1.receive(1000));
+ // Don't consume 'RB_C' leave it in the prefetch cache to ensure rollback removes it.
+ // Quick sleep to ensure 'RB_C' gets pre-fetched
+ Thread.sleep(500);
+
+ // rollback
+ _logger.info("rollback");
+ session.rollback();
+
+ _logger.info("Receiving RB_A RB_B RB_C");
+ // ensure sent messages are not visible and received messages are requeued
+ expect("RB_A", consumer1.receive(1000), true);
+ expect("RB_B", consumer1.receive(1000), true);
+ expect("RB_C", consumer1.receive(1000), true);
+
+ _logger.info("Starting new connection");
+ testCon.start();
+ testConsumer1 = testSession.createConsumer(queue1);
+ _logger.info("Testing we have no messages left");
+ assertTrue(null == testConsumer1.receive(1000));
+ assertTrue(null == testConsumer2.receive(1000));
+
+ session.commit();
+
+ _logger.info("Testing we have no messages left after commit");
+ assertTrue(null == testConsumer1.receive(1000));
+ assertTrue(null == testConsumer2.receive(1000));
+ }
+
+ public void testResendsMsgsAfterSessionClose() throws Exception
+ {
+ AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test");
+
+ Session consumerSession = con.createSession(true, Session.SESSION_TRANSACTED);
+ AMQQueue queue3 = new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), false);
+ MessageConsumer consumer = consumerSession.createConsumer(queue3);
+
+ AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test");
+ Session producerSession = con2.createSession(true, Session.SESSION_TRANSACTED);
+ MessageProducer producer = producerSession.createProducer(queue3);
+
+ _logger.info("Sending four messages");
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer.send(producerSession.createTextMessage("msg2"));
+ producer.send(producerSession.createTextMessage("msg3"));
+ producer.send(producerSession.createTextMessage("msg4"));
+
+ producerSession.commit();
+
+ _logger.info("Starting connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive();
+ assertNotNull(tm);
+ assertEquals("msg1", tm.getText());
+
+ consumerSession.commit();
+
+ _logger.info("Received and committed first message");
+ tm = (TextMessage) consumer.receive(1000);
+ assertNotNull(tm);
+ assertEquals("msg2", tm.getText());
+
+ tm = (TextMessage) consumer.receive(1000);
+ assertNotNull(tm);
+ assertEquals("msg3", tm.getText());
+
+ tm = (TextMessage) consumer.receive(1000);
+ assertNotNull(tm);
+ assertEquals("msg4", tm.getText());
+
+ _logger.info("Received all four messages. Closing connection with three outstanding messages");
+
+ consumerSession.close();
+
+ consumerSession = con.createSession(true, Session.SESSION_TRANSACTED);
+
+ consumer = consumerSession.createConsumer(queue3);
+
+ // no ack for last three messages so when I call recover I expect to get three messages back
+ tm = (TextMessage) consumer.receive(3000);
+ assertNotNull(tm);
+ assertEquals("msg2", tm.getText());
+ assertTrue("Message is not redelivered", tm.getJMSRedelivered());
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertNotNull(tm);
+ assertEquals("msg3", tm.getText());
+ assertTrue("Message is not redelivered", tm.getJMSRedelivered());
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertNotNull(tm);
+ assertEquals("msg4", tm.getText());
+ assertTrue("Message is not redelivered", tm.getJMSRedelivered());
+
+ _logger.info("Received redelivery of three messages. Committing");
+
+ consumerSession.commit();
+
+ _logger.info("Called commit");
+
+ tm = (TextMessage) consumer.receive(1000);
+ assertNull(tm);
+
+ _logger.info("No messages redelivered as is expected");
+
+ con.close();
+ con2.close();
+ }
+
+ private void expect(String text, Message msg) throws JMSException
+ {
+ expect(text, msg, false);
+ }
+
+ private void expect(String text, Message msg, boolean requeued) throws JMSException
+ {
+ assertNotNull("Message should not be null", msg);
+ assertTrue("Message should be a text message", msg instanceof TextMessage);
+ assertEquals("Message content does not match expected", text, ((TextMessage) msg).getText());
+ assertEquals("Message should " + (requeued ? "" : "not") + " be requeued", requeued, msg.getJMSRedelivered());
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TransactedTest.class);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/testutil/Config.java b/Final/java/client/src/test/java/org/apache/qpid/testutil/Config.java
new file mode 100644
index 0000000000..b777cf93b6
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/testutil/Config.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.testutil;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQHeadersExchange;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+
+public class Config
+{
+ public static final String QUEUE = "queue";
+ public static final String TOPIC = "topic";
+ public static final String HEADERS = "headers";
+
+ private String host = "localhost";
+ private int port = 5672;
+ private String type;
+ private String name = "simple_test_queue";
+
+ public Config()
+ {
+ this("localhost", 5672, QUEUE, "simple_test_queue");
+ }
+
+ public Config(String host, int port, String type, String name)
+ {
+ setHost(host);
+ setPort(port);
+ setType(type);
+ setName(name);
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public void setHost(String host)
+ {
+ this.host = host;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ public void setPort(int port)
+ {
+ this.port = port;
+ }
+
+ public String getType()
+ {
+ return type;
+ }
+
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+ public boolean isQueue()
+ {
+ return QUEUE.equalsIgnoreCase(type);
+ }
+
+ public boolean isTopic()
+ {
+ return TOPIC.equalsIgnoreCase(type);
+ }
+
+ private boolean isHeaders()
+ {
+ return HEADERS.equalsIgnoreCase(type);
+ }
+
+ public void setQueue(boolean queue)
+ {
+ type = queue ? QUEUE : TOPIC;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public Destination getDestination()
+ {
+ if(isQueue())
+ {
+ System.out.println("Using queue named " + name);
+ return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME,name);
+ }
+ else if(isTopic())
+ {
+ System.out.println("Using topic named " + name);
+ return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME,name);
+ }
+ else if(isHeaders())
+ {
+ System.out.println("Using headers exhange named " + name);
+ return new AMQHeadersExchange(name);
+ }
+ return null;
+ }
+
+ public Connection getConnection() throws Exception
+ {
+ System.out.println("Connecting to " + host + " on " + port + "...");
+ return new AMQConnection(host, port, "guest", "guest", "Client" + System.currentTimeMillis(), "/test");
+ }
+
+ public boolean setOptions(String[] argv)
+ {
+ try
+ {
+ for(int i = 0; i < argv.length - 1; i += 2)
+ {
+ String key = argv[i];
+ String value = argv[i+1];
+ setOption(key, value);
+ }
+ return true;
+ }
+ catch(Exception e)
+ {
+ System.out.println(e.getMessage());
+ }
+ return false;
+ }
+
+ private void setOption(String key, String value)
+ {
+ if("-host".equalsIgnoreCase(key))
+ {
+ setHost(value);
+ }
+ else if("-port".equalsIgnoreCase(key))
+ {
+ try
+ {
+ setPort(Integer.parseInt(value));
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException("Bad port number: " + value, e);
+ }
+ }
+ else if("-name".equalsIgnoreCase(key))
+ {
+ setName(value);
+ }
+ else if("-type".equalsIgnoreCase(key))
+ {
+ if(QUEUE.equalsIgnoreCase(value)
+ || TOPIC.equalsIgnoreCase(value)
+ || HEADERS.equalsIgnoreCase(value))
+ {
+ type = value;
+ }
+ else{
+ throw new RuntimeException("Bad destination type: " + value);
+ }
+ }
+ else
+ {
+ System.out.println("Ignoring unrecognised option: " + key);
+ }
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.java b/Final/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.java
new file mode 100644
index 0000000000..7eb2abe7eb
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.java
@@ -0,0 +1,287 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.testutil;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.client.AMQConnectionURL;
+import org.apache.qpid.client.JMSAMQException;
+import org.apache.qpid.url.URLSyntaxException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+public class QpidClientConnection implements ExceptionListener
+{
+ private static final Logger _logger = LoggerFactory.getLogger(QpidClientConnection.class);
+
+ private boolean transacted = true;
+ private int ackMode = Session.CLIENT_ACKNOWLEDGE;
+ private Connection connection;
+
+ private String virtualHost;
+ private String brokerlist;
+ private int prefetch;
+ protected Session session;
+ protected boolean connected;
+
+ public QpidClientConnection(String broker)
+ {
+ super();
+ setVirtualHost("/test");
+ setBrokerList(broker);
+ setPrefetch(5000);
+ }
+
+ public void connect() throws JMSException
+ {
+ if (!connected)
+ {
+ /*
+ * amqp://[user:pass@][clientid]/virtualhost?
+ * brokerlist='[transport://]host[:port][?option='value'[&option='value']];'
+ * [&failover='method[?option='value'[&option='value']]']
+ * [&option='value']"
+ */
+ String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'";
+ try
+ {
+ AMQConnectionFactory factory = new AMQConnectionFactory(new AMQConnectionURL(brokerUrl));
+ _logger.info("connecting to Qpid :" + brokerUrl);
+ connection = factory.createConnection();
+
+ // register exception listener
+ connection.setExceptionListener(this);
+
+ session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch);
+
+ _logger.info("starting connection");
+ connection.start();
+
+ connected = true;
+ }
+ catch (URLSyntaxException e)
+ {
+ throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ public void disconnect() throws JMSException
+ {
+ if (connected)
+ {
+ session.commit();
+ session.close();
+ connection.close();
+ connected = false;
+ _logger.info("disconnected");
+ }
+ }
+
+ public void disconnectWithoutCommit() throws JMSException
+ {
+ if (connected)
+ {
+ session.close();
+ connection.close();
+ connected = false;
+ _logger.info("disconnected without commit");
+ }
+ }
+
+ public String getBrokerList()
+ {
+ return brokerlist;
+ }
+
+ public void setBrokerList(String brokerlist)
+ {
+ this.brokerlist = brokerlist;
+ }
+
+ public String getVirtualHost()
+ {
+ return virtualHost;
+ }
+
+ public void setVirtualHost(String virtualHost)
+ {
+ this.virtualHost = virtualHost;
+ }
+
+ public void setPrefetch(int prefetch)
+ {
+ this.prefetch = prefetch;
+ }
+
+ /** override as necessary */
+ public void onException(JMSException exception)
+ {
+ _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage());
+ }
+
+ public boolean isConnected()
+ {
+ return connected;
+ }
+
+ public Session getSession()
+ {
+ return session;
+ }
+
+ /**
+ * Put a String as a text messages, repeat n times. A null payload will result in a null message.
+ *
+ * @param queueName The queue name to put to
+ * @param payload the content of the payload
+ * @param copies the number of messages to put
+ *
+ * @throws javax.jms.JMSException any exception that occurs
+ */
+ public void put(String queueName, String payload, int copies) throws JMSException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ _logger.info("putting to queue " + queueName);
+ Queue queue = session.createQueue(queueName);
+
+ final MessageProducer sender = session.createProducer(queue);
+
+ for (int i = 0; i < copies; i++)
+ {
+ Message m = session.createTextMessage(payload + i);
+ m.setIntProperty("index", i + 1);
+ sender.send(m);
+ }
+
+ session.commit();
+ sender.close();
+ _logger.info("put " + copies + " copies");
+ }
+
+ /**
+ * GET the top message on a queue. Consumes the message. Accepts timeout value.
+ *
+ * @param queueName The quename to get from
+ * @param readTimeout The timeout to use
+ *
+ * @return the content of the text message if any
+ *
+ * @throws javax.jms.JMSException any exception that occured
+ */
+ public Message getNextMessage(String queueName, long readTimeout) throws JMSException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ Queue queue = session.createQueue(queueName);
+
+ final MessageConsumer consumer = session.createConsumer(queue);
+
+ Message message = consumer.receive(readTimeout);
+ session.commit();
+ consumer.close();
+
+ Message result;
+
+ // all messages we consume should be TextMessages
+ if (message instanceof TextMessage)
+ {
+ result = ((TextMessage) message);
+ }
+ else if (null == message)
+ {
+ result = null;
+ }
+ else
+ {
+ _logger.info("warning: received non-text message");
+ result = message;
+ }
+
+ return result;
+ }
+
+ /**
+ * GET the top message on a queue. Consumes the message.
+ *
+ * @param queueName The Queuename to get from
+ *
+ * @return The string content of the text message, if any received
+ *
+ * @throws javax.jms.JMSException any exception that occurs
+ */
+ public Message getNextMessage(String queueName) throws JMSException
+ {
+ return getNextMessage(queueName, 0);
+ }
+
+ /**
+ * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer.
+ *
+ * @param queueName The Queue name to consume from
+ * @param readTimeout The timeout for each consume
+ *
+ * @throws javax.jms.JMSException Any exception that occurs during the consume
+ * @throws InterruptedException If the consume thread was interrupted during a consume.
+ */
+ public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ _logger.info("consuming queue " + queueName);
+ Queue queue = session.createQueue(queueName);
+
+ final MessageConsumer consumer = session.createConsumer(queue);
+ int messagesReceived = 0;
+
+ _logger.info("consuming...");
+ while ((consumer.receive(readTimeout)) != null)
+ {
+ messagesReceived++;
+ }
+
+ session.commit();
+ consumer.close();
+ _logger.info("consumed: " + messagesReceived);
+ }
+}
diff --git a/Final/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.java b/Final/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.java
new file mode 100644
index 0000000000..cedf1ac824
--- /dev/null
+++ b/Final/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.testutil;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+
+import org.apache.qpid.client.transport.TransportConnection;
+
+public class VMBrokerSetup extends TestSetup
+{
+ public VMBrokerSetup(Test t)
+ {
+ super(t);
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ catch (Exception e)
+ {
+ fail("Unable to create broker: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ TransportConnection.killVMBroker(1);
+ super.tearDown();
+ }
+}
diff --git a/Final/java/client/test/bin/IBM-JNDI-Setup.bat b/Final/java/client/test/bin/IBM-JNDI-Setup.bat
new file mode 100644
index 0000000000..eb6a87fa9e
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-JNDI-Setup.bat
@@ -0,0 +1,69 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Java Broker
+
+set CMD="IBM-JNDI-Setup.bat"
+set JAVACLASS=
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar
+
+echo on
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist='localhost' amq.ConnectionFactory
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist='vm://:1' amq.VMConnectionFactory
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindQueue amq.Queue direct://amq.direct//IBMPerfQueue1
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic1 topic://amq.topic/IBMPerfTopic1/
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic2 topic://amq.topic/IBMPerfTopic2/
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic3 topic://amq.topic/IBMPerfTopic3/
+
+
+
+:end
+
+pause \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-JNDI-Setup.sh b/Final/java/client/test/bin/IBM-JNDI-Setup.sh
new file mode 100755
index 0000000000..e3112f812d
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-JNDI-Setup.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+qpid-run org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist=\'tcp://localhost\' amq.ConnectionFactory
+qpid-run org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist=\'vm://:1\' amq.VMConnectionFactory
+qpid-run org.apache.qpid.IBMPerfTest.JNDIBindQueue amq.Queue direct://amq.direct//IBMPerfQueue1
+qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic1 topic://amq.topic/IBMPerfTopic1/
+qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic2 topic://amq.topic/IBMPerfTopic2/
+qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic3 topic://amq.topic/IBMPerfTopic3/
+qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic4 topic://amq.topic/IBMPerfTopic4/ \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Publisher.bat b/Final/java/client/test/bin/IBM-Publisher.bat
new file mode 100644
index 0000000000..5bb4343c4c
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Publisher.bat
@@ -0,0 +1,62 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Java Broker
+
+set CMD="IBM-Publisher.bat"
+set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Publisher -nt 4 %*
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar
+
+echo on
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS%
+
+:end
+
+pause \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Publisher.sh b/Final/java/client/test/bin/IBM-Publisher.sh
new file mode 100755
index 0000000000..adecf040bc
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Publisher.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+export MSGSIZE=100
+qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Publisher -nt 4 $* \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-PutGet.bat b/Final/java/client/test/bin/IBM-PutGet.bat
new file mode 100644
index 0000000000..c4316f1256
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-PutGet.bat
@@ -0,0 +1,62 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Java Broker
+
+set CMD="IBM-PutGet.bat"
+set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.PutGet -nt 6 %*
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar
+
+echo on
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS%
+
+:end
+
+pause \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-PutGet.sh b/Final/java/client/test/bin/IBM-PutGet.sh
new file mode 100755
index 0000000000..c75667c9f6
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-PutGet.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.PutGet -nt 6 $* \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-README.txt b/Final/java/client/test/bin/IBM-README.txt
new file mode 100644
index 0000000000..b076f3b3ca
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-README.txt
@@ -0,0 +1,19 @@
+The IBM JMS Performance Harness scripts have take the following additional parameters
+
+-tx : Enable transactions
+-pp : Enable persistent messaging
+
+-ms 1000 : Set message size (default 1000 bytes)
+-cc 1 : Number of messages to per commit (default 1) Only applies to Sender/Subscriber
+
+The IBM JMS Performance Harness will need to be downloaded and the library added to client/test/lib.
+
+The Library can be found here:
+
+http://www.alphaworks.ibm.com/tech/perfharness
+
+Before running the required test the IBM JNDI Setup script should be run.
+
+This will create a filesystem based JNDI Context located at:
+
+System.properties{java.io.tmpdir}/IBMPerfTestsJNDI/ \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Receiver.bat b/Final/java/client/test/bin/IBM-Receiver.bat
new file mode 100644
index 0000000000..dff44d472a
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Receiver.bat
@@ -0,0 +1,62 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Java Broker
+
+set CMD="IBM-Receiver.bat"
+set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Receiver %*
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar
+
+echo on
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS%
+
+:end
+
+pause \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Receiver.sh b/Final/java/client/test/bin/IBM-Receiver.sh
new file mode 100755
index 0000000000..f50f0f744e
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Receiver.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+export MSGSIZE=100
+qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Receiver $* \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Sender.bat b/Final/java/client/test/bin/IBM-Sender.bat
new file mode 100644
index 0000000000..b8826322e5
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Sender.bat
@@ -0,0 +1,62 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Java Broker
+
+set CMD="IBM-Sender.bat"
+set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Sender -ms $MSGSIZE -mg 1000000 %*
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar
+
+echo on
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS%
+
+:end
+
+pause \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Sender.sh b/Final/java/client/test/bin/IBM-Sender.sh
new file mode 100755
index 0000000000..b99429fd54
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Sender.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+export MSGSIZE=100
+qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Sender -ms $MSGSIZE -mg 1000000 $* \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Subscriber.bat b/Final/java/client/test/bin/IBM-Subscriber.bat
new file mode 100644
index 0000000000..5245639eba
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Subscriber.bat
@@ -0,0 +1,62 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Java Broker
+
+set CMD="IBM-Subscriber.bat"
+set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Subscriber -nt 4 %*
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\%CMD%" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar
+
+echo on
+"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS%
+
+:end
+
+pause \ No newline at end of file
diff --git a/Final/java/client/test/bin/IBM-Subscriber.sh b/Final/java/client/test/bin/IBM-Subscriber.sh
new file mode 100755
index 0000000000..43550100be
--- /dev/null
+++ b/Final/java/client/test/bin/IBM-Subscriber.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+export MSGSIZE=100
+qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Subscriber -nt 4 $* \ No newline at end of file
diff --git a/Final/java/client/test/bin/headersListener.sh b/Final/java/client/test/bin/headersListener.sh
new file mode 100755
index 0000000000..81930b7043
--- /dev/null
+++ b/Final/java/client/test/bin/headersListener.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.headers.Listener $*
diff --git a/Final/java/client/test/bin/headersListenerGroup.sh b/Final/java/client/test/bin/headersListenerGroup.sh
new file mode 100755
index 0000000000..e1cc05cfd2
--- /dev/null
+++ b/Final/java/client/test/bin/headersListenerGroup.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+for i; do
+ ./headersListener.sh -host 10.0.0.1 -port 5672 >$i.out 2>$i.err &
+ echo $! > $i.pid
+done;
diff --git a/Final/java/client/test/bin/headersPublisher.sh b/Final/java/client/test/bin/headersPublisher.sh
new file mode 100755
index 0000000000..fd9fd26416
--- /dev/null
+++ b/Final/java/client/test/bin/headersPublisher.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.headers.Publisher $*
diff --git a/Final/java/client/test/bin/run_many.sh b/Final/java/client/test/bin/run_many.sh
new file mode 100755
index 0000000000..cca2ffec21
--- /dev/null
+++ b/Final/java/client/test/bin/run_many.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+# args:
+# <number of processes to start>
+# <name of run>
+# <command ro run>
+
+for i in `seq 1 $1`; do
+ $3 >$2.$i.out 2>>$2.err &
+ echo $! > $2.$i.pid
+done;
diff --git a/Final/java/client/test/bin/serviceProvidingClient.sh b/Final/java/client/test/bin/serviceProvidingClient.sh
new file mode 100755
index 0000000000..cbcf5a0f4b
--- /dev/null
+++ b/Final/java/client/test/bin/serviceProvidingClient.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+PROFILE=$1
+shift
+# XXX -Xms1024m -XX:NewSize=300m
+. qpid-run org.apache.qpid.requestreply1.ServiceProvidingClient $1 guest guest /test serviceQ
diff --git a/Final/java/client/test/bin/serviceRequestingClient.sh b/Final/java/client/test/bin/serviceRequestingClient.sh
new file mode 100755
index 0000000000..213f44c00b
--- /dev/null
+++ b/Final/java/client/test/bin/serviceRequestingClient.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+PROFILE=$1
+shift
+thehosts=$1
+shift
+echo $thehosts
+# XXX -Xms1024m -XX:NewSize=300m
+. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.requestreply1.ServiceRequestingClient $thehosts guest guest /test serviceQ "$@"
diff --git a/Final/java/client/test/bin/testService.sh b/Final/java/client/test/bin/testService.sh
new file mode 100755
index 0000000000..20161c3abf
--- /dev/null
+++ b/Final/java/client/test/bin/testService.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+. qpid-run org.apache.qpid.requestreply1.TestService 192.168.55.63 5672 foo x x
diff --git a/Final/java/client/test/bin/topicListener.sh b/Final/java/client/test/bin/topicListener.sh
new file mode 100755
index 0000000000..ac0cb63c91
--- /dev/null
+++ b/Final/java/client/test/bin/topicListener.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+# XXX -Xmx512m -Xms512m -XX:NewSize=150m
+. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.topic.Listener $*
diff --git a/Final/java/client/test/bin/topicPublisher.sh b/Final/java/client/test/bin/topicPublisher.sh
new file mode 100755
index 0000000000..e35c131fe8
--- /dev/null
+++ b/Final/java/client/test/bin/topicPublisher.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# XXX -Xmx512m -Xms512m -XX:NewSize=150m
+. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.topic.Publisher $*
diff --git a/Final/java/client/test/etc/ApacheDS.properties b/Final/java/client/test/etc/ApacheDS.properties
new file mode 100644
index 0000000000..6c5cb4cec4
--- /dev/null
+++ b/Final/java/client/test/etc/ApacheDS.properties
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# Standard JNDI properties
+java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
+java.naming.provider.url=ldap://localhost:389/ou=system
+java.naming.security.authentication=simple
+java.naming.security.principal=uid=admin,ou=system
+java.naming.security.credentials=secret
diff --git a/Final/java/client/test/example_build.xml b/Final/java/client/test/example_build.xml
new file mode 100644
index 0000000000..a12862be04
--- /dev/null
+++ b/Final/java/client/test/example_build.xml
@@ -0,0 +1,104 @@
+<?xml version="1.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.
+ -
+ -->
+
+<!-- example Blaze Component Java build file -->
+
+<project name="example-client" default="jar" basedir=".">
+ <property name="lib" value="${basedir}/lib"/>
+ <property name="common.lib" value="${basedir}/../common/lib"/>
+ <property name="example.dir" value="${basedir}"/>
+ <property name="example.src" value="${example.dir}/src"/>
+ <property name="example.lib" value="${example.dir}/lib"/>
+ <property name="example.tests" value="${example.dir}/test"/>
+ <property name="example.classes" value="${example.dir}/classes"/>
+ <property name="dist" value="${basedir}/dist"/>
+ <property name="dam.dist" value="${basedir}/damPackage"/>
+
+ <!-- Setup details -->
+ <target name="init">
+ <tstamp>
+ <format property="release" pattern="-dMMMyy" locale="en" timezone="GMT"/>
+ </tstamp>
+ <mkdir dir="${example.classes}"/>
+ </target>
+
+ <path id="example.classpath">
+ <fileset dir="${common}/lib">
+ <include name="**/*.jar"/>
+ </fileset>
+ <pathelement path="${example.classes}"/>
+ </path>
+
+ <!-- Remove all built files -->
+ <target name="clean" depends="init">
+ <delete dir="${example.classes}"/>
+ </target>
+
+ <path id="example_amq.classpath">
+ <fileset dir="${basedir}/lib">
+ <include name="**/*.jar"/>
+ </fileset>
+ <fileset dir="${example.lib}">
+ <include name="**/*.jar"/>
+ </fileset>
+ <pathelement path="${example.classes}"/>
+
+ </path>
+
+ <!-- Compile Java -->
+ <target name="compile" depends="init">
+ <javac destdir="${example.classes}" debug="on">
+ <classpath refid="example_amq.classpath"/>
+ <src path="${example.src}"/>
+ <exclude name="**/Test*.java"/>
+ </javac>
+
+ <copy todir="${example.classes}">
+ <!-- copy any non java src files into the build tree, e.g. log4j.properties -->
+ <fileset dir="${example.src}">
+ <exclude name="**/*.java"/>
+ <exclude name="**/package.html"/>
+ </fileset>
+ </copy>
+ </target>
+
+ <!-- Compile and build jar archive -->
+ <target name="dist" depends="compile">
+ <mkdir dir="${dist}"/>
+ <jar basedir="${example.classes}" jarfile="${dist}/example_amq.jar"/>
+ </target>
+
+ <!-- Create release zip and tar -->
+ <target name="release" depends="dist" description="Create a release package">
+
+ <zip destfile="${dist}/example_client.zip">
+ <zipfileset prefix="lib" file="${dist}/example_amq.jar" />
+ </zip>
+
+ <tar destfile="${dist}/example_client.tar.gz" compression="gzip">
+ <tarfileset prefix="lib" file="${dist}/example_amq.jar" />
+ </tar>
+ </target>
+
+
+
+</project>
diff --git a/Final/java/cluster/doc/design.doc b/Final/java/cluster/doc/design.doc
new file mode 100644
index 0000000000..c5bbf0f8a4
--- /dev/null
+++ b/Final/java/cluster/doc/design.doc
Binary files differ
diff --git a/Final/java/cluster/pom.xml b/Final/java/cluster/pom.xml
new file mode 100644
index 0000000000..aaaf7d2f26
--- /dev/null
+++ b/Final/java/cluster/pom.xml
@@ -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.
+-->
+<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</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</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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java
new file mode 100644
index 0000000000..2baaa344ef
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+import org.apache.qpid.AMQException;
+
+/**
+ * AMQConnectionWaitException represents a failure to connect to a cluster peer in a timely manner.
+ *
+ * <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.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQConnectionWaitException extends AMQException
+{
+ public AMQConnectionWaitException(String s, Throwable e)
+ {
+ super(s, e);
+
+ }
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java
new file mode 100644
index 0000000000..951bd22df0
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java
new file mode 100644
index 0000000000..4dd318f90d
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQUnexpectedFrameTypeException represents a failure when Mina passes an unexpected frame type.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to cast a frame to its expected type.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would
+ * be better just to leave that as a ClassCastException. However, check the framing layer catches this error
+ * first.
+ */
+public class AMQUnexpectedFrameTypeException extends AMQException
+{
+ public AMQUnexpectedFrameTypeException(String s)
+ {
+ super(s);
+ }
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java
new file mode 100644
index 0000000000..39508df566
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java
@@ -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.
+ *
+ */
+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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java
new file mode 100644
index 0000000000..145aa58574
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+public interface BroadcastPolicy
+{
+ public boolean isComplete(int responded, int members);
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java
new file mode 100644
index 0000000000..7e2cf6da83
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java
@@ -0,0 +1,247 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java
new file mode 100644
index 0000000000..92c3c4e7bf
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+interface BrokerFactory
+{
+ public Broker create(MemberHandle handle);
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java
new file mode 100644
index 0000000000..755a341607
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java
@@ -0,0 +1,368 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java
new file mode 100644
index 0000000000..1b4a3e8327
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java
new file mode 100644
index 0000000000..c1caf8bbff
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java
@@ -0,0 +1,130 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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)
+ {
+ return super.createConnectionOpenFrame(channel, path, new AMQShortString(ClusterCapability.add(capabilities, _identity)), insist, major, minor);
+ }
+ }
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java
new file mode 100644
index 0000000000..80f9ef62b1
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java
new file mode 100644
index 0000000000..57c48f0611
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java
new file mode 100644
index 0000000000..ee5aa48db9
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java
@@ -0,0 +1,190 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java
new file mode 100644
index 0000000000..eea660c4f0
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java
@@ -0,0 +1,133 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java
new file mode 100644
index 0000000000..a1f01eff46
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java
new file mode 100644
index 0000000000..2f473b63fb
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java
@@ -0,0 +1,396 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java
new file mode 100644
index 0000000000..5599ae4b1f
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java
new file mode 100644
index 0000000000..8ab7856e87
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java
@@ -0,0 +1,107 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java
new file mode 100644
index 0000000000..d2e9de2f39
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java
new file mode 100644
index 0000000000..586d7d4ae8
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java
new file mode 100644
index 0000000000..5f92aa2971
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+public enum JoinState
+{
+ UNINITIALISED, JOINING, INITIATION, INDUCTION, JOINED
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java
new file mode 100644
index 0000000000..13465a8615
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java
@@ -0,0 +1,107 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java
new file mode 100644
index 0000000000..15752353d1
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java
@@ -0,0 +1,117 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java
new file mode 100644
index 0000000000..3fbdfdde70
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java
new file mode 100644
index 0000000000..7ce45dffaa
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+interface MemberFailureListener
+{
+ public void failed(MemberHandle member);
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java
new file mode 100644
index 0000000000..b8099a12f7
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+public interface MemberHandle
+{
+ public String getHost();
+
+ public int getPort();
+
+ public boolean matches(MemberHandle m);
+
+ public boolean matches(String host, int port);
+
+ public AMQShortString getDetails();
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java
new file mode 100644
index 0000000000..591e652e32
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+import java.util.List;
+
+public interface MembershipChangeListener
+{
+ public void changed(List<MemberHandle> members);
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java
new file mode 100644
index 0000000000..a83f034021
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java
new file mode 100644
index 0000000000..9bf04f5458
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+import org.apache.qpid.server.state.AMQState;
+
+public interface MethodHandlerFactory
+{
+ public MethodHandlerRegistry register(AMQState state, MethodHandlerRegistry registry);
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java
new file mode 100644
index 0000000000..748a660bb8
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java
new file mode 100644
index 0000000000..b01ec491ec
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java
@@ -0,0 +1,272 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java
new file mode 100644
index 0000000000..5e70de7665
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java
new file mode 100644
index 0000000000..fe76ca6505
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+public interface ResponseHandler
+{
+ public void responded(AMQMethodBody response);
+
+ public void removed();
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java
new file mode 100644
index 0000000000..159612331c
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+import org.apache.qpid.AMQException;
+
+public interface Sendable
+{
+ public void send(int channel, Member member) throws AMQException;
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java
new file mode 100644
index 0000000000..aadcfa4b4c
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java
new file mode 100644
index 0000000000..bd3757bf97
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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();
+ }
+
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java
new file mode 100644
index 0000000000..1255094b1d
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java
@@ -0,0 +1,166 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java
new file mode 100644
index 0000000000..7e5563460f
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.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.server.cluster;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java
new file mode 100644
index 0000000000..86710e8a31
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java
new file mode 100644
index 0000000000..c9f6dbfb37
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java
@@ -0,0 +1,136 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java
new file mode 100644
index 0000000000..faab99b0f6
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java
new file mode 100644
index 0000000000..e7509da32a
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java
new file mode 100644
index 0000000000..a2f62f714b
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java
new file mode 100644
index 0000000000..0dc7fe00d2
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java
@@ -0,0 +1,25 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java
new file mode 100644
index 0000000000..f01a8349f2
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java
new file mode 100644
index 0000000000..8b0bb4b127
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java
new file mode 100644
index 0000000000..447e51ccd9
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java
new file mode 100644
index 0000000000..a669171d3c
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java
new file mode 100644
index 0000000000..f09763e1ad
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java
new file mode 100644
index 0000000000..073b13688c
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java
@@ -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.
+ *
+ */
+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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java
new file mode 100644
index 0000000000..897f8e4fb7
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java
new file mode 100644
index 0000000000..888fa4e426
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java
new file mode 100644
index 0000000000..8b0c638d63
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java
new file mode 100644
index 0000000000..5ec3c9660a
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java
@@ -0,0 +1,85 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java
new file mode 100644
index 0000000000..79cb558ede
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java
new file mode 100644
index 0000000000..42382c6e7a
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java
new file mode 100644
index 0000000000..e3072a6a40
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java
new file mode 100644
index 0000000000..dbaf690d3a
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java
new file mode 100644
index 0000000000..605b8dd51e
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java
new file mode 100644
index 0000000000..3664be58bc
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster.replay;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+abstract class ChainedMethodRecorder <T extends AMQMethodBody> implements MethodRecorder<T>
+{
+ private final MethodRecorder<T> _recorder;
+
+ ChainedMethodRecorder()
+ {
+ this(null);
+ }
+
+ ChainedMethodRecorder(MethodRecorder<T> recorder)
+ {
+ _recorder = recorder;
+ }
+
+ public final void record(T method)
+ {
+ if(!doRecord(method) && _recorder != null)
+ {
+ _recorder.record(method);
+ }
+ }
+
+ protected abstract boolean doRecord(T method);
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java
new file mode 100644
index 0000000000..5a433b869b
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java
@@ -0,0 +1,83 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java
new file mode 100644
index 0000000000..e45810438e
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java
new file mode 100644
index 0000000000..4d3fe1dbed
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java
new file mode 100644
index 0000000000..898cb80cb3
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java
@@ -0,0 +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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java
new file mode 100644
index 0000000000..d7bbb1c36b
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java
@@ -0,0 +1,311 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java
new file mode 100644
index 0000000000..49de0a7cbf
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java
@@ -0,0 +1,83 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java
new file mode 100644
index 0000000000..406fe45701
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java
new file mode 100644
index 0000000000..9be90298ea
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java
new file mode 100644
index 0000000000..ebe1fe47dd
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Maps a key to a collection of values
+ *
+ */
+public class MultiValuedMap<K, V>
+{
+ private Map<K, Collection<V>> _map = new HashMap<K, Collection<V>>();
+
+ public boolean add(K key, V value)
+ {
+ 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);
+ }
+
+ public Collection<V> remove(K key)
+ {
+ return _map.remove(key);
+ }
+
+ protected Collection<V> createList()
+ {
+ return new ArrayList<V>();
+ }
+}
diff --git a/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java
new file mode 100644
index 0000000000..9fa96ece1e
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.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.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/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java
new file mode 100644
index 0000000000..39ae7e3c3e
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java
new file mode 100644
index 0000000000..0566c5203b
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java
@@ -0,0 +1,116 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java
new file mode 100644
index 0000000000..f8e4311a77
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java
new file mode 100644
index 0000000000..efc0540c18
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java
new file mode 100644
index 0000000000..2a83d65ae5
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java
@@ -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.
+ *
+ */
+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/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java
new file mode 100644
index 0000000000..e396432cea
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java
@@ -0,0 +1,176 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java
new file mode 100644
index 0000000000..cc951a4709
--- /dev/null
+++ b/Final/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java
new file mode 100644
index 0000000000..b91d7140e0
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java
@@ -0,0 +1,270 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java
new file mode 100644
index 0000000000..f1da312eea
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java
@@ -0,0 +1,237 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java
new file mode 100644
index 0000000000..830a00f4c2
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.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.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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java
new file mode 100644
index 0000000000..7e58add91e
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java
@@ -0,0 +1,106 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java
new file mode 100644
index 0000000000..1ec5154a98
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQDataBlock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class RecordingBroker extends TestBroker
+{
+ private final List<AMQDataBlock> _messages = new ArrayList<AMQDataBlock>();
+
+ RecordingBroker(String host, int port)
+ {
+ super(host, port);
+ }
+
+ public void send(AMQDataBlock data) throws AMQException
+ {
+ _messages.add(data);
+ }
+
+ List<AMQDataBlock> getMessages()
+ {
+ return _messages;
+ }
+
+ void clear()
+ {
+ _messages.clear();
+ }
+
+}
diff --git a/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java
new file mode 100644
index 0000000000..d3e972e273
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+class RecordingBrokerFactory implements BrokerFactory
+{
+ public Broker create(MemberHandle handle)
+ {
+ return new RecordingBroker(handle.getHost(), handle.getPort());
+ }
+}
diff --git a/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java
new file mode 100644
index 0000000000..86cde3cee7
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java
new file mode 100644
index 0000000000..8ff8357377
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.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.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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java
new file mode 100644
index 0000000000..d3ccbf0ac6
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java
new file mode 100644
index 0000000000..92eaec876a
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.cluster;
+
+class TestBrokerFactory implements BrokerFactory
+{
+ public Broker create(MemberHandle handle)
+ {
+ return new TestBroker(handle.getHost(), handle.getPort());
+ }
+}
diff --git a/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java
new file mode 100644
index 0000000000..c529c83cc0
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java
@@ -0,0 +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.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/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java
new file mode 100644
index 0000000000..86ec808924
--- /dev/null
+++ b/Final/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java
@@ -0,0 +1,269 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/common/bin/qpid-run b/Final/java/common/bin/qpid-run
new file mode 100644
index 0000000000..c9e37b21a1
--- /dev/null
+++ b/Final/java/common/bin/qpid-run
@@ -0,0 +1,238 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Test if we're running on cygwin.
+cygwin=false
+if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then
+ cygwin=true
+fi
+
+die() {
+ if [[ $1 = -usage ]]; then
+ shift
+ usage=true
+ else
+ usage=false
+ fi
+ echo "$@"
+ $usage && echo
+ $usage && usage
+ exit 1
+}
+
+if [ -z $AMQJ_LOGGING_LEVEL ]; then
+ export AMQJ_LOGGING_LEVEL=info
+fi
+
+if [ -z "$QPID_HOME" ]; then
+ die "QPID_HOME must be set"
+fi
+
+if [ -z "$QPID_WORK" ]; then
+ echo Setting QPID_WORK to $HOME as default
+ QPID_WORK=$HOME
+fi
+
+if $cygwin; then
+ QPID_HOME=$(cygpath -w $QPID_HOME)
+ QPID_WORK=$(cygpath -w $QPID_WORK)
+fi
+
+#Set the default system properties that we'll use now that they have
+#all been initialised
+SYSTEM_PROPS="-Damqj.logging.level=$AMQJ_LOGGING_LEVEL -DQPID_HOME=$QPID_HOME -DQPID_WORK=$QPID_WORK"
+
+#If logprefix or logsuffix set to use PID make that happen
+#Otherwise just pass the value through for these props
+#Using X character to avoid probs with empty strings
+if [ -n "$QPID_LOG_PREFIX" ]; then
+ if [ "X$QPID_LOG_PREFIX" = "XPID" ]; then
+ echo Using pid in qpid log name prefix
+ LOG_PREFIX=" -Dlogprefix=$$"
+ else
+ echo Using qpid logprefix property
+ LOG_PREFIX=" -Dlogprefix=$QPID_LOG_PREFIX"
+ fi
+ SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_PREFIX}"
+fi
+
+if [ -n "$QPID_LOG_SUFFIX" ]; then
+ if [ "X$QPID_LOG_SUFFIX" = "XPID" ]; then
+ echo Using pid in qpid log name suffix
+ LOG_SUFFIX=" -Dlogsuffix=$$"
+ else
+ echo Using qpig logsuffix property
+ LOG_SUFFIX=" -Dlogsuffix=$QPID_LOG_SUFFIX"
+ fi
+ SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_SUFFIX}"
+fi
+
+echo System Properties set to $SYSTEM_PROPS
+
+program=$(basename $0)
+sourced=${BASH_SOURCE[0]}
+if [[ -z ${sourced:-''} ]]; then
+ sourced=$(which qpid-run) || ${QPID_HOME}/bin/qpid-run
+fi
+
+usage() {
+ echo Usage: $program ... "[-run:<option>]" ...
+ echo
+ echo Options:
+ egrep -B 1 "^\s*#USAGE: " ${sourced} |\
+ sed "s/#USAGE:/ /" |\
+ sed "s/-run:\(.*\))/-run:\1/" |\
+ sed "s/-run:\(.*\)=\*/-run:\1=<value>/" |\
+ sed "s/^--$//"
+}
+
+export EXTERNAL_CLASSPATH=$CLASSPATH
+unset CLASSPATH
+
+#Use QPID_CLASSPATH if set
+if [ -n "$QPID_CLASSPATH" ]; then
+ export CLASSPATH=$QPID_CLASSPATH
+ echo "Using QPID_CLASSPATH" $QPID_CLASSPATH
+else
+ echo "Warning: Qpid classpath not set. CLASSPATH must include qpid jars."
+fi
+
+#Use QPID_JAVA_GC if set
+if [ -n "$QPID_JAVA_GC" ]; then
+ export JAVA_GC=$QPID_JAVA_GC
+ echo "Using QPID_JAVA_GC setting" $QPID_JAVA_GC
+else
+ echo "Info: QPID_JAVA_GC not set. Defaulting to JAVA_GC" $JAVA_GC
+fi
+
+
+#Use QPID_JAVA_MEM if set
+if [ -n "$QPID_JAVA_MEM" ]; then
+ export JAVA_MEM=$QPID_JAVA_MEM
+ echo "Using QPID_JAVA_MEM setting" $QPID_JAVA_MEM
+else
+ echo "Info: QPID_JAVA_MEM not set. Defaulting to JAVA_MEM" $JAVA_MEM
+fi
+
+declare -a RUN_ARGS JAVA_ARGS
+for arg in "$@"; do
+ if [[ $arg == -run:* ]]; then
+ RUN_ARGS[${#RUN_ARGS[@]}]="$arg"
+ else
+ JAVA_ARGS[${#JAVA_ARGS[@]}]="$arg"
+ fi
+done
+
+# this defines the default behavior, it may be modified during option
+# processing below
+DISPATCH() {
+ if $debug; then
+ echo "CLASSPATH=${CLASSPATH}"
+ echo "${COMMAND[@]}"
+ fi
+
+ exec "${COMMAND[@]}"
+}
+
+exclusive() {
+ if [ -z "$PREVIOUS_ARGS" ]; then
+ PREVIOUS_ARGS=$1
+ else
+ PREVIOUS_ARGS="${PREVIOUS_ARGS}, $1"
+ DISPATCH() {
+ die -usage "you must choose one of: $PREVIOUS_ARGS"
+ }
+ fi
+}
+
+debug=false
+
+for arg in "${RUN_ARGS[@]}"; do
+ case $arg in
+ -run:debug)
+#USAGE: print the classpath and command before running it
+ debug=true
+ ;;
+ -run:jpda)
+#USAGE: adds debugging options to the java command, use
+#USAGE: JDPA_TRANSPORT and JPDA_ADDRESS to customize the debugging
+#USAGE: behavior and use JPDA_OPTS to override it entirely
+ if [ -z "$JPDA_OPTS" ]; then
+ JPDA_OPTS="-Xdebug -Xrunjdwp:transport=${JPDA_TRANSPORT:-dt_socket},address=${JPDA_ADDRESS:-8000},server=y,suspend=n"
+ fi
+ QPID_OPTS="${QPID_OPTS} ${JPDA_OPTS}"
+ ;;
+ -run:external-classpath=*)
+#USAGE: controls how the CLASSPATH environment variable is used by
+#USAGE: this script, value can be one of ignore (the default), first,
+#USAGE: last, and only
+ case $arg in
+ *=ignore)
+ # do nothing
+ ;;
+ *=first)
+ CLASSPATH=$EXTERNAL_CLASSPATH:$CLASSPATH
+ ;;
+ *=last)
+ CLASSPATH=$CLASSPATH:$EXTERNAL_CLASSPATH
+ ;;
+ *=only)
+ CLASSPATH=$EXTERNAL_CLASSPATH
+ ;;
+ *)
+ die -usage $(echo $arg | sed "s/=/: invalid value '/")\'
+ ;;
+ esac
+ ;;
+ -run:print-classpath)
+#USAGE: print the classpath
+ DISPATCH() {
+ echo $CLASSPATH
+ }
+ exclusive $arg
+ ;;
+ -run:print-command)
+#USAGE: print the command
+ DISPATCH() {
+ echo "${COMMAND[@]}"
+ }
+ exclusive $arg
+ ;;
+ -run:help)
+#USAGE: print this message
+ DISPATCH() {
+ usage
+ }
+ exclusive $arg
+ ;;
+ *)
+ die -usage "unrecognized -run option '$arg'"
+ ;;
+ esac
+done
+
+if $cygwin; then
+ CLASSPATH=$(cygpath -w -p $CLASSPATH)
+ JAVA=$(cygpath -u $JAVA)
+fi
+
+COMMAND=($JAVA $JAVA_VM $JAVA_GC $JAVA_MEM $SYSTEM_PROPS $JAVA_OPTS $QPID_OPTS "${JAVA_ARGS[@]}")
+
+DISPATCH
diff --git a/Final/java/common/etc/qpid-run.conf b/Final/java/common/etc/qpid-run.conf
new file mode 100644
index 0000000000..b9765fe3ce
--- /dev/null
+++ b/Final/java/common/etc/qpid-run.conf
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+QPID_LIBS=$(find ${QPID_HOME}/lib -name "*.jar" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D')
+
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ CLASSPATH=$QPID_LIBS
diff --git a/Final/java/common/etc/qpid-run.conf.dev b/Final/java/common/etc/qpid-run.conf.dev
new file mode 100644
index 0000000000..a5419eb4e8
--- /dev/null
+++ b/Final/java/common/etc/qpid-run.conf.dev
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+QPID_LIBS=$(find ${QPID_HOME} -type d -name "classes" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D')
+QPID_LIBS=${QPID_LIBS}:$(find $(dirname ${QPID_HOME}) -name "*.jar" -print | sed -e :a -e '$!N;s/\n/:/;ta' -e 'P;D')
+
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ CLASSPATH=$QPID_LIBS
diff --git a/Final/java/common/pom.xml b/Final/java/common/pom.xml
new file mode 100644
index 0000000000..b08982fde3
--- /dev/null
+++ b/Final/java/common/pom.xml
@@ -0,0 +1,144 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<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-common</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Common Utilities</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <gentools.home>${topDirectoryLocation}/../gentools</gentools.home>
+ <generated.path>${project.build.directory}/generated-sources/gentools</generated.path>
+ <generated.package>org/apache/qpid/framing</generated.package>
+ <generated.dir>${generated.path}/${generated.package}</generated.dir>
+ <generated.timestamp>${generated.dir}/timestamp</generated.timestamp>
+ <specs.dir>${topDirectoryLocation}/../specs</specs.dir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>protocol-version</id>
+ <phase>generate-sources</phase>
+ <configuration>
+ <tasks>
+ <ant antfile="protocol-version.xml" />
+ </tasks>
+ <sourceRoot>${generated.path}</sourceRoot>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </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-common</id>
+ <goals>
+ <goal>translate-project</goal>
+ </goals>
+ <configuration>
+ <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>
+
+ </plugins>
+ </build>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-java5</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-filter-ssl</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- This needs to be included at compile time, for the retrotranslator verification to find it. -->
+ <dependency>
+ <groupId>net.sf.retrotranslator</groupId>
+ <artifactId>retrotranslator-runtime</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+</project>
diff --git a/Final/java/common/protocol-version.xml b/Final/java/common/protocol-version.xml
new file mode 100644
index 0000000000..40331a8a84
--- /dev/null
+++ b/Final/java/common/protocol-version.xml
@@ -0,0 +1,60 @@
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT 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 name="Qpid Common Protocol Versions" default="generate">
+ <property name="topDirectoryLocation" location=".." />
+ <property name="project.build.directory" location="target" />
+ <property name="gentools.home" location="${topDirectoryLocation}/../gentools" />
+ <property name="generated.path" location="${project.build.directory}/generated-sources/gentools" />
+ <property name="generated.package" value="org/apache/qpid/framing" />
+ <property name="generated.dir" location="${generated.path}/${generated.package}" />
+ <property name="generated.timestamp" location="${generated.dir}/timestamp" />
+ <property name="xml.spec.dir" location="${topDirectoryLocation}/../specs" />
+ <property name="xml.spec.deps" value="amqp.0-8.xml cluster.0-8.xml" />
+ <property name="xml.spec.list" value="${xml.spec.dir}/amqp.0-8.xml ${xml.spec.dir}/cluster.0-8.xml" />
+
+ <target name="generate" depends="compile_generator,check_generate_deps" unless="generation.notRequired">
+ <mkdir dir="${generated.dir}"/>
+ <java classname="org.apache.qpid.gentools.Main" fork="true" dir="${gentools.home}/src" failonerror="true">
+ <arg line="-j -o ${generated.dir} -t ${gentools.home}/templ.java ${xml.spec.list}" />
+ <classpath>
+ <pathelement path="${gentools.home}/src" />
+ </classpath>
+ </java>
+ <touch file="${generated.timestamp}" />
+ </target>
+
+ <target name="check_generate_deps">
+ <uptodate property="generation.notRequired" targetfile="${generated.timestamp}">
+ <srcfiles dir="${xml.spec.dir}" includes="${xml.spec.deps}" />
+ </uptodate>
+ </target>
+
+ <target name="compile_generator">
+ <ant dir="${gentools.home}" />
+ </target>
+
+ <target name="precompile" depends="generate"/>
+
+ <target name="clean">
+ <delete dir="${generated.path}" />
+ </target>
+
+</project>
diff --git a/Final/java/common/readme.txt b/Final/java/common/readme.txt
new file mode 100644
index 0000000000..12841fa08d
--- /dev/null
+++ b/Final/java/common/readme.txt
@@ -0,0 +1,4 @@
+AMQP Common Java API
+
+Common generated functionality for AMQP Java client and broker. See the
+readme in the client and broker directories.
diff --git a/Final/java/common/src/main/java/log4j.properties b/Final/java/common/src/main/java/log4j.properties
new file mode 100644
index 0000000000..6d596d1d19
--- /dev/null
+++ b/Final/java/common/src/main/java/log4j.properties
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootLogger=${root.logging.level}
+
+
+log4j.logger.org.apache.qpid=${amqj.logging.level}, console
+log4j.additivity.org.apache.qpid=false
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+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/Final/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java
new file mode 100644
index 0000000000..251e91c1b9
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQChannelClosedException indicates that an operation cannot be performed becauase a channel has been closed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a failed operation on a closed channel.
+ * </table>
+ *
+ * @todo Does this duplicate AMQChannelException?
+ */
+public class AMQChannelClosedException extends AMQException
+{
+ public AMQChannelClosedException(AMQConstant errorCode, String msg)
+ {
+ super(errorCode, msg);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQChannelException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelException.java
new file mode 100644
index 0000000000..9efd271e4d
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQChannelException.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;
+
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQChannelException indicates that an error that requires the channel to be closed has occurred.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an error that rquires the channel to be closed.
+ * </table>
+ *
+ * @todo Does this duplicate AMQChannelClosedException?
+ */
+public class AMQChannelException extends AMQException
+{
+ private final int _classId;
+ private final int _methodId;
+ /* AMQP version for which exception ocurred */
+ private final byte major;
+ private final byte minor;
+
+ public AMQChannelException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor, Throwable t)
+ {
+ super(errorCode, msg, t);
+ _classId = classId;
+ _methodId = methodId;
+ this.major = major;
+ this.minor = minor;
+ }
+
+ public AMQChannelException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor)
+ {
+ super(errorCode, msg);
+ _classId = classId;
+ _methodId = methodId;
+ this.major = major;
+ this.minor = minor;
+ }
+
+ public AMQFrame getCloseFrame(int channel)
+ {
+ return ChannelCloseBody.createAMQFrame(channel, major, minor, _classId, _methodId, getErrorCode().getCode(), new AMQShortString(getMessage()));
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java
new file mode 100644
index 0000000000..eb736d437f
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid;
+
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQConnectionClosedException indicates that a connection has been closed.
+ *
+ * <p/>This exception is really used as an event, in order that the method handler that raises it creates an event
+ * which is propagated to the io handler, in order to notify it of the connection closure.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a the closure of a connection.
+ * </table>
+ *
+ * @todo Should review where exceptions-as-events
+ */
+public class AMQConnectionClosedException extends AMQException
+{
+ public AMQConnectionClosedException(AMQConstant errorCode, String msg)
+ {
+ super(errorCode, msg);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java
new file mode 100644
index 0000000000..7edfa648ed
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionException.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;
+
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQConnectionException indicates that an error that requires the channel to be closed has occurred.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an error that rquires the channel to be closed.
+ * </table>
+ *
+ * @todo Does this duplicate AMQChannelClosedException?
+ */
+public class AMQConnectionException extends AMQException
+{
+ private final int _classId;
+ private final int _methodId;
+ /* AMQP version for which exception ocurred */
+ private final byte major;
+ private final byte minor;
+ boolean _closeConnetion;
+
+ public AMQConnectionException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor,
+ Throwable t)
+ {
+ super(errorCode, msg, t);
+ _classId = classId;
+ _methodId = methodId;
+ this.major = major;
+ this.minor = minor;
+ }
+
+ public AMQConnectionException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor)
+ {
+ super(errorCode, msg);
+ _classId = classId;
+ _methodId = methodId;
+ this.major = major;
+ this.minor = minor;
+ }
+
+ public AMQFrame getCloseFrame(int channel)
+ {
+ return ConnectionCloseBody.createAMQFrame(channel, major, minor, _classId, _methodId, getErrorCode().getCode(),
+ new AMQShortString(getMessage()));
+ }
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java
new file mode 100644
index 0000000000..f78307d16f
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid;
+
+/**
+ * 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);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java
new file mode 100644
index 0000000000..e62b2c10a2
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid;
+
+/**
+ * AMQDisconnectedException indicates that a broker disconnected without failover.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents disconnection without failover by the broker.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQDisconnectedException extends AMQException
+{
+ public AMQDisconnectedException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQException.java
new file mode 100644
index 0000000000..41599ed880
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQException.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid;
+
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQException forms the root exception of all exceptions relating to the AMQ protocol. It provides space to associate
+ * a required AMQ error code with the exception, which is a numeric value, with a meaning defined by the protocol.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an exception condition associated with an AMQ protocol status code.
+ * </table>
+ *
+ * @todo This exception class is also used as a generic exception throughout Qpid code. This usage may not be strictly
+ * correct if this is to signify a protocol exception. Should review.
+ */
+public class AMQException extends Exception
+{
+ /** Holds the AMQ error code constant associated with this exception. */
+ private AMQConstant _errorCode;
+
+ /**
+ * Creates an exception with an optional error code, optional message and optional underlying cause.
+ *
+ * @param errorCode The error code. May be null if not to be set.
+ * @param msg The exception message. May be null if not to be set.
+ * @param t The underlying cause of the exception. May be null if not to be set.
+ */
+ public AMQException(AMQConstant errorCode, String msg, Throwable t)
+ {
+ super(((msg == null) ? "" : msg) + ((errorCode == null) ? "" : (" [error code " + errorCode + "]")), t);
+ _errorCode = errorCode;
+ }
+
+ /**
+ * @param message
+ *
+ * @deprecated Use {@link #AMQException(org.apache.qpid.protocol.AMQConstant, String, Throwable)} instead.
+ */
+ public AMQException(String message)
+ {
+ super(message);
+ // fixme This method needs removed and all AMQExceptions need a valid error code
+ _errorCode = AMQConstant.getConstant(-1);
+ }
+
+ /**
+ * @param msg
+ * @param t
+ *
+ * @deprecated Use {@link #AMQException(org.apache.qpid.protocol.AMQConstant, String, Throwable)} instead.
+ */
+ public AMQException(String msg, Throwable t)
+ {
+ super(msg, t);
+ // fixme This method needs removed and all AMQExceptions need a valid error code
+ _errorCode = AMQConstant.getConstant(-1);
+ }
+
+ /**
+ * @param errorCode
+ * @param msg
+ *
+ * @deprecated Use {@link #AMQException(org.apache.qpid.protocol.AMQConstant, String, Throwable)} instead.
+ */
+ public AMQException(AMQConstant errorCode, String msg)
+ {
+ super(msg + " [error code " + errorCode + ']');
+ _errorCode = errorCode;
+ }
+
+ /**
+ * Gets the AMQ protocol exception code associated with this exception.
+ *
+ * @return The AMQ protocol exception code associated with this exception.
+ */
+ public AMQConstant getErrorCode()
+ {
+ return _errorCode;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
new file mode 100644
index 0000000000..278128f924
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQInvalidArgumentException indicates that an invalid argument has been passed to an AMQP method.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents an error due to an invalid argument being passed to an AMQP method.
+ * </table>
+ */
+public class AMQInvalidArgumentException extends AMQException
+{
+ public AMQInvalidArgumentException(String message)
+ {
+ super(AMQConstant.INVALID_ARGUMENT, message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java
new file mode 100644
index 0000000000..b5ec9845d6
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQInvalidRoutingKeyException indicates an error with a routing key having an invalid format.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a format error in a routing key.
+ * </table>
+ */
+public class AMQInvalidRoutingKeyException extends AMQException
+{
+ public AMQInvalidRoutingKeyException(String message)
+ {
+ super(AMQConstant.INVALID_ROUTING_KEY, message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java
new file mode 100644
index 0000000000..a0574efa72
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid;
+
+/**
+ * AMQPInvalidClassException indicates an error when trying to store an illegally typed argument in a field table.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents illegal argument type for field table values.
+ * </table>
+ *
+ * @todo Could just re-use an exising exception like IllegalArgumentException or ClassCastException.
+ */
+public class AMQPInvalidClassException extends RuntimeException
+{
+ public AMQPInvalidClassException(String s)
+ {
+ super(s);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java
new file mode 100644
index 0000000000..0f8d9c47db
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQTimeoutException indicates that an expected response from a broker took too long.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Indicates that an expected response from a broker took too long.
+ * </table>
+ */
+public class AMQTimeoutException extends AMQException
+{
+ public AMQTimeoutException(String message)
+ {
+ super(AMQConstant.REQUEST_TIMEOUT, message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java
new file mode 100644
index 0000000000..03220cc95e
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid;
+
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQUndeliveredException indicates that a message, marked immediate or mandatory, could not be delivered.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to delivery a message that must be delivered.
+ * </table>
+ */
+public class AMQUndeliveredException extends AMQException
+{
+ private Object _bounced;
+
+ public AMQUndeliveredException(AMQConstant errorCode, String msg, Object bounced)
+ {
+ super(errorCode, msg);
+
+ _bounced = bounced;
+ }
+
+ public Object getUndeliveredMessage()
+ {
+ return _bounced;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java b/Final/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java
new file mode 100644
index 0000000000..a1e7088817
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.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;
+
+/**
+ * AMQUnknownExchangeType represents coding error where unknown exchange type requested from exchange factory.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents unknown exchange type request.
+ * <tr><td>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Represent coding error, where unknown exchange type is requested by passing a string parameter. Use a type safe
+ * enum for the exchange type, or replace with IllegalArgumentException. Should be runtime.
+ */
+public class AMQUnknownExchangeType extends AMQException
+{
+ public AMQUnknownExchangeType(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java b/Final/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java
new file mode 100644
index 0000000000..6cc9c3fe00
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.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;
+
+/**
+ * AMQUnresolvedAddressException indicates failure to resolve a socket address.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failre to resolve a socket address.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Why replace java.nio.UnresolvedAddressException with this? This is checked, which may explain why, but it
+ * doesn't wrap the underlying exception.
+ */
+public class AMQUnresolvedAddressException extends AMQException
+{
+ String _broker;
+
+ public AMQUnresolvedAddressException(String message, String broker)
+ {
+ super(message);
+ _broker = broker;
+ }
+
+ public String toString()
+ {
+ return super.toString() + " Broker, \"" + _broker + "\"";
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java
new file mode 100644
index 0000000000..fa890d0ebb
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.codec;
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+/**
+ * AMQCodecFactory is a Mina codec factory. It supplies the encoders and decoders need to read and write the bytes to
+ * the wire.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations.
+ * <tr><td> Supply the protocol encoder. <td> {@link AMQEncoder}
+ * <tr><td> Supply the protocol decoder. <td> {@link AMQDecoder}
+ * </table>
+ */
+public class AMQCodecFactory implements ProtocolCodecFactory
+{
+ /** Holds the protocol encoder. */
+ private final AMQEncoder _encoder = new AMQEncoder();
+
+ /** Holds the protocol decoder. */
+ private final AMQDecoder _frameDecoder;
+
+ /**
+ * Creates a new codec factory, specifiying whether it is expected that the first frame of data should be an
+ * initiation. This is the case for the broker, which always expects to received the protocol initiation on a newly
+ * connected client.
+ *
+ * @param expectProtocolInitiation <tt>true</tt> if the first frame received is going to be a protocol initiation
+ * frame, <tt>false</tt> if it is going to be a standard AMQ data block.
+ */
+ public AMQCodecFactory(boolean expectProtocolInitiation)
+ {
+ _frameDecoder = new AMQDecoder(expectProtocolInitiation);
+ }
+
+ /**
+ * Gets the AMQP encoder.
+ *
+ * @return The AMQP encoder.
+ */
+ public ProtocolEncoder getEncoder()
+ {
+ return _encoder;
+ }
+
+ /**
+ * Gets the AMQP decoder.
+ *
+ * @return The AMQP decoder.
+ */
+ public ProtocolDecoder getDecoder()
+ {
+ return _frameDecoder;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java
new file mode 100644
index 0000000000..02ae3cb089
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java
@@ -0,0 +1,163 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.codec;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+import org.apache.qpid.framing.AMQDataBlockDecoder;
+import org.apache.qpid.framing.ProtocolInitiation;
+
+/**
+ * AMQDecoder delegates the decoding of AMQP either to a data block decoder, or in the case of new connections, to a
+ * protocol initiation decoder. It is a cumulative decoder, which means that it can accumulate data to decode in the
+ * buffer until there is enough data to decode.
+ *
+ * <p/>One instance of this class is created per session, so any changes or configuration done at run time to the
+ * decoder will only affect decoding of the protocol session data to which is it bound.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Delegate protocol initiation to its decoder. <td> {@link ProtocolInitiation.Decoder}
+ * <tr><td> Delegate AMQP data to its decoder. <td> {@link AMQDataBlockDecoder}
+ * <tr><td> Accept notification that protocol initiation has completed.
+ * </table>
+ *
+ * @todo If protocol initiation decoder not needed, then don't create it. Probably not a big deal, but it adds to the
+ * per-session overhead.
+ */
+public class AMQDecoder extends CumulativeProtocolDecoder
+{
+ /** Holds the 'normal' AMQP data decoder. */
+ private AMQDataBlockDecoder _dataBlockDecoder = new AMQDataBlockDecoder();
+
+ /** Holds the protocol initiation decoder. */
+ private ProtocolInitiation.Decoder _piDecoder = new ProtocolInitiation.Decoder();
+
+ /** Flag to indicate whether this decoder needs to handle protocol initiation. */
+ private boolean _expectProtocolInitiation;
+
+ /**
+ * Creates a new AMQP decoder.
+ *
+ * @param expectProtocolInitiation <tt>true</tt> if this decoder needs to handle protocol initiation.
+ */
+ public AMQDecoder(boolean expectProtocolInitiation)
+ {
+ _expectProtocolInitiation = expectProtocolInitiation;
+ }
+
+ /**
+ * Delegates decoding AMQP from the data buffer that Mina has retrieved from the wire, to the data or protocol
+ * intiation decoders.
+ *
+ * @param session The Mina session.
+ * @param in The raw byte buffer.
+ * @param out The Mina object output gatherer to write decoded objects to.
+ *
+ * @return <tt>true</tt> if the data was decoded, <tt>false<tt> if more is needed and the data should accumulate.
+ *
+ * @throws Exception If the data cannot be decoded for any reason.
+ */
+ protected boolean doDecode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception
+ {
+ if (_expectProtocolInitiation)
+ {
+ return doDecodePI(session, in, out);
+ }
+ else
+ {
+ return doDecodeDataBlock(session, in, out);
+ }
+ }
+
+ /**
+ * Decodes AMQP data, delegating the decoding to an {@link AMQDataBlockDecoder}.
+ *
+ * @param session The Mina session.
+ * @param in The raw byte buffer.
+ * @param out The Mina object output gatherer to write decoded objects to.
+ *
+ * @return <tt>true</tt> if the data was decoded, <tt>false<tt> if more is needed and the data should accumulate.
+ *
+ * @throws Exception If the data cannot be decoded for any reason.
+ */
+ protected boolean doDecodeDataBlock(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception
+ {
+ int pos = in.position();
+ boolean enoughData = _dataBlockDecoder.decodable(session, in);
+ in.position(pos);
+ if (!enoughData)
+ {
+ // returning false means it will leave the contents in the buffer and
+ // call us again when more data has been read
+ return false;
+ }
+ else
+ {
+ _dataBlockDecoder.decode(session, in, out);
+
+ return true;
+ }
+ }
+
+ /**
+ * Decodes an AMQP initiation, delegating the decoding to a {@link ProtocolInitiation.Decoder}.
+ *
+ * @param session The Mina session.
+ * @param in The raw byte buffer.
+ * @param out The Mina object output gatherer to write decoded objects to.
+ *
+ * @return <tt>true</tt> if the data was decoded, <tt>false<tt> if more is needed and the data should accumulate.
+ *
+ * @throws Exception If the data cannot be decoded for any reason.
+ */
+ private boolean doDecodePI(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception
+ {
+ boolean enoughData = _piDecoder.decodable(session, in);
+ if (!enoughData)
+ {
+ // returning false means it will leave the contents in the buffer and
+ // call us again when more data has been read
+ return false;
+ }
+ else
+ {
+ _piDecoder.decode(session, in, out);
+
+ return true;
+ }
+ }
+
+ /**
+ * Sets the protocol initation flag, that determines whether decoding is handled by the data decoder of the protocol
+ * initation decoder. This method is expected to be called with <tt>false</tt> once protocol initation completes.
+ *
+ * @param expectProtocolInitiation <tt>true</tt> to use the protocol initiation decoder, <tt>false</tt> to use the
+ * data decoder.
+ */
+ public void setExpectProtocolInitiation(boolean expectProtocolInitiation)
+ {
+ _expectProtocolInitiation = expectProtocolInitiation;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java
new file mode 100644
index 0000000000..53f48ae1c8
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.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.codec;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+import org.apache.qpid.framing.AMQDataBlockEncoder;
+
+/**
+ * AMQEncoder delegates encoding of AMQP to a data encoder.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Delegate AMQP encoding. <td> {@link AMQDataBlockEncoder}
+ * </table>
+ *
+ * @todo This class just delegates to another, so seems to be pointless. Unless it is going to handle some
+ * responsibilities in the future, then drop it.
+ */
+public class AMQEncoder implements ProtocolEncoder
+{
+ /** The data encoder that is delegated to. */
+ private AMQDataBlockEncoder _dataBlockEncoder = new AMQDataBlockEncoder();
+
+ /**
+ * Encodes AMQP.
+ *
+ * @param session The Mina session.
+ * @param message The data object to encode.
+ * @param out The Mina writer to output the raw byte data to.
+ *
+ * @throws Exception If the data cannot be encoded for any reason.
+ */
+ public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception
+ {
+ _dataBlockEncoder.encode(session, message, out);
+ }
+
+ /**
+ * Does nothing. Called by Mina to allow this to clean up resources when it is no longer needed.
+ *
+ * @param session The Mina session.
+ */
+ public void dispose(IoSession session)
+ { }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java b/Final/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java
new file mode 100644
index 0000000000..9ed915cc35
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.common;
+
+import org.apache.qpid.framing.AMQShortString;
+
+/**
+ * Specifies the different filter types for consumers that filter their messages.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent different consumer filter types.
+ * </table>
+ */
+public enum AMQPFilterTypes
+{
+ JMS_SELECTOR("x-filter-jms-selector"),
+ NO_CONSUME("x-filter-no-consume"),
+ AUTO_CLOSE("x-filter-auto-close");
+
+ /** The identifying string for the filter type. */
+ private final AMQShortString _value;
+
+ /**
+ * Creates a new filter type from its identifying string.
+ *
+ * @param value The identifying string.
+ */
+ AMQPFilterTypes(String value)
+ {
+ _value = new AMQShortString(value);
+ }
+
+ /**
+ * Gets the identifying string of the filter type.
+ *
+ * @return The identifying string of the filter type.
+ */
+ public AMQShortString getValue()
+ {
+ return _value;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java b/Final/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java
new file mode 100644
index 0000000000..67f16e6a87
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java
@@ -0,0 +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.common;
+
+/**
+ * Specifies the available client property types that different clients can use to identify themselves with.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Specify the available client property types.
+ * </table>
+ */
+public enum ClientProperties
+{
+ instance,
+ product,
+ version,
+ platform
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java b/Final/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java
new file mode 100644
index 0000000000..2c783aeaa4
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.common;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * QpidProperties captures the project name, version number, and source code repository revision number from a properties
+ * file which is generated as part of the build process. Normally, the name and version number are pulled from the module
+ * name and version number of the Maven build POM, but could come from other sources if the build system is changed. The
+ * idea behind this, is that every build has these values incorporated directly into its jar file, so that code in the
+ * wild can be identified, should its origination be forgotten.
+ *
+ * <p/>To get the build version of any Qpid code call the {@link #main} method. This version string is usually also
+ * printed to the console on broker start up.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><td>Load build versioning information into the runtime, for code identification purposes.
+ * </table>
+ *
+ * @todo Code to locate/load/log properties can be factored into a reusable properties utils class. Avoid having this
+ * same snippet of loading code scattered in many places.
+ *
+ * @todo Could also add a build number property for a sequential build number assigned by an automated build system, for
+ * build reproducability purposes.
+ */
+public class QpidProperties
+{
+ /** Used for debugging purposes. */
+ private static final Logger _logger = LoggerFactory.getLogger(QpidProperties.class);
+
+ /** The name of the version properties file to load from the class path. */
+ public static final String VERSION_RESOURCE = "qpidversion.properties";
+
+ /** Defines the name of the product property. */
+ public static final String PRODUCT_NAME_PROPERTY = "qpid.name";
+
+ /** Defines the name of the version property. */
+ public static final String RELEASE_VERSION_PROPERTY = "qpid.version";
+
+ /** Defines the name of the source code revision property. */
+ public static final String BUILD_VERSION_PROPERTY = "qpid.svnversion";
+
+ /** Defines the default value for all properties that cannot be loaded. */
+ private static final String DEFAULT = "unknown";
+
+ /** Holds the product name. */
+ private static String productName = DEFAULT;
+
+ /** Holds the product version. */
+ private static String releaseVersion = DEFAULT;
+
+ /** Holds the source code revision. */
+ private static String buildVersion = DEFAULT;
+
+ // Loads the values from the version properties file.
+ static
+ {
+ Properties props = new Properties();
+
+ try
+ {
+ InputStream propertyStream = QpidProperties.class.getClassLoader().getResourceAsStream(VERSION_RESOURCE);
+ if (propertyStream == null)
+ {
+ _logger.warn("Unable to find resource " + VERSION_RESOURCE + " from classloader");
+ }
+ else
+ {
+ props.load(propertyStream);
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Dumping QpidProperties");
+ for (Map.Entry<Object, Object> entry : props.entrySet())
+ {
+ _logger.debug("Property: " + entry.getKey() + " Value: " + entry.getValue());
+ }
+
+ _logger.debug("End of property dump");
+ }
+
+ productName = readPropertyValue(props, PRODUCT_NAME_PROPERTY);
+ releaseVersion = readPropertyValue(props, RELEASE_VERSION_PROPERTY);
+ buildVersion = readPropertyValue(props, BUILD_VERSION_PROPERTY);
+ }
+ }
+ catch (IOException e)
+ {
+ // Log a warning about this and leave the values initialized to unknown.
+ _logger.error("Could not load version.properties resource: " + e, e);
+ }
+ }
+
+ /**
+ * Gets the product name.
+ *
+ * @return The product name.
+ */
+ public static String getProductName()
+ {
+ return productName;
+ }
+
+ /**
+ * Gets the product version.
+ *
+ * @return The product version.
+ */
+ public static String getReleaseVersion()
+ {
+ return releaseVersion;
+ }
+
+ /**
+ * Gets the source code revision.
+ *
+ * @return The source code revision.
+ */
+ public static String getBuildVersion()
+ {
+ return buildVersion;
+ }
+
+ /**
+ * Extracts all of the version information as a printable string.
+ *
+ * @return All of the version information as a printable string.
+ */
+ public static String getVersionString()
+ {
+ return getProductName() + " - " + getReleaseVersion() + " build: " + getBuildVersion();
+ }
+
+ /**
+ * Helper method to extract a named property from properties.
+ *
+ * @param props The properties.
+ * @param propertyName The named property to extract.
+ *
+ * @return The extracted property or a default value if the properties do not contain the named property.
+ *
+ * @todo A bit pointless.
+ */
+ private static String readPropertyValue(Properties props, String propertyName)
+ {
+ String retVal = (String) props.get(propertyName);
+ if (retVal == null)
+ {
+ retVal = DEFAULT;
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Prints the versioning information to the console. This is extremely usefull for identifying Qpid code in the
+ * wild, where the origination of the code has been forgotten.
+ *
+ * @param args Does not require any arguments.
+ */
+ public static void main(String[] args)
+ {
+ System.out.println(getVersionString());
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/configuration/Configured.java b/Final/java/common/src/main/java/org/apache/qpid/configuration/Configured.java
new file mode 100644
index 0000000000..22903888fe
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/configuration/Configured.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.configuration;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a field as having a "configured" value injected into it by a configurator.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Configured
+{
+ /**
+ * The Commons Configuration path to the configuration element
+ */
+ String path();
+
+ /**
+ * The default value to use should the path not be found in the configuration source
+ */
+ String defaultValue();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java
new file mode 100644
index 0000000000..1e5cc57fff
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.configuration;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * Indicates a failure to parse a property expansion. See {@link PropertyUtils} for the code that does property
+ * expansions.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaboration
+ * <tr><td> Represent failure to expand a property name into a value.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class PropertyException extends AMQException
+{
+ public PropertyException(String message)
+ {
+ super(message);
+ }
+
+ /*
+ public PropertyException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+
+ public PropertyException(AMQConstant errorCode, String msg, Throwable t)
+ {
+ super(errorCode, msg, t);
+ }
+
+ public PropertyException(AMQConstant errorCode, String msg)
+ {
+ super(errorCode, msg);
+ }
+ */
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java
new file mode 100644
index 0000000000..b3c310d23c
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java
@@ -0,0 +1,164 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.configuration;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * PropertyUtils provides helper methods for dealing with Java properties.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Expand system properties into strings with named expansions.
+ * </table>
+ *
+ * @todo Make the lookup method generic by passing in the properties to use for the expansion, rather than hard coding
+ * as system properties. The expansion code has greater potential for re-use that way.
+ *
+ * @todo Some more property related code could be added to this utils class, which might more appropriately reside under
+ * org.apache.qpid.util. For example standardised code to load properties from a resource name, currently found in
+ * QpidProperties and possibly other places could be moved here.
+ */
+public class PropertyUtils
+{
+ /**
+ * Given a string that contains substrings of the form <code>${xxx}</code>, looks up the valuea of 'xxx' as a
+ * system properties and substitutes tham back into the original string, to provide a property value expanded
+ * string.
+ *
+ * @param value The string to be scanned for property references. May be <code>null</code>, in which case this
+ * method returns immediately with no effect.
+ *
+ * @return The original string with the properties replaced, or <code>null</code> if the original string is
+ * <code>null</code>.
+ *
+ * @throws PropertyException If the string contains an opening <code>${</code> without a balancing <code>}</code>,
+ * or if the property to expand does not exist as a system property.
+ */
+ public static String replaceProperties(String value) throws PropertyException
+ {
+ if (value == null)
+ {
+ return null;
+ }
+
+ ArrayList<String> fragments = new ArrayList<String>();
+ ArrayList<String> propertyRefs = new ArrayList<String>();
+ parsePropertyString(value, fragments, propertyRefs);
+
+ StringBuffer sb = new StringBuffer();
+ Iterator j = propertyRefs.iterator();
+
+ for (String fragment : fragments)
+ {
+ if (fragment == null)
+ {
+ String propertyName = (String) j.next();
+
+ // try to get it from the project or keys
+ // Backward compatibility
+ String replacement = System.getProperty(propertyName);
+
+ if (replacement == null)
+ {
+ throw new PropertyException("Property ${" + propertyName + "} has not been set");
+ }
+
+ fragment = replacement;
+ }
+
+ sb.append(fragment);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Parses the supplied value for properties which are specified using ${foo} syntax. $X is left as is, and $$
+ * specifies a single $.
+ *
+ * @param value The property string to parse.
+ * @param fragments Is populated with the string fragments. A null means "insert a property value here. The number
+ * of nulls in the list when populated is equal to the size of the propertyRefs list.
+ * @param propertyRefs Populated with the property names to be added into the final string.
+ */
+ private static void parsePropertyString(String value, ArrayList<String> fragments, ArrayList<String> propertyRefs)
+ throws PropertyException
+ {
+ int prev = 0;
+ int pos;
+ // search for the next instance of $ from the 'prev' position
+ while ((pos = value.indexOf("$", prev)) >= 0)
+ {
+
+ // if there was any text before this, add it as a fragment
+ if (pos > 0)
+ {
+ fragments.add(value.substring(prev, pos));
+ }
+ // if we are at the end of the string, we tack on a $
+ // then move past it
+ if (pos == (value.length() - 1))
+ {
+ fragments.add("$");
+ prev = pos + 1;
+ }
+ else if (value.charAt(pos + 1) != '{')
+ {
+ // peek ahead to see if the next char is a property or not
+ // not a property: insert the char as a literal
+ if (value.charAt(pos + 1) == '$')
+ {
+ // two $ map to one $
+ fragments.add("$");
+ prev = pos + 2;
+ }
+ else
+ {
+ // $X maps to $X for all values of X!='$'
+ fragments.add(value.substring(pos, pos + 2));
+ prev = pos + 2;
+ }
+ }
+ else
+ {
+ // property found, extract its name or bail on a typo
+ int endName = value.indexOf('}', pos);
+ if (endName < 0)
+ {
+ throw new PropertyException("Syntax error in property: " + value);
+ }
+
+ String propertyName = value.substring(pos + 2, endName);
+ fragments.add(null);
+ propertyRefs.add(propertyName);
+ prev = endName + 1;
+ }
+ }
+ // no more $ signs found
+ // if there is any tail to the file, append it
+ if (prev < value.length())
+ {
+ fragments.add(value.substring(prev));
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java b/Final/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java
new file mode 100644
index 0000000000..123901b577
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.exchange;
+
+import org.apache.qpid.framing.AMQShortString;
+
+/**
+ * Defines the names of the standard AMQP exchanges that every AMQP broker should provide. These exchange names
+ * and type are given in the specification.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Defines the standard AMQP exchange names.
+ * <tr><td> Defines the standard AMQP exchange types.
+ * </table>
+ *
+ * @todo A type safe enum, might be more appropriate for the exchange types.
+ */
+public class ExchangeDefaults
+{
+ /** The default direct exchange, which is a special internal exchange that cannot be explicitly bound to. */
+ public static final AMQShortString DEFAULT_EXCHANGE_NAME = new AMQShortString("<<default>>");
+
+ /** The pre-defined topic exchange, the broker SHOULD provide this. */
+ public static final AMQShortString TOPIC_EXCHANGE_NAME = new AMQShortString("amq.topic");
+
+ /** Defines the identifying type name of topic exchanges. */
+ public static final AMQShortString TOPIC_EXCHANGE_CLASS = new AMQShortString("topic");
+
+ /** The pre-defined direct exchange, the broker MUST provide this. */
+ public static final AMQShortString DIRECT_EXCHANGE_NAME = new AMQShortString("amq.direct");
+
+ /** Defines the identifying type name of direct exchanges. */
+ public static final AMQShortString DIRECT_EXCHANGE_CLASS = new AMQShortString("direct");
+
+ /** The pre-defined headers exchange, the specification does not say this needs to be provided. */
+ public static final AMQShortString HEADERS_EXCHANGE_NAME = new AMQShortString("amq.match");
+
+ /** Defines the identifying type name of headers exchanges. */
+ public static final AMQShortString HEADERS_EXCHANGE_CLASS = new AMQShortString("headers");
+
+ /** The pre-defined fanout exchange, the boker MUST provide this. */
+ public static final AMQShortString FANOUT_EXCHANGE_NAME = new AMQShortString("amq.fanout");
+
+ /** Defines the identifying type name of fanout exchanges. */
+ public static final AMQShortString FANOUT_EXCHANGE_CLASS = new AMQShortString("fanout");
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java
new file mode 100644
index 0000000000..ebeea8d2b4
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public abstract class AMQBody
+{
+ public abstract byte getFrameType();
+
+ /**
+ * Get the size of the body
+ * @return unsigned short
+ */
+ protected abstract int getSize();
+
+ protected abstract void writePayload(ByteBuffer buffer);
+
+ protected abstract void populateFromBuffer(ByteBuffer buffer, long size)
+ throws AMQFrameDecodingException, AMQProtocolVersionException;
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java
new file mode 100644
index 0000000000..903b5bfa7a
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+/**
+ * A data block represents something that has a size in bytes and the ability to write itself to a byte
+ * buffer (similar to a byte array).
+ */
+public abstract class AMQDataBlock implements EncodableAMQDataBlock
+{
+ /**
+ * Get the size of buffer needed to store the byte representation of this
+ * frame.
+ * @return unsigned integer
+ */
+ public abstract long getSize();
+
+ /**
+ * Writes the datablock to the specified buffer.
+ * @param buffer
+ */
+ public abstract void writePayload(ByteBuffer buffer);
+
+ public ByteBuffer toByteBuffer()
+ {
+ final ByteBuffer buffer = ByteBuffer.allocate((int)getSize());
+
+ writePayload(buffer);
+ buffer.flip();
+ return buffer;
+ }
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java
new file mode 100644
index 0000000000..82ffc60802
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java
@@ -0,0 +1,120 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AMQDataBlockDecoder
+{
+ private static final String SESSION_METHOD_BODY_FACTORY = "QPID_SESSION_METHOD_BODY_FACTORY";
+
+ private static final BodyFactory[] _bodiesSupported = new BodyFactory[Byte.MAX_VALUE];
+
+ static
+ {
+ _bodiesSupported[ContentHeaderBody.TYPE] = ContentHeaderBodyFactory.getInstance();
+ _bodiesSupported[ContentBody.TYPE] = ContentBodyFactory.getInstance();
+ _bodiesSupported[HeartbeatBody.TYPE] = new HeartbeatBodyFactory();
+ }
+
+ Logger _logger = LoggerFactory.getLogger(AMQDataBlockDecoder.class);
+
+ public AMQDataBlockDecoder()
+ { }
+
+ public boolean decodable(IoSession session, ByteBuffer in) throws AMQFrameDecodingException
+ {
+ final int remainingAfterAttributes = in.remaining() - (1 + 2 + 4 + 1);
+ // type, channel, body length and end byte
+ if (remainingAfterAttributes < 0)
+ {
+ return false;
+ }
+
+ in.skip(1 + 2);
+ final long bodySize = in.getUnsignedInt();
+
+ return (remainingAfterAttributes >= bodySize);
+
+ }
+
+ protected Object createAndPopulateFrame(IoSession session, ByteBuffer in)
+ throws AMQFrameDecodingException, AMQProtocolVersionException
+ {
+ final byte type = in.get();
+
+ BodyFactory bodyFactory;
+ if (type == AMQMethodBody.TYPE)
+ {
+ bodyFactory = (BodyFactory) session.getAttribute(SESSION_METHOD_BODY_FACTORY);
+ if (bodyFactory == null)
+ {
+ AMQVersionAwareProtocolSession protocolSession = (AMQVersionAwareProtocolSession) session.getAttachment();
+ bodyFactory = new AMQMethodBodyFactory(protocolSession);
+ session.setAttribute(SESSION_METHOD_BODY_FACTORY, bodyFactory);
+
+ }
+
+ }
+ else
+ {
+ bodyFactory = _bodiesSupported[type];
+ }
+
+ if (bodyFactory == null)
+ {
+ throw new AMQFrameDecodingException(null, "Unsupported frame type: " + type, null);
+ }
+
+ final int channel = in.getUnsignedShort();
+ final long bodySize = in.getUnsignedInt();
+
+ // bodySize can be zero
+ if ((channel < 0) || (bodySize < 0))
+ {
+ throw new AMQFrameDecodingException(null, "Undecodable frame: type = " + type + " channel = " + channel
+ + " bodySize = " + bodySize, null);
+ }
+
+ AMQFrame frame = new AMQFrame(in, channel, bodySize, bodyFactory);
+
+ byte marker = in.get();
+ if ((marker & 0xFF) != 0xCE)
+ {
+ throw new AMQFrameDecodingException(null, "End of frame marker not found. Read " + marker + " length=" + bodySize
+ + " type=" + type, null);
+ }
+
+ return frame;
+ }
+
+ public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception
+ {
+ out.write(createAndPopulateFrame(session, in));
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java
new file mode 100644
index 0000000000..05fd2bb480
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+import org.apache.mina.filter.codec.demux.MessageEncoder;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Set;
+
+public final class AMQDataBlockEncoder implements MessageEncoder
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AMQDataBlockEncoder.class);
+
+ private final Set _messageTypes = Collections.singleton(EncodableAMQDataBlock.class);
+
+ public AMQDataBlockEncoder()
+ { }
+
+ public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception
+ {
+ final AMQDataBlock frame = (AMQDataBlock) message;
+
+ final ByteBuffer buffer = frame.toByteBuffer();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Encoded frame byte-buffer is '" + EncodingUtils.convertToHexString(buffer) + "'");
+ }
+
+ out.write(buffer);
+ }
+
+ public Set getMessageTypes()
+ {
+ return _messageTypes;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java
new file mode 100644
index 0000000000..11f505fd4b
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class AMQFrame extends AMQDataBlock implements EncodableAMQDataBlock
+{
+ private final int _channel;
+
+ private final AMQBody _bodyFrame;
+
+
+
+ public AMQFrame(final int channel, final AMQBody bodyFrame)
+ {
+ _channel = channel;
+ _bodyFrame = bodyFrame;
+ }
+
+ public AMQFrame(final ByteBuffer in, final int channel, final long bodySize, final BodyFactory bodyFactory) throws AMQFrameDecodingException
+ {
+ this._channel = channel;
+ this._bodyFrame = bodyFactory.createBody(in,bodySize);
+ }
+
+ public long getSize()
+ {
+ return 1 + 2 + 4 + _bodyFrame.getSize() + 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);
+ }
+
+ public final int getChannel()
+ {
+ return _channel;
+ }
+
+ public final AMQBody getBodyFrame()
+ {
+ return _bodyFrame;
+ }
+
+
+
+ public String toString()
+ {
+ return "Frame channelId: " + _channel + ", bodyFrame: " + String.valueOf(_bodyFrame);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java
new file mode 100644
index 0000000000..cd5ccf8e04
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * AMQFrameDecodingException indicates that an AMQP frame cannot be decoded because it does not have the correct
+ * format as defined by the protocol.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents a format error in a protocol frame.
+ * </table>
+ */
+public class AMQFrameDecodingException extends AMQException
+{
+ public AMQFrameDecodingException(AMQConstant errorCode, String message, Throwable t)
+ {
+ super(errorCode, message, t);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java
new file mode 100644
index 0000000000..f2e91083ca
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.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.framing;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.AMQChannelException;
+import org.apache.qpid.AMQConnectionException;
+import org.apache.qpid.protocol.AMQConstant;
+
+public abstract class AMQMethodBody extends AMQBody
+{
+ public static final byte TYPE = 1;
+
+ /** AMQP version */
+ protected byte major;
+ protected byte minor;
+
+ public byte getMajor()
+ {
+ return major;
+ }
+
+ public byte getMinor()
+ {
+ return minor;
+ }
+
+ public AMQMethodBody(byte major, byte minor)
+ {
+ this.major = major;
+ this.minor = minor;
+ }
+
+ /** unsigned short */
+ protected abstract int getBodySize();
+
+ /** @return unsigned short */
+ protected abstract int getClazz();
+
+ /** @return unsigned short */
+ protected abstract int getMethod();
+
+ protected abstract void writeMethodPayload(ByteBuffer buffer);
+
+ public byte getFrameType()
+ {
+ return TYPE;
+ }
+
+ protected int getSize()
+ {
+ return 2 + 2 + getBodySize();
+ }
+
+ protected void writePayload(ByteBuffer buffer)
+ {
+ EncodingUtils.writeUnsignedShort(buffer, getClazz());
+ EncodingUtils.writeUnsignedShort(buffer, getMethod());
+ writeMethodPayload(buffer);
+ }
+
+ protected abstract void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException;
+
+ protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException
+ {
+ populateMethodBodyFromBuffer(buffer);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append("[ Class: ").append(getClazz());
+ buf.append(" Method: ").append(getMethod()).append(']');
+ return buf.toString();
+ }
+
+ /**
+ * Creates an AMQChannelException for the corresponding body type (a channel exception should include the class and
+ * method ids of the body it resulted from).
+ */
+
+ /**
+ * Convenience Method to create a channel not found exception
+ *
+ * @param channelId The channel id that is not found
+ *
+ * @return new AMQChannelException
+ */
+ public AMQChannelException getChannelNotFoundException(int channelId)
+ {
+ return getChannelException(AMQConstant.NOT_FOUND, "Channel not found for id:" + channelId);
+ }
+
+ public AMQChannelException getChannelException(AMQConstant code, String message)
+ {
+ return new AMQChannelException(code, message, getClazz(), getMethod(), major, minor);
+ }
+
+ public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause)
+ {
+ return new AMQChannelException(code, message, getClazz(), getMethod(), major, minor, cause);
+ }
+
+ public AMQConnectionException getConnectionException(AMQConstant code, String message)
+ {
+ return new AMQConnectionException(code, message, getClazz(), getMethod(), major, minor);
+ }
+
+ public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause)
+ {
+ return new AMQConnectionException(code, message, getClazz(), getMethod(), major, minor, cause);
+ }
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java
new file mode 100644
index 0000000000..cf85bdab31
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AMQMethodBodyFactory implements BodyFactory
+{
+ private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class);
+
+ private final AMQVersionAwareProtocolSession _protocolSession;
+
+ public AMQMethodBodyFactory(AMQVersionAwareProtocolSession protocolSession)
+ {
+ _protocolSession = protocolSession;
+ }
+
+ public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException
+ {
+ return _protocolSession.getRegistry().get((short) in.getUnsignedShort(), (short) in.getUnsignedShort(), in,
+ bodySize);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java
new file mode 100644
index 0000000000..359efe7eb7
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+
+public abstract interface AMQMethodBodyInstanceFactory
+{
+ public AMQMethodBody newInstance(byte major, byte minor, ByteBuffer buffer, long size) throws AMQFrameDecodingException;
+ public AMQMethodBody newInstance(byte major, byte minor, int clazzID, int methodID, ByteBuffer buffer, long size) throws AMQFrameDecodingException;
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java
new file mode 100644
index 0000000000..e48fd2e7f9
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQProtocolInstanceException indicates that the protocol class is incorrect in a header.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent incorrect protocol class in frame header.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQProtocolClassException extends AMQProtocolHeaderException
+{
+ public AMQProtocolClassException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java
new file mode 100644
index 0000000000..1ce49aba83
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.qpid.AMQException;
+
+/**
+ * AMQProtocolHeaderException indicates a format error in an AMQP frame header.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent format error in frame header.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQProtocolHeaderException extends AMQException
+{
+ public AMQProtocolHeaderException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java
new file mode 100644
index 0000000000..9049eace2a
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQProtocolInstanceException indicates that the protocol instance is incorrect in a header.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent incorrect protocol instance in frame header.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQProtocolInstanceException extends AMQProtocolHeaderException
+{
+ public AMQProtocolInstanceException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java
new file mode 100644
index 0000000000..9074931617
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * AMQProtocolInstanceException indicates that the client and server differ on expected protocol version in a header.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent incorrect protocol version in frame header.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ */
+public class AMQProtocolVersionException extends AMQProtocolHeaderException
+{
+ public AMQProtocolVersionException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
new file mode 100644
index 0000000000..ec501951af
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
@@ -0,0 +1,436 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 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;
+
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQType.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQType.java
new file mode 100644
index 0000000000..6dda91a488
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQType.java
@@ -0,0 +1,701 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public enum AMQType
+{
+ //AMQP FieldTable Wire Types
+
+ LONG_STRING('S')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongStringLength((String) value);
+ }
+
+
+ public String toNativeValue(Object value)
+ {
+ if (value != null)
+ {
+ return value.toString();
+ }
+ else
+ {
+ throw new NullPointerException("Cannot convert: null to String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongStringBytes(buffer, (String) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongString(buffer);
+ }
+
+ },
+
+ INTEGER('i')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.unsignedIntegerLength();
+ }
+
+ public Long toNativeValue(Object value)
+ {
+ if (value instanceof Long)
+ {
+ return (Long) value;
+ }
+ else if (value instanceof Integer)
+ {
+ return ((Integer) value).longValue();
+ }
+ else if (value instanceof Short)
+ {
+ return ((Short) value).longValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).longValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Long.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to int.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeUnsignedInteger(buffer, (Long) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readUnsignedInteger(buffer);
+ }
+ },
+
+ DECIMAL('D')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedByteLength()+ EncodingUtils.encodedIntegerLength();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ if(value instanceof BigDecimal)
+ {
+ return (BigDecimal) value;
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to BigDecimal.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ BigDecimal bd = (BigDecimal) value;
+
+ byte places = new Integer(bd.scale()).byteValue();
+
+ int unscaled = bd.intValue();
+
+ EncodingUtils.writeByte(buffer, places);
+
+ EncodingUtils.writeInteger(buffer, unscaled);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ byte places = EncodingUtils.readByte(buffer);
+
+ int unscaled = EncodingUtils.readInteger(buffer);
+
+ BigDecimal bd = new BigDecimal(unscaled);
+ return bd.setScale(places);
+ }
+ },
+
+ TIMESTAMP('T')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongLength();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ if(value instanceof Long)
+ {
+ return (Long) value;
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to timestamp.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLong(buffer, (Long) value);
+ }
+
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLong(buffer);
+ }
+ },
+
+ FIELD_TABLE('F')
+ {
+ public int getEncodingSize(Object value)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ // TODO : fixme
+ throw new UnsupportedOperationException();
+ }
+ },
+
+ VOID('V')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return 0;
+ }
+
+
+ public Object toNativeValue(Object value)
+ {
+ if (value == null)
+ {
+ return null;
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to null String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return null;
+ }
+ },
+
+ // Extended types
+
+ BINARY('x')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongstrLength((byte[]) value);
+ }
+
+
+ public Object toNativeValue(Object value)
+ {
+ if((value instanceof byte[]) || (value == null))
+ {
+ return value;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName() +
+ ") cannot be converted to byte[]");
+ }
+ }
+
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongstr(buffer, (byte[]) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongstr(buffer);
+ }
+
+ },
+
+ ASCII_STRING('c')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongStringLength((String) value);
+ }
+
+
+ public String toNativeValue(Object value)
+ {
+ if (value != null)
+ {
+ return value.toString();
+ }
+ else
+ {
+ throw new NullPointerException("Cannot convert: null to String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongStringBytes(buffer, (String) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongString(buffer);
+ }
+
+ },
+
+ WIDE_STRING('C')
+ {
+ public int getEncodingSize(Object value)
+ {
+ // FIXME: use proper charset encoder
+ return EncodingUtils.encodedLongStringLength((String) value);
+ }
+
+
+ public String toNativeValue(Object value)
+ {
+ if (value != null)
+ {
+ return value.toString();
+ }
+ else
+ {
+ throw new NullPointerException("Cannot convert: null to String.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLongStringBytes(buffer, (String) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLongString(buffer);
+ }
+ },
+
+ BOOLEAN('t')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedBooleanLength();
+ }
+
+
+ public Object toNativeValue(Object value)
+ {
+ if (value instanceof Boolean)
+ {
+ return (Boolean) value;
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Boolean.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to boolean.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeBoolean(buffer, (Boolean) value);
+ }
+
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readBoolean(buffer);
+ }
+ },
+
+ ASCII_CHARACTER('k')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedCharLength();
+ }
+
+
+ public Character toNativeValue(Object value)
+ {
+ if (value instanceof Character)
+ {
+ return (Character) value;
+ }
+ else if (value == null)
+ {
+ throw new NullPointerException("Cannot convert null into char");
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to char.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeChar(buffer, (Character) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readChar(buffer);
+ }
+
+ },
+
+ BYTE('b')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedByteLength();
+ }
+
+
+ public Byte toNativeValue(Object value)
+ {
+ if (value instanceof Byte)
+ {
+ return (Byte) value;
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Byte.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to byte.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeByte(buffer, (Byte) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readByte(buffer);
+ }
+ },
+
+ SHORT('s')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedShortLength();
+ }
+
+
+ public Short toNativeValue(Object value)
+ {
+ if (value instanceof Short)
+ {
+ return (Short) value;
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).shortValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Short.valueOf((String)value);
+ }
+
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to short.");
+ }
+
+
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeShort(buffer, (Short) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readShort(buffer);
+ }
+ },
+
+ INT('I')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedIntegerLength();
+ }
+
+ public Integer toNativeValue(Object value)
+ {
+ if (value instanceof Integer)
+ {
+ return (Integer) value;
+ }
+ else if (value instanceof Short)
+ {
+ return ((Short) value).intValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).intValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Integer.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to int.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeInteger(buffer, (Integer) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readInteger(buffer);
+ }
+ },
+
+ LONG('l')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedLongLength();
+ }
+
+ public Object toNativeValue(Object value)
+ {
+ if(value instanceof Long)
+ {
+ return (Long) value;
+ }
+ else if (value instanceof Integer)
+ {
+ return ((Integer) value).longValue();
+ }
+ else if (value instanceof Short)
+ {
+ return ((Short) value).longValue();
+ }
+ else if (value instanceof Byte)
+ {
+ return ((Byte) value).longValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Long.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to long.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeLong(buffer, (Long) value);
+ }
+
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readLong(buffer);
+ }
+ },
+
+ FLOAT('f')
+ {
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedFloatLength();
+ }
+
+
+ public Float toNativeValue(Object value)
+ {
+ if (value instanceof Float)
+ {
+ return (Float) value;
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Float.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to float.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeFloat(buffer, (Float) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readFloat(buffer);
+ }
+ },
+
+ DOUBLE('d')
+ {
+
+ public int getEncodingSize(Object value)
+ {
+ return EncodingUtils.encodedDoubleLength();
+ }
+
+
+ public Double toNativeValue(Object value)
+ {
+ if (value instanceof Double)
+ {
+ return (Double) value;
+ }
+ else if (value instanceof Float)
+ {
+ return ((Float) value).doubleValue();
+ }
+ else if ((value instanceof String) || (value == null))
+ {
+ return Double.valueOf((String)value);
+ }
+ else
+ {
+ throw new NumberFormatException("Cannot convert: " + value + "(" +
+ value.getClass().getName() + ") to double.");
+ }
+ }
+
+ public void writeValueImpl(Object value, ByteBuffer buffer)
+ {
+ EncodingUtils.writeDouble(buffer, (Double) value);
+ }
+
+ public Object readValueFromBuffer(ByteBuffer buffer)
+ {
+ return EncodingUtils.readDouble(buffer);
+ }
+ };
+
+ private final byte _identifier;
+
+ AMQType(char identifier)
+ {
+ _identifier = (byte) identifier;
+ }
+
+ public final byte identifier()
+ {
+ return _identifier;
+ }
+
+
+ public abstract int getEncodingSize(Object value);
+
+ public abstract Object toNativeValue(Object value);
+
+ public AMQTypedValue asTypedValue(Object value)
+ {
+ return new AMQTypedValue(this, toNativeValue(value));
+ }
+
+ public void writeToBuffer(Object value, ByteBuffer buffer)
+ {
+ buffer.put((byte)identifier());
+ writeValueImpl(value, buffer);
+ }
+
+ abstract void writeValueImpl(Object value, ByteBuffer buffer);
+
+ abstract Object readValueFromBuffer(ByteBuffer buffer);
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java
new file mode 100644
index 0000000000..1419dd75b1
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AMQTypeMap
+{
+ public static Map<Byte, AMQType> _reverseTypeMap = new HashMap<Byte, AMQType>();
+
+ static
+ {
+ for(AMQType type : AMQType.values())
+ {
+ _reverseTypeMap.put(type.identifier(), type);
+ }
+ }
+
+ public static AMQType getType(Byte identifier)
+ {
+ AMQType result = _reverseTypeMap.get(identifier);
+ if (result == null) {
+ throw new IllegalArgumentException
+ ("no such type code: " + Integer.toHexString(identifier.intValue()));
+ }
+ return result;
+ }
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java
new file mode 100644
index 0000000000..7193580884
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class AMQTypedValue
+{
+ private final AMQType _type;
+ private final Object _value;
+
+
+ public AMQTypedValue(AMQType type, Object value)
+ {
+ if(type == null)
+ {
+ throw new NullPointerException("Cannot create a typed value with null type");
+ }
+ _type = type;
+ _value = type.toNativeValue(value);
+ }
+
+ private AMQTypedValue(AMQType type, ByteBuffer buffer)
+ {
+ _type = type;
+ _value = type.readValueFromBuffer( buffer );
+ }
+
+
+ public AMQType getType()
+ {
+ return _type;
+ }
+
+ public Object getValue()
+ {
+ return _value;
+ }
+
+
+ public void writeToBuffer(ByteBuffer buffer)
+ {
+ _type.writeToBuffer(_value,buffer);
+ }
+
+ public int getEncodingSize()
+ {
+ return _type.getEncodingSize(_value);
+ }
+
+ public static AMQTypedValue readFromBuffer(ByteBuffer buffer)
+ {
+ AMQType type = AMQTypeMap.getType(buffer.get());
+ return new AMQTypedValue(type, buffer);
+ }
+
+ public String toString()
+ {
+ return "["+getType()+": "+getValue()+"]";
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java b/Final/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java
new file mode 100644
index 0000000000..61f2f891e5
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java
@@ -0,0 +1,799 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+public class BasicContentHeaderProperties implements CommonContentHeaderProperties
+{
+ private static final Logger _logger = LoggerFactory.getLogger(BasicContentHeaderProperties.class);
+
+ private static final AMQShortString ZERO_STRING = null;
+
+ /**
+ * We store the encoded form when we decode the content header so that if we need to write it out without modifying
+ * it we can do so without incurring the expense of reencoding it
+ */
+ private byte[] _encodedForm;
+
+ /** Flag indicating whether the entire content header has been decoded yet */
+ private boolean _decoded = true;
+
+ /**
+ * We have some optimisations for partial decoding for maximum performance. The headers are used in the broker for
+ * routing in some cases so we can decode that separately.
+ */
+ private boolean _decodedHeaders = true;
+
+ /**
+ * We have some optimisations for partial decoding for maximum performance. The content type is used by all clients
+ * to determine the message type
+ */
+ private boolean _decodedContentType = true;
+
+ private AMQShortString _contentType;
+
+ private AMQShortString _encoding;
+
+ private FieldTable _headers;
+
+ private byte _deliveryMode;
+
+ private byte _priority;
+
+ private AMQShortString _correlationId;
+
+ private AMQShortString _replyTo;
+
+ private long _expiration;
+
+ private AMQShortString _messageId;
+
+ private long _timestamp;
+
+ private AMQShortString _type;
+
+ private AMQShortString _userId;
+
+ private AMQShortString _appId;
+
+ private AMQShortString _clusterId;
+
+ private int _propertyFlags = 0;
+ private static final int CONTENT_TYPE_MASK = 1 << 15;
+ private static final int ENCONDING_MASK = 1 << 14;
+ private static final int HEADERS_MASK = 1 << 13;
+ private static final int DELIVERY_MODE_MASK = 1 << 12;
+ private static final int PROPRITY_MASK = 1 << 11;
+ private static final int CORRELATION_ID_MASK = 1 << 10;
+ private static final int REPLY_TO_MASK = 1 << 9;
+ private static final int EXPIRATION_MASK = 1 << 8;
+ private static final int MESSAGE_ID_MASK = 1 << 7;
+ private static final int TIMESTAMP_MASK = 1 << 6;
+ private static final int TYPE_MASK = 1 << 5;
+ private static final int USER_ID_MASK = 1 << 4;
+ private static final int APPLICATION_ID_MASK = 1 << 3;
+ private static final int CLUSTER_ID_MASK = 1 << 2;
+
+ public BasicContentHeaderProperties()
+ { }
+
+ public int getPropertyListSize()
+ {
+ if (_encodedForm != null)
+ {
+ return _encodedForm.length;
+ }
+ else
+ {
+ int size = 0;
+
+ if ((_propertyFlags & (CONTENT_TYPE_MASK)) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_contentType);
+ }
+
+ if ((_propertyFlags & ENCONDING_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_encoding);
+ }
+
+ if ((_propertyFlags & HEADERS_MASK) > 0)
+ {
+ size += EncodingUtils.encodedFieldTableLength(_headers);
+ }
+
+ if ((_propertyFlags & DELIVERY_MODE_MASK) > 0)
+ {
+ size += 1;
+ }
+
+ if ((_propertyFlags & PROPRITY_MASK) > 0)
+ {
+ size += 1;
+ }
+
+ if ((_propertyFlags & CORRELATION_ID_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_correlationId);
+ }
+
+ if ((_propertyFlags & REPLY_TO_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_replyTo);
+ }
+
+ if ((_propertyFlags & EXPIRATION_MASK) > 0)
+ {
+ if (_expiration == 0L)
+ {
+ size += EncodingUtils.encodedShortStringLength(ZERO_STRING);
+ }
+ else
+ {
+ size += EncodingUtils.encodedShortStringLength(_expiration);
+ }
+ }
+
+ if ((_propertyFlags & MESSAGE_ID_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_messageId);
+ }
+
+ if ((_propertyFlags & TIMESTAMP_MASK) > 0)
+ {
+ size += 8;
+ }
+
+ if ((_propertyFlags & TYPE_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_type);
+ }
+
+ if ((_propertyFlags & USER_ID_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_userId);
+ }
+
+ if ((_propertyFlags & APPLICATION_ID_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_appId);
+ }
+
+ if ((_propertyFlags & CLUSTER_ID_MASK) > 0)
+ {
+ size += EncodingUtils.encodedShortStringLength(_clusterId);
+ }
+
+ return size;
+ }
+ }
+
+ private void clearEncodedForm()
+ {
+ if (!_decoded && (_encodedForm != null))
+ {
+ // decode();
+ }
+
+ _encodedForm = null;
+ }
+
+ public void setPropertyFlags(int propertyFlags)
+ {
+ clearEncodedForm();
+ _propertyFlags = propertyFlags;
+ }
+
+ public int getPropertyFlags()
+ {
+ return _propertyFlags;
+ }
+
+ public void writePropertyListPayload(ByteBuffer buffer)
+ {
+ if (_encodedForm != null)
+ {
+ buffer.put(_encodedForm);
+ }
+ else
+ {
+ if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _contentType);
+ }
+
+ if ((_propertyFlags & ENCONDING_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _encoding);
+ }
+
+ if ((_propertyFlags & HEADERS_MASK) != 0)
+ {
+ EncodingUtils.writeFieldTableBytes(buffer, _headers);
+ }
+
+ if ((_propertyFlags & DELIVERY_MODE_MASK) != 0)
+ {
+ buffer.put(_deliveryMode);
+ }
+
+ if ((_propertyFlags & PROPRITY_MASK) != 0)
+ {
+ buffer.put(_priority);
+ }
+
+ if ((_propertyFlags & CORRELATION_ID_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _correlationId);
+ }
+
+ if ((_propertyFlags & REPLY_TO_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _replyTo);
+ }
+
+ if ((_propertyFlags & EXPIRATION_MASK) != 0)
+ {
+ if (_expiration == 0L)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, ZERO_STRING);
+ }
+ else
+ {
+ EncodingUtils.writeShortStringBytes(buffer, String.valueOf(_expiration));
+ }
+ }
+
+ if ((_propertyFlags & MESSAGE_ID_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _messageId);
+ }
+
+ if ((_propertyFlags & TIMESTAMP_MASK) != 0)
+ {
+ EncodingUtils.writeTimestamp(buffer, _timestamp);
+ }
+
+ if ((_propertyFlags & TYPE_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _type);
+ }
+
+ if ((_propertyFlags & USER_ID_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _userId);
+ }
+
+ if ((_propertyFlags & APPLICATION_ID_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _appId);
+ }
+
+ if ((_propertyFlags & CLUSTER_ID_MASK) != 0)
+ {
+ EncodingUtils.writeShortStringBytes(buffer, _clusterId);
+ }
+ }
+ }
+
+ public void populatePropertiesFromBuffer(ByteBuffer buffer, int propertyFlags, int size) throws AMQFrameDecodingException
+ {
+ _propertyFlags = propertyFlags;
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Property flags: " + _propertyFlags);
+ }
+
+ decode(buffer);
+ /*_encodedForm = new byte[size];
+ buffer.get(_encodedForm, 0, size);
+ _decoded = false;
+ _decodedHeaders = false;
+ _decodedContentType = false;*/
+ }
+
+ private void decode(ByteBuffer buffer)
+ {
+ // ByteBuffer buffer = ByteBuffer.wrap(_encodedForm);
+ int pos = buffer.position();
+ try
+ {
+ if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0)
+ {
+ _contentType = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & ENCONDING_MASK) != 0)
+ {
+ _encoding = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & HEADERS_MASK) != 0)
+ {
+ _headers = EncodingUtils.readFieldTable(buffer);
+ }
+
+ if ((_propertyFlags & DELIVERY_MODE_MASK) != 0)
+ {
+ _deliveryMode = buffer.get();
+ }
+
+ if ((_propertyFlags & PROPRITY_MASK) != 0)
+ {
+ _priority = buffer.get();
+ }
+
+ if ((_propertyFlags & CORRELATION_ID_MASK) != 0)
+ {
+ _correlationId = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & REPLY_TO_MASK) != 0)
+ {
+ _replyTo = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & EXPIRATION_MASK) != 0)
+ {
+ _expiration = EncodingUtils.readLongAsShortString(buffer);
+ }
+
+ if ((_propertyFlags & MESSAGE_ID_MASK) != 0)
+ {
+ _messageId = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & TIMESTAMP_MASK) != 0)
+ {
+ _timestamp = EncodingUtils.readTimestamp(buffer);
+ }
+
+ if ((_propertyFlags & TYPE_MASK) != 0)
+ {
+ _type = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & USER_ID_MASK) != 0)
+ {
+ _userId = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & APPLICATION_ID_MASK) != 0)
+ {
+ _appId = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ if ((_propertyFlags & CLUSTER_ID_MASK) != 0)
+ {
+ _clusterId = EncodingUtils.readAMQShortString(buffer);
+ }
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ throw new RuntimeException("Error in content header data: " + e, e);
+ }
+
+ final int endPos = buffer.position();
+ buffer.position(pos);
+ final int len = endPos - pos;
+ _encodedForm = new byte[len];
+ final int limit = buffer.limit();
+ buffer.limit(endPos);
+ buffer.get(_encodedForm, 0, len);
+ buffer.limit(limit);
+ buffer.position(endPos);
+ _decoded = true;
+ }
+
+ private void decodeUpToHeaders()
+ {
+ ByteBuffer buffer = ByteBuffer.wrap(_encodedForm);
+ try
+ {
+ if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0)
+ {
+ byte length = buffer.get();
+ buffer.skip(length);
+ }
+
+ if ((_propertyFlags & ENCONDING_MASK) != 0)
+ {
+ byte length = buffer.get();
+ buffer.skip(length);
+ }
+
+ if ((_propertyFlags & HEADERS_MASK) != 0)
+ {
+ _headers = EncodingUtils.readFieldTable(buffer);
+
+ }
+
+ _decodedHeaders = true;
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ throw new RuntimeException("Error in content header data: " + e, e);
+ }
+ }
+
+ private void decodeUpToContentType()
+ {
+ ByteBuffer buffer = ByteBuffer.wrap(_encodedForm);
+
+ if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0)
+ {
+ _contentType = EncodingUtils.readAMQShortString(buffer);
+ }
+
+ _decodedContentType = true;
+ }
+
+ private void decodeIfNecessary()
+ {
+ if (!_decoded)
+ {
+ // decode();
+ }
+ }
+
+ private void decodeHeadersIfNecessary()
+ {
+ if (!_decoded && !_decodedHeaders)
+ {
+ decodeUpToHeaders();
+ }
+ }
+
+ private void decodeContentTypeIfNecessary()
+ {
+ if (!_decoded && !_decodedContentType)
+ {
+ decodeUpToContentType();
+ }
+ }
+
+ public AMQShortString getContentType()
+ {
+ decodeContentTypeIfNecessary();
+
+ return _contentType;
+ }
+
+ public String getContentTypeAsString()
+ {
+ decodeContentTypeIfNecessary();
+
+ return (_contentType == null) ? null : _contentType.toString();
+ }
+
+ public void setContentType(AMQShortString contentType)
+ {
+ clearEncodedForm();
+ _propertyFlags |= (CONTENT_TYPE_MASK);
+ _contentType = contentType;
+ }
+
+ public void setContentType(String contentType)
+ {
+ setContentType((contentType == null) ? null : new AMQShortString(contentType));
+ }
+
+ public String getEncodingAsString()
+ {
+
+ return (getEncoding() == null) ? null : getEncoding().toString();
+ }
+
+ public AMQShortString getEncoding()
+ {
+ decodeIfNecessary();
+
+ return _encoding;
+ }
+
+ public void setEncoding(String encoding)
+ {
+ clearEncodedForm();
+ _propertyFlags |= ENCONDING_MASK;
+ _encoding = (encoding == null) ? null : new AMQShortString(encoding);
+ }
+
+ public void setEncoding(AMQShortString encoding)
+ {
+ clearEncodedForm();
+ _propertyFlags |= ENCONDING_MASK;
+ _encoding = encoding;
+ }
+
+ public FieldTable getHeaders()
+ {
+ decodeHeadersIfNecessary();
+
+ if (_headers == null)
+ {
+ setHeaders(FieldTableFactory.newFieldTable());
+ }
+
+ return _headers;
+ }
+
+ public void setHeaders(FieldTable headers)
+ {
+ clearEncodedForm();
+ _propertyFlags |= HEADERS_MASK;
+ _headers = headers;
+ }
+
+ public byte getDeliveryMode()
+ {
+ decodeIfNecessary();
+
+ return _deliveryMode;
+ }
+
+ public void setDeliveryMode(byte deliveryMode)
+ {
+ clearEncodedForm();
+ _propertyFlags |= DELIVERY_MODE_MASK;
+ _deliveryMode = deliveryMode;
+ }
+
+ public byte getPriority()
+ {
+ decodeIfNecessary();
+
+ return _priority;
+ }
+
+ public void setPriority(byte priority)
+ {
+ clearEncodedForm();
+ _propertyFlags |= PROPRITY_MASK;
+ _priority = priority;
+ }
+
+ public AMQShortString getCorrelationId()
+ {
+ decodeIfNecessary();
+
+ return _correlationId;
+ }
+
+ public String getCorrelationIdAsString()
+ {
+ decodeIfNecessary();
+
+ return (_correlationId == null) ? null : _correlationId.toString();
+ }
+
+ public void setCorrelationId(String correlationId)
+ {
+ setCorrelationId((correlationId == null) ? null : new AMQShortString(correlationId));
+ }
+
+ public void setCorrelationId(AMQShortString correlationId)
+ {
+ clearEncodedForm();
+ _propertyFlags |= CORRELATION_ID_MASK;
+ _correlationId = correlationId;
+ }
+
+ public String getReplyToAsString()
+ {
+ decodeIfNecessary();
+
+ return (_replyTo == null) ? null : _replyTo.toString();
+ }
+
+ public AMQShortString getReplyTo()
+ {
+ decodeIfNecessary();
+
+ return _replyTo;
+ }
+
+ public void setReplyTo(String replyTo)
+ {
+ setReplyTo((replyTo == null) ? null : new AMQShortString(replyTo));
+ }
+
+ public void setReplyTo(AMQShortString replyTo)
+ {
+
+ clearEncodedForm();
+ _propertyFlags |= REPLY_TO_MASK;
+ _replyTo = replyTo;
+ }
+
+ public long getExpiration()
+ {
+ decodeIfNecessary();
+
+ return _expiration;
+ }
+
+ public void setExpiration(long expiration)
+ {
+ clearEncodedForm();
+ _propertyFlags |= EXPIRATION_MASK;
+ _expiration = expiration;
+ }
+
+ public AMQShortString getMessageId()
+ {
+ decodeIfNecessary();
+
+ return _messageId;
+ }
+
+ public String getMessageIdAsString()
+ {
+ decodeIfNecessary();
+
+ return (_messageId == null) ? null : _messageId.toString();
+ }
+
+ public void setMessageId(String messageId)
+ {
+ clearEncodedForm();
+ _propertyFlags |= MESSAGE_ID_MASK;
+ _messageId = (messageId == null) ? null : new AMQShortString(messageId);
+ }
+
+ public void setMessageId(AMQShortString messageId)
+ {
+ clearEncodedForm();
+ _propertyFlags |= MESSAGE_ID_MASK;
+ _messageId = messageId;
+ }
+
+ public long getTimestamp()
+ {
+ decodeIfNecessary();
+
+ return _timestamp;
+ }
+
+ public void setTimestamp(long timestamp)
+ {
+ clearEncodedForm();
+ _propertyFlags |= TIMESTAMP_MASK;
+ _timestamp = timestamp;
+ }
+
+ public String getTypeAsString()
+ {
+ decodeIfNecessary();
+
+ return (_type == null) ? null : _type.toString();
+ }
+
+ public AMQShortString getType()
+ {
+ decodeIfNecessary();
+
+ return _type;
+ }
+
+ public void setType(String type)
+ {
+ setType((type == null) ? null : new AMQShortString(type));
+ }
+
+ public void setType(AMQShortString type)
+ {
+ clearEncodedForm();
+ _propertyFlags |= TYPE_MASK;
+ _type = type;
+ }
+
+ public String getUserIdAsString()
+ {
+ decodeIfNecessary();
+
+ return (_userId == null) ? null : _userId.toString();
+ }
+
+ public AMQShortString getUserId()
+ {
+ decodeIfNecessary();
+
+ return _userId;
+ }
+
+ public void setUserId(String userId)
+ {
+ setUserId((userId == null) ? null : new AMQShortString(userId));
+ }
+
+ public void setUserId(AMQShortString userId)
+ {
+ clearEncodedForm();
+ _propertyFlags |= USER_ID_MASK;
+ _userId = userId;
+ }
+
+ public String getAppIdAsString()
+ {
+ decodeIfNecessary();
+
+ return (_appId == null) ? null : _appId.toString();
+ }
+
+ public AMQShortString getAppId()
+ {
+ decodeIfNecessary();
+
+ return _appId;
+ }
+
+ public void setAppId(String appId)
+ {
+ setAppId((appId == null) ? null : new AMQShortString(appId));
+ }
+
+ public void setAppId(AMQShortString appId)
+ {
+ clearEncodedForm();
+ _propertyFlags |= APPLICATION_ID_MASK;
+ _appId = appId;
+ }
+
+ public String getClusterIdAsString()
+ {
+ decodeIfNecessary();
+
+ return (_clusterId == null) ? null : _clusterId.toString();
+ }
+
+ public AMQShortString getClusterId()
+ {
+ decodeIfNecessary();
+
+ return _clusterId;
+ }
+
+ public void setClusterId(String clusterId)
+ {
+ setClusterId((clusterId == null) ? null : new AMQShortString(clusterId));
+ }
+
+ public void setClusterId(AMQShortString clusterId)
+ {
+ clearEncodedForm();
+ _propertyFlags |= CLUSTER_ID_MASK;
+ _clusterId = clusterId;
+ }
+
+ public String toString()
+ {
+ return "reply-to = " + _replyTo + ",propertyFlags = " + _propertyFlags + ",ApplicationID = " + _appId
+ + ",ClusterID = " + _clusterId + ",UserId = " + _userId + ",JMSMessageID = " + _messageId
+ + ",JMSCorrelationID = " + _correlationId + ",JMSDeliveryMode = " + _deliveryMode + ",JMSExpiration = "
+ + _expiration + ",JMSPriority = " + _priority + ",JMSTimestamp = " + _timestamp + ",JMSType = " + _type;
+ }
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java
new file mode 100644
index 0000000000..59646577e1
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * Any class that is capable of turning a stream of bytes into an AMQ structure must implement this interface.
+ */
+public interface BodyFactory
+{
+ AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException;
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java b/Final/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java
new file mode 100644
index 0000000000..6a608a8bff
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.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.framing;
+
+public interface CommonContentHeaderProperties extends ContentHeaderProperties
+{
+ AMQShortString getContentType();
+
+ void setContentType(AMQShortString contentType);
+
+ FieldTable getHeaders();
+
+ void setHeaders(FieldTable headers);
+
+ byte getDeliveryMode();
+
+ void setDeliveryMode(byte deliveryMode);
+
+ byte getPriority();
+
+ void setPriority(byte priority);
+
+ AMQShortString getCorrelationId();
+
+ void setCorrelationId(AMQShortString correlationId);
+
+ AMQShortString getReplyTo();
+
+ void setReplyTo(AMQShortString replyTo);
+
+ long getExpiration();
+
+ void setExpiration(long expiration);
+
+ AMQShortString getMessageId();
+
+ void setMessageId(AMQShortString messageId);
+
+ long getTimestamp();
+
+ void setTimestamp(long timestamp);
+
+ AMQShortString getType();
+
+ void setType(AMQShortString type);
+
+ AMQShortString getUserId();
+
+ void setUserId(AMQShortString userId);
+
+ AMQShortString getAppId();
+
+ void setAppId(AMQShortString appId);
+
+ AMQShortString getClusterId();
+
+ void setClusterId(AMQShortString clusterId);
+
+ AMQShortString getEncoding();
+
+ void setEncoding(AMQShortString encoding);
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java
new file mode 100644
index 0000000000..5ec62ede93
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class CompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock
+{
+ private ByteBuffer _encodedBlock;
+
+ private AMQDataBlock[] _blocks;
+
+ public CompositeAMQDataBlock(AMQDataBlock[] blocks)
+ {
+ _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()
+ {
+ long frameSize = 0;
+ for (int i = 0; i < _blocks.length; i++)
+ {
+ 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);
+ }
+ }
+
+ public String toString()
+ {
+ if (_blocks == null)
+ {
+ return "No blocks contained in composite frame";
+ }
+ else
+ {
+ StringBuilder buf = new StringBuilder(this.getClass().getName());
+ buf.append("{encodedBlock=").append(_encodedBlock);
+ for (int i = 0 ; i < _blocks.length; i++)
+ {
+ buf.append(" ").append(i).append("=[").append(_blocks[i].toString()).append("]");
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/Content.java b/Final/java/common/src/main/java/org/apache/qpid/framing/Content.java
new file mode 100644
index 0000000000..e5feeec2a4
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/Content.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+public interface Content
+{
+ // TODO: New Content class required for AMQP 0-9.
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java
new file mode 100644
index 0000000000..be38695384
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java
@@ -0,0 +1,106 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class ContentBody extends AMQBody
+{
+ public static final byte TYPE = 3;
+
+ public ByteBuffer payload;
+
+ public ContentBody()
+ {
+ }
+
+ public ContentBody(ByteBuffer buffer, long size) throws AMQFrameDecodingException
+ {
+ if (size > 0)
+ {
+ payload = buffer.slice();
+ payload.limit((int) size);
+ buffer.skip((int) size);
+ }
+
+ }
+
+
+ public ContentBody(ByteBuffer payload)
+ {
+ this.payload = payload;
+ }
+
+ public byte getFrameType()
+ {
+ return TYPE;
+ }
+
+ public int getSize()
+ {
+ return (payload == null ? 0 : payload.limit());
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ if (payload != null)
+ {
+ ByteBuffer copy = payload.duplicate();
+ buffer.put(copy.rewind());
+ }
+ }
+
+ protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException
+ {
+ if (size > 0)
+ {
+ payload = buffer.slice();
+ payload.limit((int) size);
+ buffer.skip((int) size);
+ }
+
+ }
+
+ public void reduceBufferToFit()
+ {
+ if (payload != null && (payload.remaining() < payload.capacity() / 2))
+ {
+ int size = payload.limit();
+ ByteBuffer newPayload = ByteBuffer.allocate(size);
+
+ newPayload.put(payload);
+ newPayload.flip();
+
+ //reduce reference count on payload
+ payload.release();
+
+ payload = newPayload;
+ }
+ }
+
+
+
+ public static AMQFrame createAMQFrame(int channelId, ContentBody body)
+ {
+ final AMQFrame frame = new AMQFrame(channelId, body);
+ return frame;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java
new file mode 100644
index 0000000000..c42995d148
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ContentBodyFactory implements BodyFactory
+{
+ private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class);
+
+ private static final ContentBodyFactory _instance = new ContentBodyFactory();
+
+ public static ContentBodyFactory getInstance()
+ {
+ return _instance;
+ }
+
+ private ContentBodyFactory()
+ {
+ _log.debug("Creating content body factory");
+ }
+
+ public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException
+ {
+ return new ContentBody(in, bodySize);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java
new file mode 100644
index 0000000000..02631a5f88
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java
@@ -0,0 +1,123 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class ContentHeaderBody extends AMQBody
+{
+ public static final byte TYPE = 2;
+
+ public int classId;
+
+ public int weight;
+
+ /** unsigned long but java can't handle that anyway when allocating byte array */
+ public long bodySize;
+
+ /** must never be null */
+ public ContentHeaderProperties properties;
+
+ public ContentHeaderBody()
+ {
+ }
+
+ public ContentHeaderBody(ByteBuffer buffer, long size) throws AMQFrameDecodingException
+ {
+ classId = buffer.getUnsignedShort();
+ weight = buffer.getUnsignedShort();
+ bodySize = buffer.getLong();
+ int propertyFlags = buffer.getUnsignedShort();
+ ContentHeaderPropertiesFactory factory = ContentHeaderPropertiesFactory.getInstance();
+ properties = factory.createContentHeaderProperties(classId, propertyFlags, buffer, (int)size - 14);
+
+ }
+
+
+ public ContentHeaderBody(ContentHeaderProperties props, int classId)
+ {
+ properties = props;
+ this.classId = classId;
+ }
+
+ public ContentHeaderBody(int classId, int weight, ContentHeaderProperties props, long bodySize)
+ {
+ this(props, classId);
+ this.weight = weight;
+ this.bodySize = bodySize;
+ }
+
+ public byte getFrameType()
+ {
+ return TYPE;
+ }
+
+ protected void populateFromBuffer(ByteBuffer buffer, long size)
+ throws AMQFrameDecodingException, AMQProtocolVersionException
+ {
+ classId = buffer.getUnsignedShort();
+ weight = buffer.getUnsignedShort();
+ bodySize = buffer.getLong();
+ int propertyFlags = buffer.getUnsignedShort();
+ ContentHeaderPropertiesFactory factory = ContentHeaderPropertiesFactory.getInstance();
+ properties = factory.createContentHeaderProperties(classId, propertyFlags, buffer, (int)size - 14);
+ }
+
+ /**
+ * Helper method that is used currently by the persistence layer (by BDB at the moment).
+ * @param buffer
+ * @param size
+ * @return
+ * @throws AMQFrameDecodingException
+ */
+ public static ContentHeaderBody createFromBuffer(ByteBuffer buffer, long size)
+ throws AMQFrameDecodingException, AMQProtocolVersionException
+ {
+ ContentHeaderBody body = new ContentHeaderBody(buffer, size);
+
+ return body;
+ }
+
+ public int getSize()
+ {
+ return 2 + 2 + 8 + 2 + properties.getPropertyListSize();
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ EncodingUtils.writeUnsignedShort(buffer, classId);
+ EncodingUtils.writeUnsignedShort(buffer, weight);
+ buffer.putLong(bodySize);
+ EncodingUtils.writeUnsignedShort(buffer, properties.getPropertyFlags());
+ properties.writePropertyListPayload(buffer);
+ }
+
+ public static AMQFrame createAMQFrame(int channelId, int classId, int weight, BasicContentHeaderProperties properties,
+ long bodySize)
+ {
+ return new AMQFrame(channelId, new ContentHeaderBody(classId, weight, properties, bodySize));
+ }
+
+ public static AMQFrame createAMQFrame(int channelId, ContentHeaderBody body)
+ {
+ return new AMQFrame(channelId, body);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java
new file mode 100644
index 0000000000..8d5e2f9fb4
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.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.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ContentHeaderBodyFactory implements BodyFactory
+{
+ private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class);
+
+ private static final ContentHeaderBodyFactory _instance = new ContentHeaderBodyFactory();
+
+ public static ContentHeaderBodyFactory getInstance()
+ {
+ return _instance;
+ }
+
+ private ContentHeaderBodyFactory()
+ {
+ _log.debug("Creating content header body factory");
+ }
+
+ public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException
+ {
+ // all content headers are the same - it is only the properties that differ.
+ // the content header body further delegates construction of properties
+ return new ContentHeaderBody(in, bodySize);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java
new file mode 100644
index 0000000000..561d7852fd
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.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.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+/**
+ * There will be an implementation of this interface for each content type. All content types have associated
+ * header properties and this provides a way to encode and decode them.
+ */
+public interface ContentHeaderProperties
+{
+ /**
+ * Writes the property list to the buffer, in a suitably encoded form.
+ * @param buffer The buffer to write to
+ */
+ void writePropertyListPayload(ByteBuffer buffer);
+
+ /**
+ * Populates the properties from buffer.
+ * @param buffer The buffer to read from.
+ * @param propertyFlags he property flags.
+ * @throws AMQFrameDecodingException when the buffer does not contain valid data
+ */
+ void populatePropertiesFromBuffer(ByteBuffer buffer, int propertyFlags, int size)
+ throws AMQFrameDecodingException;
+
+ /**
+ * @return the size of the encoded property list in bytes.
+ */
+ int getPropertyListSize();
+
+ /**
+ * Gets the property flags. Property flags indicate which properties are set in the list. The
+ * position and meaning of each flag is defined in the protocol specification for the particular
+ * content type with which these properties are associated.
+ * @return flags
+ */
+ int getPropertyFlags();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java
new file mode 100644
index 0000000000..712eb437db
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.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.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class ContentHeaderPropertiesFactory
+{
+ private static final ContentHeaderPropertiesFactory _instance = new ContentHeaderPropertiesFactory();
+
+ public static ContentHeaderPropertiesFactory getInstance()
+ {
+ return _instance;
+ }
+
+ private ContentHeaderPropertiesFactory()
+ {
+ }
+
+ public ContentHeaderProperties createContentHeaderProperties(int classId, int propertyFlags,
+ ByteBuffer buffer, int size)
+ throws AMQFrameDecodingException
+ {
+ ContentHeaderProperties properties;
+ // AMQP version change: "Hardwired" version to major=8, minor=0
+ // TODO: Change so that the actual version is obtained from
+ // the ProtocolInitiation object for this session.
+ if (classId == BasicConsumeBody.getClazz((byte)8, (byte)0))
+ {
+ properties = new BasicContentHeaderProperties();
+ }
+ else
+ {
+ throw new AMQFrameDecodingException(null, "Unsupport content header class id: " + classId, null);
+ }
+ properties.populatePropertiesFromBuffer(buffer, propertyFlags, size);
+ return properties;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java
new file mode 100644
index 0000000000..9cf96e698c
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * Marker interface to indicate to MINA that a data block should be encoded with the
+ * single encoder/decoder that we have defined.
+ *
+ * Note that due to a bug in MINA all classes must directly implement this interface, even if
+ * a superclass implements it.
+ * TODO: fix MINA so that this is not necessary
+ *
+ */
+public interface EncodableAMQDataBlock
+{
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java
new file mode 100644
index 0000000000..ccba8bd41e
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java
@@ -0,0 +1,1028 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.nio.charset.Charset;
+
+public class EncodingUtils
+{
+ private static final Logger _logger = LoggerFactory.getLogger(EncodingUtils.class);
+
+ private static final String STRING_ENCODING = "iso8859-15";
+
+ private static final Charset _charset = Charset.forName("iso8859-15");
+
+ public static final int SIZEOF_UNSIGNED_SHORT = 2;
+ public static final int SIZEOF_UNSIGNED_INT = 4;
+ private static final boolean[] ALL_FALSE_ARRAY = new boolean[8];
+
+ public static int encodedShortStringLength(String s)
+ {
+ if (s == null)
+ {
+ return 1;
+ }
+ else
+ {
+ return (short) (1 + s.length());
+ }
+ }
+
+ public static int encodedShortStringLength(short s)
+ {
+ if (s == 0)
+ {
+ return 1 + 1;
+ }
+
+ int len = 0;
+ if (s < 0)
+ {
+ len = 1;
+ // sloppy - doesn't work of Integer.MIN_VALUE
+ s = (short) -s;
+ }
+
+ if (s > 9999)
+ {
+ return 1 + 5;
+ }
+ else if (s > 999)
+ {
+ return 1 + 4;
+ }
+ else if (s > 99)
+ {
+ return 1 + 3;
+ }
+ else if (s > 9)
+ {
+ return 1 + 2;
+ }
+ else
+ {
+ return 1 + 1;
+ }
+
+ }
+
+ public static int encodedShortStringLength(int i)
+ {
+ if (i == 0)
+ {
+ return 1 + 1;
+ }
+
+ int len = 0;
+ if (i < 0)
+ {
+ len = 1;
+ // sloppy - doesn't work of Integer.MIN_VALUE
+ i = -i;
+ }
+
+ // range is now 1 - 2147483647
+ if (i < Short.MAX_VALUE)
+ {
+ return len + encodedShortStringLength((short) i);
+ }
+ else if (i > 999999)
+ {
+ return len + 6 + encodedShortStringLength((short) (i / 1000000));
+ }
+ else // if (i > 99999)
+ {
+ return len + 5 + encodedShortStringLength((short) (i / 100000));
+ }
+
+ }
+
+ public static int encodedShortStringLength(long l)
+ {
+ if (l == 0)
+ {
+ return 1 + 1;
+ }
+
+ int len = 0;
+ if (l < 0)
+ {
+ len = 1;
+ // sloppy - doesn't work of Long.MIN_VALUE
+ l = -l;
+ }
+
+ if (l < Integer.MAX_VALUE)
+ {
+ return len + encodedShortStringLength((int) l);
+ }
+ else if (l > 9999999999L)
+ {
+ return len + 10 + encodedShortStringLength((int) (l / 10000000000L));
+ }
+ else
+ {
+ return len + 1 + encodedShortStringLength((int) (l / 10L));
+ }
+
+ }
+
+ public static int encodedShortStringLength(AMQShortString s)
+ {
+ if (s == null)
+ {
+ return 1;
+ }
+ else
+ {
+ return (1 + s.length());
+ }
+ }
+
+ public static int encodedLongStringLength(String s)
+ {
+ if (s == null)
+ {
+ return 4;
+ }
+ else
+ {
+ return 4 + s.length();
+ }
+ }
+
+ public static int encodedLongStringLength(char[] s)
+ {
+ if (s == null)
+ {
+ return 4;
+ }
+ else
+ {
+ return 4 + s.length;
+ }
+ }
+
+ public static int encodedLongstrLength(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ return 4;
+ }
+ else
+ {
+ return 4 + bytes.length;
+ }
+ }
+
+ public static int encodedFieldTableLength(FieldTable table)
+ {
+ if (table == null)
+ {
+ // length is encoded as 4 octets
+ return 4;
+ }
+ else
+ {
+ // length of the table plus 4 octets for the length
+ return (int) table.getEncodedSize() + 4;
+ }
+ }
+
+ public static int encodedContentLength(Content table)
+ {
+ // TODO: New Content class required for AMQP 0-9.
+ return 0;
+ }
+
+ public static void writeShortStringBytes(ByteBuffer buffer, String s)
+ {
+ if (s != null)
+ {
+ byte[] encodedString = new byte[s.length()];
+ char[] cha = s.toCharArray();
+ for (int i = 0; i < cha.length; i++)
+ {
+ encodedString[i] = (byte) cha[i];
+ }
+
+ writeBytes(buffer, encodedString);
+ }
+ else
+ {
+ // really writing out unsigned byte
+ buffer.put((byte) 0);
+ }
+ }
+
+ public static void writeShortStringBytes(ByteBuffer buffer, AMQShortString s)
+ {
+ if (s != null)
+ {
+
+ s.writeToBuffer(buffer);
+ }
+ else
+ {
+ // really writing out unsigned byte
+ buffer.put((byte) 0);
+ }
+ }
+
+ public static void writeLongStringBytes(ByteBuffer buffer, String s)
+ {
+ assert (s == null) || (s.length() <= 0xFFFE);
+ if (s != null)
+ {
+ int len = s.length();
+ writeUnsignedInteger(buffer, s.length());
+ byte[] encodedString = new byte[len];
+ char[] cha = s.toCharArray();
+ for (int i = 0; i < cha.length; i++)
+ {
+ encodedString[i] = (byte) cha[i];
+ }
+
+ buffer.put(encodedString);
+ }
+ else
+ {
+ writeUnsignedInteger(buffer, 0);
+ }
+ }
+
+ public static void writeLongStringBytes(ByteBuffer buffer, char[] s)
+ {
+ assert (s == null) || (s.length <= 0xFFFE);
+ if (s != null)
+ {
+ int len = s.length;
+ writeUnsignedInteger(buffer, s.length);
+ byte[] encodedString = new byte[len];
+ for (int i = 0; i < s.length; i++)
+ {
+ encodedString[i] = (byte) s[i];
+ }
+
+ buffer.put(encodedString);
+ }
+ else
+ {
+ writeUnsignedInteger(buffer, 0);
+ }
+ }
+
+ public static void writeLongStringBytes(ByteBuffer buffer, byte[] bytes)
+ {
+ assert (bytes == null) || (bytes.length <= 0xFFFE);
+ if (bytes != null)
+ {
+ writeUnsignedInteger(buffer, bytes.length);
+ buffer.put(bytes);
+ }
+ else
+ {
+ writeUnsignedInteger(buffer, 0);
+ }
+ }
+
+ public static void writeUnsignedByte(ByteBuffer buffer, short b)
+ {
+ byte bv = (byte) b;
+ buffer.put(bv);
+ }
+
+ public static void writeUnsignedShort(ByteBuffer buffer, int s)
+ {
+ // TODO: Is this comparison safe? Do I need to cast RHS to long?
+ if (s < Short.MAX_VALUE)
+ {
+ buffer.putShort((short) s);
+ }
+ else
+ {
+ short sv = (short) s;
+ buffer.put((byte) (0xFF & (sv >> 8)));
+ buffer.put((byte) (0xFF & sv));
+ }
+ }
+
+ public static int unsignedIntegerLength()
+ {
+ return 4;
+ }
+
+ public static void writeUnsignedInteger(ByteBuffer buffer, long l)
+ {
+ // TODO: Is this comparison safe? Do I need to cast RHS to long?
+ if (l < Integer.MAX_VALUE)
+ {
+ buffer.putInt((int) l);
+ }
+ else
+ {
+ int iv = (int) l;
+
+ // FIXME: This *may* go faster if we build this into a local 4-byte array and then
+ // put the array in a single call.
+ buffer.put((byte) (0xFF & (iv >> 24)));
+ buffer.put((byte) (0xFF & (iv >> 16)));
+ buffer.put((byte) (0xFF & (iv >> 8)));
+ buffer.put((byte) (0xFF & iv));
+ }
+ }
+
+ public static void writeFieldTableBytes(ByteBuffer buffer, FieldTable table)
+ {
+ if (table != null)
+ {
+ table.writeToBuffer(buffer);
+ }
+ else
+ {
+ EncodingUtils.writeUnsignedInteger(buffer, 0);
+ }
+ }
+
+ public static void writeContentBytes(ByteBuffer buffer, Content content)
+ {
+ // TODO: New Content class required for AMQP 0-9.
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean[] values)
+ {
+ byte packedValue = 0;
+ for (int i = 0; i < values.length; i++)
+ {
+ if (values[i])
+ {
+ packedValue = (byte) (packedValue | (1 << i));
+ }
+ }
+
+ buffer.put(packedValue);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value)
+ {
+
+ buffer.put(value ? (byte) 1 : (byte) 0);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1)
+ {
+ byte packedValue = value0 ? (byte) 1 : (byte) 0;
+
+ if (value1)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 1));
+ }
+
+ buffer.put(packedValue);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2)
+ {
+ byte packedValue = value0 ? (byte) 1 : (byte) 0;
+
+ if (value1)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 1));
+ }
+
+ if (value2)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 2));
+ }
+
+ buffer.put(packedValue);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3)
+ {
+ byte packedValue = value0 ? (byte) 1 : (byte) 0;
+
+ if (value1)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 1));
+ }
+
+ if (value2)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 2));
+ }
+
+ if (value3)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 3));
+ }
+
+ buffer.put(packedValue);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3,
+ boolean value4)
+ {
+ byte packedValue = value0 ? (byte) 1 : (byte) 0;
+
+ if (value1)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 1));
+ }
+
+ if (value2)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 2));
+ }
+
+ if (value3)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 3));
+ }
+
+ if (value4)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 4));
+ }
+
+ buffer.put(packedValue);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3,
+ boolean value4, boolean value5)
+ {
+ byte packedValue = value0 ? (byte) 1 : (byte) 0;
+
+ if (value1)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 1));
+ }
+
+ if (value2)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 2));
+ }
+
+ if (value3)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 3));
+ }
+
+ if (value4)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 4));
+ }
+
+ if (value5)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 5));
+ }
+
+ buffer.put(packedValue);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3,
+ boolean value4, boolean value5, boolean value6)
+ {
+ byte packedValue = value0 ? (byte) 1 : (byte) 0;
+
+ if (value1)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 1));
+ }
+
+ if (value2)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 2));
+ }
+
+ if (value3)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 3));
+ }
+
+ if (value4)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 4));
+ }
+
+ if (value5)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 5));
+ }
+
+ if (value6)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 6));
+ }
+
+ buffer.put(packedValue);
+ }
+
+ public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3,
+ boolean value4, boolean value5, boolean value6, boolean value7)
+ {
+ byte packedValue = value0 ? (byte) 1 : (byte) 0;
+
+ if (value1)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 1));
+ }
+
+ if (value2)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 2));
+ }
+
+ if (value3)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 3));
+ }
+
+ if (value4)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 4));
+ }
+
+ if (value5)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 5));
+ }
+
+ if (value6)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 6));
+ }
+
+ if (value7)
+ {
+ packedValue = (byte) (packedValue | (byte) (1 << 7));
+ }
+
+ buffer.put(packedValue);
+ }
+
+ /**
+ * This is used for writing longstrs.
+ *
+ * @param buffer
+ * @param data
+ */
+ public static void writeLongstr(ByteBuffer buffer, byte[] data)
+ {
+ if (data != null)
+ {
+ writeUnsignedInteger(buffer, data.length);
+ buffer.put(data);
+ }
+ else
+ {
+ writeUnsignedInteger(buffer, 0);
+ }
+ }
+
+ public static void writeTimestamp(ByteBuffer buffer, long timestamp)
+ {
+ writeLong(buffer, timestamp);
+ }
+
+ public static boolean[] readBooleans(ByteBuffer buffer)
+ {
+ final byte packedValue = buffer.get();
+ if (packedValue == 0)
+ {
+ return ALL_FALSE_ARRAY;
+ }
+
+ final boolean[] result = new boolean[8];
+
+ result[0] = ((packedValue & 1) != 0);
+ result[1] = ((packedValue & (1 << 1)) != 0);
+ result[2] = ((packedValue & (1 << 2)) != 0);
+ result[3] = ((packedValue & (1 << 3)) != 0);
+ if ((packedValue & 0xF0) == 0)
+ {
+ result[0] = ((packedValue & 1) != 0);
+ }
+
+ result[4] = ((packedValue & (1 << 4)) != 0);
+ result[5] = ((packedValue & (1 << 5)) != 0);
+ result[6] = ((packedValue & (1 << 6)) != 0);
+ result[7] = ((packedValue & (1 << 7)) != 0);
+
+ return result;
+ }
+
+ public static FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException
+ {
+ long length = buffer.getUnsignedInt();
+ if (length == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return FieldTableFactory.newFieldTable(buffer, length);
+ }
+ }
+
+ public static Content readContent(ByteBuffer buffer) throws AMQFrameDecodingException
+ {
+ // TODO: New Content class required for AMQP 0-9.
+ return null;
+ }
+
+ public static AMQShortString readAMQShortString(ByteBuffer buffer)
+ {
+ return AMQShortString.readFromBuffer(buffer);
+
+ }
+
+ public static String readShortString(ByteBuffer buffer)
+ {
+ short length = buffer.getUnsigned();
+ if (length == 0)
+ {
+ return null;
+ }
+ else
+ {
+ // this may seem rather odd to declare two array but testing has shown
+ // that constructing a string from a byte array is 5 (five) times slower
+ // than constructing one from a char array.
+ // this approach here is valid since we know that all the chars are
+ // ASCII (0-127)
+ byte[] stringBytes = new byte[length];
+ buffer.get(stringBytes, 0, length);
+ char[] stringChars = new char[length];
+ for (int i = 0; i < stringChars.length; i++)
+ {
+ stringChars[i] = (char) stringBytes[i];
+ }
+
+ return new String(stringChars);
+ }
+ }
+
+ public static String readLongString(ByteBuffer buffer)
+ {
+ long length = buffer.getUnsignedInt();
+ if (length == 0)
+ {
+ return "";
+ }
+ else
+ {
+ // this may seem rather odd to declare two array but testing has shown
+ // that constructing a string from a byte array is 5 (five) times slower
+ // than constructing one from a char array.
+ // this approach here is valid since we know that all the chars are
+ // ASCII (0-127)
+ byte[] stringBytes = new byte[(int) length];
+ buffer.get(stringBytes, 0, (int) length);
+ char[] stringChars = new char[(int) length];
+ for (int i = 0; i < stringChars.length; i++)
+ {
+ stringChars[i] = (char) stringBytes[i];
+ }
+
+ return new String(stringChars);
+ }
+ }
+
+ public static byte[] readLongstr(ByteBuffer buffer)
+ {
+ long length = buffer.getUnsignedInt();
+ if (length == 0)
+ {
+ return null;
+ }
+ else
+ {
+ byte[] result = new byte[(int) length];
+ buffer.get(result);
+
+ return result;
+ }
+ }
+
+ public static long readTimestamp(ByteBuffer buffer)
+ {
+ // Discard msb from AMQ timestamp
+ // buffer.getUnsignedInt();
+ return buffer.getLong();
+ }
+
+ static byte[] hexToByteArray(String id)
+ {
+ // Should check param for null, long enough for this check, upper-case and trailing char
+ String s = (id.charAt(1) == 'x') ? id.substring(2) : id; // strip 0x
+
+ int len = s.length();
+ int byte_len = len / 2;
+ byte[] b = new byte[byte_len];
+
+ for (int i = 0; i < byte_len; i++)
+ {
+ // fixme: refine these repetitive subscript calcs.
+ int ch = i * 2;
+
+ byte b1 = Byte.parseByte(s.substring(ch, ch + 1), 16);
+ byte b2 = Byte.parseByte(s.substring(ch + 1, ch + 2), 16);
+
+ b[i] = (byte) ((b1 * 16) + b2);
+ }
+
+ return (b);
+ }
+
+ public static char[] convertToHexCharArray(byte[] from)
+ {
+ int length = from.length;
+ char[] result_buff = new char[(length * 2) + 2];
+
+ result_buff[0] = '0';
+ result_buff[1] = 'x';
+
+ int bite;
+ int dest = 2;
+
+ for (int i = 0; i < length; i++)
+ {
+ bite = from[i];
+
+ if (bite < 0)
+ {
+ bite += 256;
+ }
+
+ result_buff[dest++] = hex_chars[bite >> 4];
+ result_buff[dest++] = hex_chars[bite & 0x0f];
+ }
+
+ return (result_buff);
+ }
+
+ public static String convertToHexString(byte[] from)
+ {
+ return (new String(convertToHexCharArray(from)));
+ }
+
+ public static String convertToHexString(ByteBuffer bb)
+ {
+ int size = bb.limit();
+
+ byte[] from = new byte[size];
+
+ // Is this not the same.
+ // bb.get(from, 0, length);
+ for (int i = 0; i < size; i++)
+ {
+ from[i] = bb.get(i);
+ }
+
+ return (new String(convertToHexCharArray(from)));
+ }
+
+ private static char[] hex_chars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ // **** new methods
+
+ // AMQP_BOOLEAN_PROPERTY_PREFIX
+
+ public static void writeBoolean(ByteBuffer buffer, Boolean aBoolean)
+ {
+ buffer.put((byte) (aBoolean ? 1 : 0));
+ }
+
+ public static boolean readBoolean(ByteBuffer buffer)
+ {
+ byte packedValue = buffer.get();
+
+ return (packedValue == 1);
+ }
+
+ public static int encodedBooleanLength()
+ {
+ return 1;
+ }
+
+ // AMQP_BYTE_PROPERTY_PREFIX
+ public static void writeByte(ByteBuffer buffer, Byte aByte)
+ {
+ buffer.put(aByte);
+ }
+
+ public static byte readByte(ByteBuffer buffer)
+ {
+ return buffer.get();
+ }
+
+ public static int encodedByteLength()
+ {
+ return 1;
+ }
+
+ // AMQP_SHORT_PROPERTY_PREFIX
+ public static void writeShort(ByteBuffer buffer, Short aShort)
+ {
+ buffer.putShort(aShort);
+ }
+
+ public static short readShort(ByteBuffer buffer)
+ {
+ return buffer.getShort();
+ }
+
+ public static int encodedShortLength()
+ {
+ return 2;
+ }
+
+ // INTEGER_PROPERTY_PREFIX
+ public static void writeInteger(ByteBuffer buffer, Integer aInteger)
+ {
+ buffer.putInt(aInteger);
+ }
+
+ public static int readInteger(ByteBuffer buffer)
+ {
+ return buffer.getInt();
+ }
+
+ public static int encodedIntegerLength()
+ {
+ return 4;
+ }
+
+ // AMQP_LONG_PROPERTY_PREFIX
+ public static void writeLong(ByteBuffer buffer, Long aLong)
+ {
+ buffer.putLong(aLong);
+ }
+
+ public static long readLong(ByteBuffer buffer)
+ {
+ return buffer.getLong();
+ }
+
+ public static int encodedLongLength()
+ {
+ return 8;
+ }
+
+ // Float_PROPERTY_PREFIX
+ public static void writeFloat(ByteBuffer buffer, Float aFloat)
+ {
+ buffer.putFloat(aFloat);
+ }
+
+ public static float readFloat(ByteBuffer buffer)
+ {
+ return buffer.getFloat();
+ }
+
+ public static int encodedFloatLength()
+ {
+ return 4;
+ }
+
+ // Double_PROPERTY_PREFIX
+ public static void writeDouble(ByteBuffer buffer, Double aDouble)
+ {
+ buffer.putDouble(aDouble);
+ }
+
+ public static double readDouble(ByteBuffer buffer)
+ {
+ return buffer.getDouble();
+ }
+
+ public static int encodedDoubleLength()
+ {
+ return 8;
+ }
+
+ public static byte[] readBytes(ByteBuffer buffer)
+ {
+ short length = buffer.getUnsigned();
+ if (length == 0)
+ {
+ return null;
+ }
+ else
+ {
+ byte[] dataBytes = new byte[length];
+ buffer.get(dataBytes, 0, length);
+
+ return dataBytes;
+ }
+ }
+
+ public static void writeBytes(ByteBuffer buffer, byte[] data)
+ {
+ if (data != null)
+ {
+ // TODO: check length fits in an unsigned byte
+ writeUnsignedByte(buffer, (short) data.length);
+ buffer.put(data);
+ }
+ else
+ {
+ // really writing out unsigned byte
+ buffer.put((byte) 0);
+ }
+ }
+
+ // CHAR_PROPERTY
+ public static int encodedCharLength()
+ {
+ return encodedByteLength();
+ }
+
+ public static char readChar(ByteBuffer buffer)
+ {
+ // This is valid as we know that the Character is ASCII 0..127
+ return (char) buffer.get();
+ }
+
+ public static void writeChar(ByteBuffer buffer, char character)
+ {
+ // This is valid as we know that the Character is ASCII 0..127
+ writeByte(buffer, (byte) character);
+ }
+
+ public static long readLongAsShortString(ByteBuffer buffer)
+ {
+ short length = buffer.getUnsigned();
+ short pos = 0;
+ if (length == 0)
+ {
+ return 0L;
+ }
+
+ byte digit = buffer.get();
+ boolean isNegative;
+ long result = 0;
+ if (digit == (byte) '-')
+ {
+ isNegative = true;
+ pos++;
+ digit = buffer.get();
+ }
+ else
+ {
+ isNegative = false;
+ }
+
+ result = digit - (byte) '0';
+ pos++;
+
+ while (pos < length)
+ {
+ pos++;
+ digit = buffer.get();
+ result = (result << 3) + (result << 1);
+ result += digit - (byte) '0';
+ }
+
+ return result;
+ }
+
+ public static long readUnsignedInteger(ByteBuffer buffer)
+ {
+ long l = 0xFF & buffer.get();
+ l <<= 8;
+ l = l | (0xFF & buffer.get());
+ l <<= 8;
+ l = l | (0xFF & buffer.get());
+ l <<= 8;
+ l = l | (0xFF & buffer.get());
+
+ return l;
+ }
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
new file mode 100644
index 0000000000..46b10b5963
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
@@ -0,0 +1,1017 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQPInvalidClassException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+// extends FieldTable
+public class FieldTable
+{
+ private static final Logger _logger = LoggerFactory.getLogger(FieldTable.class);
+ private static final String STRICT_AMQP = "STRICT_AMQP";
+ private final boolean _strictAMQP = Boolean.valueOf(System.getProperty(STRICT_AMQP, "false"));
+
+ private ByteBuffer _encodedForm;
+ private LinkedHashMap<AMQShortString, AMQTypedValue> _properties;
+ private long _encodedSize;
+ private static final int INITIAL_HASHMAP_CAPACITY = 16;
+ private static final int INITIAL_ENCODED_FORM_SIZE = 256;
+
+ public FieldTable()
+ {
+ super();
+ // _encodedForm = ByteBuffer.allocate(INITIAL_ENCODED_FORM_SIZE);
+ // _encodedForm.setAutoExpand(true);
+ // _encodedForm.limit(0);
+ }
+
+ /**
+ * Construct a new field table.
+ *
+ * @param buffer the buffer from which to read data. The length byte must be read already
+ * @param length the length of the field table. Must be > 0.
+ *
+ * @throws AMQFrameDecodingException if there is an error decoding the table
+ */
+ public FieldTable(ByteBuffer buffer, long length) throws AMQFrameDecodingException
+ {
+ this();
+ _encodedForm = buffer.slice();
+ _encodedForm.limit((int) length);
+ _encodedSize = length;
+ buffer.skip((int) length);
+ }
+
+ private AMQTypedValue getProperty(AMQShortString string)
+ {
+ checkPropertyName(string);
+
+ synchronized (this)
+ {
+ if (_properties == null)
+ {
+ if (_encodedForm == null)
+ {
+ return null;
+ }
+ else
+ {
+ populateFromBuffer();
+ }
+ }
+ }
+
+ if (_properties == null)
+ {
+ return null;
+ }
+ else
+ {
+ return _properties.get(string);
+ }
+ }
+
+ private void populateFromBuffer()
+ {
+ try
+ {
+ setFromBuffer(_encodedForm, _encodedSize);
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ _logger.error("Error decoding FieldTable in deferred decoding mode ", e);
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private AMQTypedValue setProperty(AMQShortString key, AMQTypedValue val)
+ {
+ checkPropertyName(key);
+ initMapIfNecessary();
+ if (_properties.containsKey(key))
+ {
+ _encodedForm = null;
+
+ if (val == null)
+ {
+ return removeKey(key);
+ }
+ }
+ else if ((_encodedForm != null) && (val != null))
+ {
+ EncodingUtils.writeShortStringBytes(_encodedForm, key);
+ val.writeToBuffer(_encodedForm);
+
+ }
+ else if (val == null)
+ {
+ return null;
+ }
+
+ AMQTypedValue oldVal = _properties.put(key, val);
+ if (oldVal != null)
+ {
+ _encodedSize -= oldVal.getEncodingSize();
+ }
+ else
+ {
+ _encodedSize += EncodingUtils.encodedShortStringLength(key) + 1;
+ }
+
+ _encodedSize += val.getEncodingSize();
+
+ return oldVal;
+ }
+
+ private void initMapIfNecessary()
+ {
+ synchronized (this)
+ {
+ if (_properties == null)
+ {
+ if ((_encodedForm == null) || (_encodedSize == 0))
+ {
+ _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>();
+ }
+ else
+ {
+ populateFromBuffer();
+ }
+ }
+
+ }
+ }
+
+ public Boolean getBoolean(String string)
+ {
+ return getBoolean(new AMQShortString(string));
+ }
+
+ public Boolean getBoolean(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.BOOLEAN))
+ {
+ return (Boolean) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public Byte getByte(String string)
+ {
+ return getByte(new AMQShortString(string));
+ }
+
+ public Byte getByte(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.BYTE))
+ {
+ return (Byte) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public Short getShort(String string)
+ {
+ return getShort(new AMQShortString(string));
+ }
+
+ public Short getShort(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.SHORT))
+ {
+ return (Short) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public Integer getInteger(String string)
+ {
+ return getInteger(new AMQShortString(string));
+ }
+
+ public Integer getInteger(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.INT))
+ {
+ return (Integer) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public Long getLong(String string)
+ {
+ return getLong(new AMQShortString(string));
+ }
+
+ public Long getLong(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.LONG))
+ {
+ return (Long) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public Float getFloat(String string)
+ {
+ return getFloat(new AMQShortString(string));
+ }
+
+ public Float getFloat(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.FLOAT))
+ {
+ return (Float) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public Double getDouble(String string)
+ {
+ return getDouble(new AMQShortString(string));
+ }
+
+ public Double getDouble(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.DOUBLE))
+ {
+ return (Double) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String getString(String string)
+ {
+ return getString(new AMQShortString(string));
+ }
+
+ public String getString(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && ((value.getType() == AMQType.WIDE_STRING) || (value.getType() == AMQType.ASCII_STRING)))
+ {
+ return (String) value.getValue();
+ }
+ else if ((value != null) && (value.getValue() != null) && !(value.getValue() instanceof byte[]))
+ {
+ return String.valueOf(value.getValue());
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ public Character getCharacter(String string)
+ {
+ return getCharacter(new AMQShortString(string));
+ }
+
+ public Character getCharacter(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.ASCII_CHARACTER))
+ {
+ return (Character) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public byte[] getBytes(String string)
+ {
+ return getBytes(new AMQShortString(string));
+ }
+
+ public byte[] getBytes(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if ((value != null) && (value.getType() == AMQType.BINARY))
+ {
+ return (byte[]) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public Object getObject(String string)
+ {
+ return getObject(new AMQShortString(string));
+ }
+
+ public Object getObject(AMQShortString string)
+ {
+ AMQTypedValue value = getProperty(string);
+ if (value != null)
+ {
+ return value.getValue();
+ }
+ else
+ {
+ return value;
+ }
+
+ }
+
+ public Long getTimestamp(AMQShortString name)
+ {
+ AMQTypedValue value = getProperty(name);
+ if ((value != null) && (value.getType() == AMQType.TIMESTAMP))
+ {
+ return (Long) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public BigDecimal getDecimal(AMQShortString propertyName)
+ {
+ AMQTypedValue value = getProperty(propertyName);
+ if ((value != null) && (value.getType() == AMQType.DECIMAL))
+ {
+ return (BigDecimal) value.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ // ************ Setters
+ public Object setBoolean(String string, Boolean b)
+ {
+ return setBoolean(new AMQShortString(string), b);
+ }
+
+ public Object setBoolean(AMQShortString string, Boolean b)
+ {
+ return setProperty(string, AMQType.BOOLEAN.asTypedValue(b));
+ }
+
+ public Object setByte(String string, Byte b)
+ {
+ return setByte(new AMQShortString(string), b);
+ }
+
+ public Object setByte(AMQShortString string, Byte b)
+ {
+ return setProperty(string, AMQType.BYTE.asTypedValue(b));
+ }
+
+ public Object setShort(String string, Short i)
+ {
+ return setShort(new AMQShortString(string), i);
+ }
+
+ public Object setShort(AMQShortString string, Short i)
+ {
+ return setProperty(string, AMQType.SHORT.asTypedValue(i));
+ }
+
+ public Object setInteger(String string, Integer i)
+ {
+ return setInteger(new AMQShortString(string), i);
+ }
+
+ public Object setInteger(AMQShortString string, Integer i)
+ {
+ return setProperty(string, AMQType.INT.asTypedValue(i));
+ }
+
+ public Object setLong(String string, Long l)
+ {
+ return setLong(new AMQShortString(string), l);
+ }
+
+ public Object setLong(AMQShortString string, Long l)
+ {
+ return setProperty(string, AMQType.LONG.asTypedValue(l));
+ }
+
+ public Object setFloat(String string, Float f)
+ {
+ return setFloat(new AMQShortString(string), f);
+ }
+
+ public Object setFloat(AMQShortString string, Float v)
+ {
+ return setProperty(string, AMQType.FLOAT.asTypedValue(v));
+ }
+
+ public Object setDouble(String string, Double d)
+ {
+ return setDouble(new AMQShortString(string), d);
+ }
+
+ public Object setDouble(AMQShortString string, Double v)
+ {
+ return setProperty(string, AMQType.DOUBLE.asTypedValue(v));
+ }
+
+ public Object setString(String string, String s)
+ {
+ return setString(new AMQShortString(string), s);
+ }
+
+ public Object setAsciiString(AMQShortString string, String value)
+ {
+ if (value == null)
+ {
+ return setProperty(string, AMQType.VOID.asTypedValue(null));
+ }
+ else
+ {
+ return setProperty(string, AMQType.ASCII_STRING.asTypedValue(value));
+ }
+ }
+
+ public Object setString(AMQShortString string, String value)
+ {
+ if (value == null)
+ {
+ return setProperty(string, AMQType.VOID.asTypedValue(null));
+ }
+ else
+ {
+ return setProperty(string, AMQType.LONG_STRING.asTypedValue(value));
+ }
+ }
+
+ public Object setChar(String string, char c)
+ {
+ return setChar(new AMQShortString(string), c);
+ }
+
+ public Object setChar(AMQShortString string, char c)
+ {
+ return setProperty(string, AMQType.ASCII_CHARACTER.asTypedValue(c));
+ }
+
+ public Object setBytes(String string, byte[] b)
+ {
+ return setBytes(new AMQShortString(string), b);
+ }
+
+ public Object setBytes(AMQShortString string, byte[] bytes)
+ {
+ return setProperty(string, AMQType.BINARY.asTypedValue(bytes));
+ }
+
+ public Object setBytes(String string, byte[] bytes, int start, int length)
+ {
+ return setBytes(new AMQShortString(string), bytes, start, length);
+ }
+
+ public Object setBytes(AMQShortString string, byte[] bytes, int start, int length)
+ {
+ byte[] newBytes = new byte[length];
+ System.arraycopy(bytes, start, newBytes, 0, length);
+
+ return setBytes(string, bytes);
+ }
+
+ public Object setObject(String string, Object o)
+ {
+ return setObject(new AMQShortString(string), o);
+ }
+
+ public Object setTimestamp(AMQShortString string, long datetime)
+ {
+ return setProperty(string, AMQType.TIMESTAMP.asTypedValue(datetime));
+ }
+
+ public Object setDecimal(AMQShortString string, BigDecimal decimal)
+ {
+ if (decimal.longValue() > Integer.MAX_VALUE)
+ {
+ throw new UnsupportedOperationException("AMQP doesnot support decimals larger than " + Integer.MAX_VALUE);
+ }
+
+ if (decimal.scale() > Byte.MAX_VALUE)
+ {
+ throw new UnsupportedOperationException("AMQP doesnot support decimal scales larger than " + Byte.MAX_VALUE);
+ }
+
+ return setProperty(string, AMQType.DECIMAL.asTypedValue(decimal));
+ }
+
+ public Object setVoid(AMQShortString string)
+ {
+ return setProperty(string, AMQType.VOID.asTypedValue(null));
+ }
+
+ public Object setObject(AMQShortString string, Object object)
+ {
+ if (object instanceof Boolean)
+ {
+ return setBoolean(string, (Boolean) object);
+ }
+ else if (object instanceof Byte)
+ {
+ return setByte(string, (Byte) object);
+ }
+ else if (object instanceof Short)
+ {
+ return setShort(string, (Short) object);
+ }
+ else if (object instanceof Integer)
+ {
+ return setInteger(string, (Integer) object);
+ }
+ else if (object instanceof Long)
+ {
+ return setLong(string, (Long) object);
+ }
+ else if (object instanceof Float)
+ {
+ return setFloat(string, (Float) object);
+ }
+ else if (object instanceof Double)
+ {
+ return setDouble(string, (Double) object);
+ }
+ else if (object instanceof String)
+ {
+ return setString(string, (String) object);
+ }
+ else if (object instanceof Character)
+ {
+ return setChar(string, (Character) object);
+ }
+ else if (object instanceof byte[])
+ {
+ return setBytes(string, (byte[]) object);
+ }
+
+ throw new AMQPInvalidClassException("Only Primatives objects allowed Object is:" + object.getClass());
+ }
+
+ public boolean isNullStringValue(String name)
+ {
+ AMQTypedValue value = getProperty(new AMQShortString(name));
+
+ return (value != null) && (value.getType() == AMQType.VOID);
+ }
+
+ // ***** Methods
+
+ public Enumeration getPropertyNames()
+ {
+ return Collections.enumeration(keys());
+ }
+
+ public boolean propertyExists(AMQShortString propertyName)
+ {
+ return itemExists(propertyName);
+ }
+
+ public boolean propertyExists(String propertyName)
+ {
+ return itemExists(propertyName);
+ }
+
+ public boolean itemExists(AMQShortString propertyName)
+ {
+ checkPropertyName(propertyName);
+ initMapIfNecessary();
+
+ return _properties.containsKey(propertyName);
+ }
+
+ public boolean itemExists(String string)
+ {
+ return itemExists(new AMQShortString(string));
+ }
+
+ public String toString()
+ {
+ initMapIfNecessary();
+
+ return _properties.toString();
+ }
+
+ private void checkPropertyName(AMQShortString propertyName)
+ {
+ if (propertyName == null)
+ {
+ throw new IllegalArgumentException("Property name must not be null");
+ }
+ else if (propertyName.length() == 0)
+ {
+ throw new IllegalArgumentException("Property name must not be the empty string");
+ }
+
+ if (_strictAMQP)
+ {
+ checkIdentiferFormat(propertyName);
+ }
+ }
+
+ protected static void checkIdentiferFormat(AMQShortString propertyName)
+ {
+ // AMQP Spec: 4.2.5.5 Field Tables
+ // Guidelines for implementers:
+ // * Field names MUST start with a letter, '$' or '#' and may continue with
+ // letters, '$' or '#', digits, or underlines, to a maximum length of 128
+ // characters.
+ // * The server SHOULD validate field names and upon receiving an invalid
+ // field name, it SHOULD signal a connection exception with reply code
+ // 503 (syntax error). Conformance test: amq_wlp_table_01.
+ // * A peer MUST handle duplicate fields by using only the first instance.
+
+ // AMQP length limit
+ if (propertyName.length() > 128)
+ {
+ throw new IllegalArgumentException("AMQP limits property names to 128 characters");
+ }
+
+ // AMQ start character
+ if (!(Character.isLetter(propertyName.charAt(0)) || (propertyName.charAt(0) == '$')
+ || (propertyName.charAt(0) == '#') || (propertyName.charAt(0) == '_'))) // Not official AMQP added for JMS.
+ {
+ throw new IllegalArgumentException("Identifier '" + propertyName
+ + "' does not start with a valid AMQP start character");
+ }
+ }
+
+ // ************************* Byte Buffer Processing
+
+ public void writeToBuffer(ByteBuffer buffer)
+ {
+ final boolean trace = _logger.isTraceEnabled();
+
+ if (trace)
+ {
+ _logger.trace("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() + "...");
+ if (_properties != null)
+ {
+ _logger.trace(_properties.toString());
+ }
+ }
+
+ EncodingUtils.writeUnsignedInteger(buffer, getEncodedSize());
+
+ putDataInBuffer(buffer);
+ }
+
+ public byte[] getDataAsBytes()
+ {
+ final int encodedSize = (int) getEncodedSize();
+ final ByteBuffer buffer = ByteBuffer.allocate(encodedSize); // FIXME XXX: Is cast a problem?
+
+ putDataInBuffer(buffer);
+
+ final byte[] result = new byte[encodedSize];
+ buffer.flip();
+ buffer.get(result);
+ buffer.release();
+
+ return result;
+ }
+
+ public long getEncodedSize()
+ {
+ return _encodedSize;
+ }
+
+ private void recalculateEncodedSize()
+ {
+
+ int encodedSize = 0;
+ if (_properties != null)
+ {
+ for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet())
+ {
+ encodedSize += EncodingUtils.encodedShortStringLength(e.getKey());
+ encodedSize++; // the byte for the encoding Type
+ encodedSize += e.getValue().getEncodingSize();
+
+ }
+ }
+
+ _encodedSize = encodedSize;
+ }
+
+ public void addAll(FieldTable fieldTable)
+ {
+ initMapIfNecessary();
+ _encodedForm = null;
+ _properties.putAll(fieldTable._properties);
+ recalculateEncodedSize();
+ }
+
+ public static interface FieldTableElementProcessor
+ {
+ public boolean processElement(String propertyName, AMQTypedValue value);
+
+ public Object getResult();
+ }
+
+ public Object processOverElements(FieldTableElementProcessor processor)
+ {
+ initMapIfNecessary();
+ if (_properties != null)
+ {
+ for (Map.Entry<AMQShortString, AMQTypedValue> e : _properties.entrySet())
+ {
+ boolean result = processor.processElement(e.getKey().toString(), e.getValue());
+ if (!result)
+ {
+ break;
+ }
+ }
+ }
+
+ return processor.getResult();
+
+ }
+
+ public int size()
+ {
+ initMapIfNecessary();
+
+ return _properties.size();
+
+ }
+
+ public boolean isEmpty()
+ {
+ return size() == 0;
+ }
+
+ public boolean containsKey(AMQShortString key)
+ {
+ initMapIfNecessary();
+
+ return _properties.containsKey(key);
+ }
+
+ public boolean containsKey(String key)
+ {
+ return containsKey(new AMQShortString(key));
+ }
+
+ public Set<String> keys()
+ {
+ initMapIfNecessary();
+ Set<String> keys = new LinkedHashSet<String>();
+ for (AMQShortString key : _properties.keySet())
+ {
+ keys.add(key.toString());
+ }
+
+ return keys;
+ }
+
+ public Object get(AMQShortString key)
+ {
+
+ return getObject(key);
+ }
+
+ public Object put(AMQShortString key, Object value)
+ {
+ return setObject(key, value);
+ }
+
+ public Object remove(String key)
+ {
+
+ return remove(new AMQShortString(key));
+
+ }
+
+ public Object remove(AMQShortString key)
+ {
+ AMQTypedValue val = removeKey(key);
+
+ return (val == null) ? null : val.getValue();
+
+ }
+
+ public AMQTypedValue removeKey(AMQShortString key)
+ {
+ initMapIfNecessary();
+ _encodedForm = null;
+ AMQTypedValue value = _properties.remove(key);
+ if (value == null)
+ {
+ return null;
+ }
+ else
+ {
+ _encodedSize -= EncodingUtils.encodedShortStringLength(key);
+ _encodedSize--;
+ _encodedSize -= value.getEncodingSize();
+
+ return value;
+ }
+
+ }
+
+ public void clear()
+ {
+ initMapIfNecessary();
+ _encodedForm = null;
+ _properties.clear();
+ _encodedSize = 0;
+ }
+
+ public Set<AMQShortString> keySet()
+ {
+ initMapIfNecessary();
+
+ return _properties.keySet();
+ }
+
+ private void putDataInBuffer(ByteBuffer buffer)
+ {
+
+ if (_encodedForm != null)
+ {
+
+ if (_encodedForm.position() != 0)
+ {
+ _encodedForm.flip();
+ }
+ // _encodedForm.limit((int)getEncodedSize());
+
+ buffer.put(_encodedForm);
+ }
+ else if (_properties != null)
+ {
+ final Iterator<Map.Entry<AMQShortString, AMQTypedValue>> it = _properties.entrySet().iterator();
+
+ // If there are values then write out the encoded Size... could check _encodedSize != 0
+ // write out the total length, which we have kept up to date as data is added
+
+ while (it.hasNext())
+ {
+ final Map.Entry<AMQShortString, AMQTypedValue> me = it.next();
+ try
+ {
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:"
+ + me.getValue().getValue());
+ _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining());
+ }
+
+ // Write the actual parameter name
+ EncodingUtils.writeShortStringBytes(buffer, me.getKey());
+ me.getValue().writeToBuffer(buffer);
+ }
+ catch (Exception e)
+ {
+ if (_logger.isTraceEnabled())
+ {
+ _logger.trace("Exception thrown:" + e);
+ _logger.trace("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:"
+ + me.getValue().getValue());
+ _logger.trace("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining());
+ }
+
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ private void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException
+ {
+
+ final boolean trace = _logger.isTraceEnabled();
+ if (length > 0)
+ {
+
+ final int expectedRemaining = buffer.remaining() - (int) length;
+
+ _properties = new LinkedHashMap<AMQShortString, AMQTypedValue>(INITIAL_HASHMAP_CAPACITY);
+
+ do
+ {
+
+ final AMQShortString key = EncodingUtils.readAMQShortString(buffer);
+ AMQTypedValue value = AMQTypedValue.readFromBuffer(buffer);
+
+ if (trace)
+ {
+ _logger.trace("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + value.getType()
+ + "', key '" + key + "', value '" + value.getValue() + "'");
+ }
+
+ _properties.put(key, value);
+
+ }
+ while (buffer.remaining() > expectedRemaining);
+
+ }
+
+ _encodedSize = length;
+
+ if (trace)
+ {
+ _logger.trace("FieldTable::FieldTable(buffer," + length + "): Done.");
+ }
+ }
+
+ public int hashCode()
+ {
+ initMapIfNecessary();
+
+ return _properties.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (o == null)
+ {
+ return false;
+ }
+
+ if (!(o instanceof FieldTable))
+ {
+ return false;
+ }
+
+ initMapIfNecessary();
+
+ FieldTable f = (FieldTable) o;
+ f.initMapIfNecessary();
+
+ return _properties.equals(f._properties);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java
new file mode 100644
index 0000000000..e9d75137ef
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class FieldTableFactory
+{
+ public static FieldTable newFieldTable()
+ {
+ return new FieldTable();
+ }
+
+ public static FieldTable newFieldTable(ByteBuffer byteBuffer, long length) throws AMQFrameDecodingException
+ {
+ return new FieldTable(byteBuffer, length);
+ }
+
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java
new file mode 100644
index 0000000000..7246c4a1cf
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class HeartbeatBody extends AMQBody
+{
+ public static final byte TYPE = 8;
+ public static AMQFrame FRAME = new HeartbeatBody().toFrame();
+
+ public HeartbeatBody()
+ {
+
+ }
+
+ public HeartbeatBody(ByteBuffer buffer, long size)
+ {
+ if(size > 0)
+ {
+ //allow other implementations to have a payload, but ignore it:
+ buffer.skip((int) size);
+ }
+ }
+
+ public byte getFrameType()
+ {
+ return TYPE;
+ }
+
+ protected int getSize()
+ {
+ return 0;//heartbeats we generate have no payload
+ }
+
+ protected void writePayload(ByteBuffer buffer)
+ {
+ }
+
+ protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException
+ {
+ if(size > 0)
+ {
+ //allow other implementations to have a payload, but ignore it:
+ buffer.skip((int) size);
+ }
+ }
+
+ public AMQFrame toFrame()
+ {
+ return new AMQFrame(0, this);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java
new file mode 100644
index 0000000000..c7ada708dc
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class HeartbeatBodyFactory implements BodyFactory
+{
+ public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException
+ {
+ return new HeartbeatBody();
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java b/Final/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java
new file mode 100644
index 0000000000..9a113f452b
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.AbstractMethodConverter;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class MethodConverter_8_0 extends AbstractMethodConverter implements ProtocolVersionMethodConverter
+{
+ private int _basicPublishClassId;
+ private int _basicPublishMethodId;
+
+ public MethodConverter_8_0()
+ {
+ super((byte)8,(byte)0);
+
+
+ }
+
+ public AMQBody convertToBody(ContentChunk contentChunk)
+ {
+ return new ContentBody(contentChunk.getData());
+ }
+
+ public ContentChunk convertToContentChunk(AMQBody body)
+ {
+ final ContentBody contentBodyChunk = (ContentBody) body;
+
+ return new ContentChunk()
+ {
+
+ public int getSize()
+ {
+ return contentBodyChunk.getSize();
+ }
+
+ public ByteBuffer getData()
+ {
+ return contentBodyChunk.payload;
+ }
+
+ public void reduceToFit()
+ {
+ contentBodyChunk.reduceBufferToFit();
+ }
+ };
+
+ }
+
+ public void configure()
+ {
+
+ _basicPublishClassId = BasicPublishBody.getClazz(getProtocolMajorVersion(),getProtocolMinorVersion());
+ _basicPublishMethodId = BasicPublishBody.getMethod(getProtocolMajorVersion(),getProtocolMinorVersion());
+
+ }
+
+ public MessagePublishInfo convertToInfo(AMQMethodBody methodBody)
+ {
+ final BasicPublishBody body = (BasicPublishBody) methodBody;
+
+ return new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return body.getExchange();
+ }
+
+ public boolean isImmediate()
+ {
+ return body.getImmediate();
+ }
+
+ public boolean isMandatory()
+ {
+ return body.getMandatory();
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return body.getRoutingKey();
+ }
+ };
+
+ }
+
+ public AMQMethodBody convertToBody(MessagePublishInfo info)
+ {
+
+ return new BasicPublishBody(getProtocolMajorVersion(),
+ getProtocolMinorVersion(),
+ _basicPublishClassId,
+ _basicPublishMethodId,
+ info.getExchange(),
+ info.isImmediate(),
+ info.isMandatory(),
+ info.getRoutingKey(),
+ 0) ; // ticket
+
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java b/Final/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java
new file mode 100644
index 0000000000..8b40fe72eb
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java
@@ -0,0 +1,194 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.qpid.AMQException;
+
+import java.io.UnsupportedEncodingException;
+
+public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQDataBlock
+{
+
+ // TODO: generate these constants automatically from the xml protocol spec file
+ public static final byte[] AMQP_HEADER = new byte[]{(byte)'A',(byte)'M',(byte)'Q',(byte)'P'};
+
+ private static final byte CURRENT_PROTOCOL_CLASS = 1;
+ private static final byte TCP_PROTOCOL_INSTANCE = 1;
+
+ public final byte[] _protocolHeader;
+ public final byte _protocolClass;
+ public final byte _protocolInstance;
+ public final byte _protocolMajor;
+ public final byte _protocolMinor;
+
+
+// public ProtocolInitiation() {}
+
+ public ProtocolInitiation(byte[] protocolHeader, byte protocolClass, byte protocolInstance, byte protocolMajor, byte protocolMinor)
+ {
+ _protocolHeader = protocolHeader;
+ _protocolClass = protocolClass;
+ _protocolInstance = protocolInstance;
+ _protocolMajor = protocolMajor;
+ _protocolMinor = protocolMinor;
+ }
+
+ public ProtocolInitiation(ProtocolVersion pv)
+ {
+ this(AMQP_HEADER, CURRENT_PROTOCOL_CLASS, TCP_PROTOCOL_INSTANCE, pv.getMajorVersion(), pv.getMinorVersion());
+ }
+
+
+ public ProtocolInitiation(ByteBuffer in)
+ {
+ _protocolHeader = new byte[4];
+ in.get(_protocolHeader);
+
+ _protocolClass = in.get();
+ _protocolInstance = in.get();
+ _protocolMajor = in.get();
+ _protocolMinor = in.get();
+ }
+
+ public long getSize()
+ {
+ return 4 + 1 + 1 + 1 + 1;
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+
+ buffer.put(_protocolHeader);
+ buffer.put(_protocolClass);
+ buffer.put(_protocolInstance);
+ buffer.put(_protocolMajor);
+ buffer.put(_protocolMinor);
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof ProtocolInitiation))
+ {
+ return false;
+ }
+
+ ProtocolInitiation pi = (ProtocolInitiation) o;
+ if (pi._protocolHeader == null)
+ {
+ return false;
+ }
+
+ if (_protocolHeader.length != pi._protocolHeader.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < _protocolHeader.length; i++)
+ {
+ if (_protocolHeader[i] != pi._protocolHeader[i])
+ {
+ return false;
+ }
+ }
+
+ return (_protocolClass == pi._protocolClass &&
+ _protocolInstance == pi._protocolInstance &&
+ _protocolMajor == pi._protocolMajor &&
+ _protocolMinor == pi._protocolMinor);
+ }
+
+ public static class Decoder //implements MessageDecoder
+ {
+ /**
+ *
+ * @param session the session
+ * @param in input buffer
+ * @return true if we have enough data to decode the PI frame fully, false if more
+ * data is required
+ */
+ public boolean decodable(IoSession session, ByteBuffer in)
+ {
+ return (in.remaining() >= 8);
+ }
+
+ public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out)
+ {
+ ProtocolInitiation pi = new ProtocolInitiation(in);
+ out.write(pi);
+ }
+ }
+
+ public void checkVersion() throws AMQException
+ {
+
+ if(_protocolHeader.length != 4)
+ {
+ throw new AMQProtocolHeaderException("Protocol header should have exactly four octets");
+ }
+ for(int i = 0; i < 4; i++)
+ {
+ if(_protocolHeader[i] != AMQP_HEADER[i])
+ {
+ try
+ {
+ throw new AMQProtocolHeaderException("Protocol header is not correct: Got " + new String(_protocolHeader,"ISO-8859-1") + " should be: " + new String(AMQP_HEADER, "ISO-8859-1"));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+
+ }
+ }
+ }
+ if (_protocolClass != CURRENT_PROTOCOL_CLASS)
+ {
+ throw new AMQProtocolClassException("Protocol class " + CURRENT_PROTOCOL_CLASS + " was expected; received " +
+ _protocolClass);
+ }
+ if (_protocolInstance != TCP_PROTOCOL_INSTANCE)
+ {
+ throw new AMQProtocolInstanceException("Protocol instance " + TCP_PROTOCOL_INSTANCE + " was expected; received " +
+ _protocolInstance);
+ }
+
+ ProtocolVersion pv = new ProtocolVersion(_protocolMajor, _protocolMinor);
+
+
+ if (!pv.isSupported())
+ {
+ // TODO: add list of available versions in list to msg...
+ throw new AMQProtocolVersionException("Protocol version " +
+ _protocolMajor + "." + _protocolMinor + " not suppoerted by this version of the Qpid broker.");
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer(new String(_protocolHeader));
+ buffer.append(Integer.toHexString(_protocolClass));
+ buffer.append(Integer.toHexString(_protocolInstance));
+ buffer.append(Integer.toHexString(_protocolMajor));
+ buffer.append(Integer.toHexString(_protocolMinor));
+ return buffer.toString();
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java b/Final/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java
new file mode 100644
index 0000000000..26c048e34a
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.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.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock
+{
+ private ByteBuffer _encodedBlock;
+
+ private AMQDataBlock _block;
+
+ public SmallCompositeAMQDataBlock(AMQDataBlock block)
+ {
+ _block = block;
+ }
+
+ /**
+ * The encoded block will be logically first before the AMQDataBlocks which are encoded
+ * into the buffer afterwards.
+ * @param encodedBlock already-encoded data
+ * @param block a block to be encoded.
+ */
+ public SmallCompositeAMQDataBlock(ByteBuffer encodedBlock, AMQDataBlock block)
+ {
+ this(block);
+ _encodedBlock = encodedBlock;
+ }
+
+ public AMQDataBlock getBlock()
+ {
+ return _block;
+ }
+
+ public ByteBuffer getEncodedBlock()
+ {
+ return _encodedBlock;
+ }
+
+ public long getSize()
+ {
+ long frameSize = _block.getSize();
+
+ if (_encodedBlock != null)
+ {
+ _encodedBlock.rewind();
+ frameSize += _encodedBlock.remaining();
+ }
+ return frameSize;
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ if (_encodedBlock != null)
+ {
+ buffer.put(_encodedBlock);
+ }
+ _block.writePayload(buffer);
+
+ }
+
+ public String toString()
+ {
+ if (_block == null)
+ {
+ return "No blocks contained in composite frame";
+ }
+ else
+ {
+ StringBuilder buf = new StringBuilder(this.getClass().getName());
+ buf.append("{encodedBlock=").append(_encodedBlock);
+
+ buf.append(" _block=[").append(_block.toString()).append("]");
+
+ buf.append("}");
+ return buf.toString();
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java b/Final/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java
new file mode 100644
index 0000000000..6006e9793c
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java
@@ -0,0 +1,198 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class VersionSpecificRegistry
+{
+ private static final Logger _log = LoggerFactory.getLogger(VersionSpecificRegistry.class);
+
+ private final byte _protocolMajorVersion;
+ private final byte _protocolMinorVersion;
+
+ private static final int DEFAULT_MAX_CLASS_ID = 200;
+ private static final int DEFAULT_MAX_METHOD_ID = 50;
+
+ private AMQMethodBodyInstanceFactory[][] _registry = new AMQMethodBodyInstanceFactory[DEFAULT_MAX_CLASS_ID][];
+
+ private ProtocolVersionMethodConverter _protocolVersionConverter;
+
+ public VersionSpecificRegistry(byte major, byte minor)
+ {
+ _protocolMajorVersion = major;
+ _protocolMinorVersion = minor;
+
+ _protocolVersionConverter = loadProtocolVersionConverters(major, minor);
+ }
+
+ private static ProtocolVersionMethodConverter loadProtocolVersionConverters(byte protocolMajorVersion,
+ byte protocolMinorVersion)
+ {
+ try
+ {
+ Class<ProtocolVersionMethodConverter> versionMethodConverterClass =
+ (Class<ProtocolVersionMethodConverter>) Class.forName("org.apache.qpid.framing.MethodConverter_"
+ + protocolMajorVersion + "_" + protocolMinorVersion);
+
+ return versionMethodConverterClass.newInstance();
+
+ }
+ catch (ClassNotFoundException e)
+ {
+ _log.warn("Could not find protocol conversion classes for " + protocolMajorVersion + "-" + protocolMinorVersion);
+ if (protocolMinorVersion != 0)
+ {
+ protocolMinorVersion--;
+
+ return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion);
+ }
+ else if (protocolMajorVersion != 0)
+ {
+ protocolMajorVersion--;
+
+ return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion);
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new IllegalStateException("Unable to load protocol version converter: ", e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new IllegalStateException("Unable to load protocol version converter: ", e);
+ }
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return _protocolMajorVersion;
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return _protocolMinorVersion;
+ }
+
+ public AMQMethodBodyInstanceFactory getMethodBody(final short classID, final short methodID)
+ {
+ try
+ {
+ return _registry[classID][methodID];
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ return null;
+ }
+ catch (NullPointerException e)
+ {
+ return null;
+ }
+ }
+
+ public void registerMethod(final short classID, final short methodID, final AMQMethodBodyInstanceFactory instanceFactory)
+ {
+ if (_registry.length <= classID)
+ {
+ AMQMethodBodyInstanceFactory[][] oldRegistry = _registry;
+ _registry = new AMQMethodBodyInstanceFactory[classID + 1][];
+ System.arraycopy(oldRegistry, 0, _registry, 0, oldRegistry.length);
+ }
+
+ if (_registry[classID] == null)
+ {
+ _registry[classID] =
+ new AMQMethodBodyInstanceFactory[(methodID > DEFAULT_MAX_METHOD_ID) ? (methodID + 1)
+ : (DEFAULT_MAX_METHOD_ID + 1)];
+ }
+ else if (_registry[classID].length <= methodID)
+ {
+ AMQMethodBodyInstanceFactory[] oldMethods = _registry[classID];
+ _registry[classID] = new AMQMethodBodyInstanceFactory[methodID + 1];
+ System.arraycopy(oldMethods, 0, _registry[classID], 0, oldMethods.length);
+ }
+
+ _registry[classID][methodID] = instanceFactory;
+
+ }
+
+ public AMQMethodBody get(short classID, short methodID, ByteBuffer in, long size) throws AMQFrameDecodingException
+ {
+ AMQMethodBodyInstanceFactory bodyFactory;
+ try
+ {
+ bodyFactory = _registry[classID][methodID];
+ }
+ catch (NullPointerException e)
+ {
+ throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", e);
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ if (classID >= _registry.length)
+ {
+ throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", e);
+
+ }
+ else
+ {
+ throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", e);
+
+ }
+ }
+
+ if (bodyFactory == null)
+ {
+ throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version "
+ + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID
+ + " method " + methodID + ".", null);
+ }
+
+ return bodyFactory.newInstance(_protocolMajorVersion, _protocolMinorVersion, classID, methodID, in, size);
+
+ }
+
+ public ProtocolVersionMethodConverter getProtocolVersionMethodConverter()
+ {
+ return _protocolVersionConverter;
+ }
+
+ public void configure()
+ {
+ _protocolVersionConverter.configure();
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java
new file mode 100644
index 0000000000..1c335f3036
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing.abstraction;
+
+public abstract class AbstractMethodConverter implements ProtocolVersionMethodConverter
+{
+ private final byte _protocolMajorVersion;
+
+
+ private final byte _protocolMinorVersion;
+
+ public AbstractMethodConverter(byte major, byte minor)
+ {
+ _protocolMajorVersion = major;
+ _protocolMinorVersion = minor;
+ }
+
+
+ public final byte getProtocolMajorVersion()
+ {
+ return _protocolMajorVersion;
+ }
+
+ public final byte getProtocolMinorVersion()
+ {
+ return _protocolMinorVersion;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java
new file mode 100644
index 0000000000..6312e478a8
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing.abstraction;
+
+import org.apache.mina.common.ByteBuffer;
+
+public interface ContentChunk
+{
+ int getSize();
+ ByteBuffer getData();
+
+ void reduceToFit();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java
new file mode 100644
index 0000000000..706499c1b0
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.framing.abstraction;
+
+import org.apache.qpid.framing.AMQShortString;
+
+public interface MessagePublishInfo
+{
+
+ public AMQShortString getExchange();
+
+ public boolean isImmediate();
+
+ public boolean isMandatory();
+
+ public AMQShortString getRoutingKey();
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java
new file mode 100644
index 0000000000..42e2f7ad97
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing.abstraction;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+
+public interface MessagePublishInfoConverter
+{
+ public MessagePublishInfo convertToInfo(AMQMethodBody body);
+ public AMQMethodBody convertToBody(MessagePublishInfo info);
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java
new file mode 100644
index 0000000000..99588a0908
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.framing.abstraction;
+
+import org.apache.qpid.framing.AMQBody;
+
+public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter
+{
+ AMQBody convertToBody(ContentChunk contentBody);
+ ContentChunk convertToContentChunk(AMQBody body);
+
+ void configure();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/Event.java b/Final/java/common/src/main/java/org/apache/qpid/pool/Event.java
new file mode 100644
index 0000000000..5996cbf89c
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/pool/Event.java
@@ -0,0 +1,155 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.pool;
+
+import org.apache.mina.common.IoFilter;
+import org.apache.mina.common.IoSession;
+
+/**
+ * An Event is a continuation, which is used to break a Mina filter chain and save the current point in the chain
+ * for later processing. It is an abstract class, with different implementations for continuations of different kinds
+ * of Mina events.
+ *
+ * <p/>These continuations are typically batched by {@link Job} for processing by a worker thread pool.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Process a continuation in the context of a Mina session.
+ * </table>
+ *
+ * @todo Pull up _nextFilter and getNextFilter into Event, as all events use it. Inner classes need to be non-static
+ * to use instance variables in the parent. Consequently they need to be non-inner to be instantiable outside of
+ * the context of the outer Event class. The inner class construction used here is preventing common code re-use
+ * (though not by a huge amount), but makes for an inelegent way of handling inheritance and doesn't seem like
+ * a justifiable use of inner classes. Move the inner classes out into their own files.
+ *
+ * @todo Could make Event implement Runnable, FutureTask, or a custom Continuation interface, to clarify its status as
+ * a continuation. Job is also a continuation, as is the job completion handler. Or, as Event is totally abstract,
+ * it is really an interface, so could just drop it and use the continuation interface instead.
+ */
+public abstract class Event
+{
+ /**
+ * Creates a continuation.
+ */
+ public Event()
+ { }
+
+ /**
+ * Processes the continuation in the context of a Mina session.
+ *
+ * @param session The Mina session.
+ */
+ public abstract void process(IoSession session);
+
+ /**
+ * A continuation ({@link Event}) that takes a Mina messageReceived event, and passes it to a NextFilter.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Pass a Mina messageReceived event to a NextFilter. <td> {@link IoFilter.NextFilter}, {@link IoSession}
+ * </table>
+ */
+ public static final class ReceivedEvent extends Event
+ {
+ private final Object _data;
+
+ private final IoFilter.NextFilter _nextFilter;
+
+ public ReceivedEvent(final IoFilter.NextFilter nextFilter, final Object data)
+ {
+ super();
+ _nextFilter = nextFilter;
+ _data = data;
+ }
+
+ public void process(IoSession session)
+ {
+ _nextFilter.messageReceived(session, _data);
+ }
+
+ public IoFilter.NextFilter getNextFilter()
+ {
+ return _nextFilter;
+ }
+ }
+
+ /**
+ * A continuation ({@link Event}) that takes a Mina filterWrite event, and passes it to a NextFilter.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Pass a Mina filterWrite event to a NextFilter.
+ * <td> {@link IoFilter.NextFilter}, {@link IoFilter.WriteRequest}, {@link IoSession}
+ * </table>
+ */
+ public static final class WriteEvent extends Event
+ {
+ private final IoFilter.WriteRequest _data;
+ private final IoFilter.NextFilter _nextFilter;
+
+ public WriteEvent(final IoFilter.NextFilter nextFilter, final IoFilter.WriteRequest data)
+ {
+ super();
+ _nextFilter = nextFilter;
+ _data = data;
+ }
+
+ public void process(IoSession session)
+ {
+ _nextFilter.filterWrite(session, _data);
+ }
+
+ public IoFilter.NextFilter getNextFilter()
+ {
+ return _nextFilter;
+ }
+ }
+
+ /**
+ * A continuation ({@link Event}) that takes a Mina sessionClosed event, and passes it to a NextFilter.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Pass a Mina sessionClosed event to a NextFilter. <td> {@link IoFilter.NextFilter}, {@link IoSession}
+ * </table>
+ */
+ public static final class CloseEvent extends Event
+ {
+ private final IoFilter.NextFilter _nextFilter;
+
+ public CloseEvent(final IoFilter.NextFilter nextFilter)
+ {
+ super();
+ _nextFilter = nextFilter;
+ }
+
+ public void process(IoSession session)
+ {
+ _nextFilter.sessionClosed(session);
+ }
+
+ public IoFilter.NextFilter getNextFilter()
+ {
+ return _nextFilter;
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/Job.java b/Final/java/common/src/main/java/org/apache/qpid/pool/Job.java
new file mode 100644
index 0000000000..ba3c5d03fa
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/pool/Job.java
@@ -0,0 +1,162 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.pool;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.mina.common.IoSession;
+
+/**
+ * A Job is a continuation that batches together other continuations, specifically {@link Event}s, into one continuation.
+ * The {@link Event}s themselves provide methods to process themselves, so processing a job simply consists of sequentially
+ * processing all of its aggregated events.
+ *
+ * The constructor accepts a maximum number of events for the job, and only runs up to that maximum number when
+ * processing the job, but the add method does not enforce this maximum. In other words, not all the enqueued events
+ * may be processed in each run of the job, several runs may be required to clear the queue.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Aggregate many coninuations together into a single continuation.
+ * <tr><td> Sequentially process aggregated continuations. <td> {@link Event}
+ * <tr><td> Provide running and completion status of the aggregate continuation.
+ * <tr><td> Execute a terminal continuation upon job completion. <td> {@link JobCompletionHandler}
+ * </table>
+ *
+ * @todo Could make Job implement Runnable, FutureTask, or a custom Continuation interface, to clarify its status as a
+ * continuation. Job is a continuation that aggregates other continuations and as such is a usefull re-usable
+ * piece of code. There may be other palces than the mina filter chain where continuation batching is used within
+ * qpid, so abstracting this out could provide a usefull building block. This also opens the way to different
+ * kinds of job with a common interface, e.g. parallel or sequential jobs etc.
+ *
+ * @todo For better re-usability could make the completion handler optional. Only run it when one is set.
+ */
+public class Job implements Runnable
+{
+ /** The maximum number of events to process per run of the job. More events than this may be queued in the job. */
+ private final int _maxEvents;
+
+ /** The Mina session. */
+ private final IoSession _session;
+
+ /** Holds the queue of events that make up the job. */
+ private final java.util.Queue<Event> _eventQueue = new ConcurrentLinkedQueue<Event>();
+
+ /** Holds a status flag, that indicates when the job is actively running. */
+ private final AtomicBoolean _active = new AtomicBoolean();
+
+ /** Holds the completion continuation, called upon completion of a run of the job. */
+ private final JobCompletionHandler _completionHandler;
+
+ /**
+ * Creates a new job that aggregates many continuations together.
+ *
+ * @param session The Mina session.
+ * @param completionHandler The per job run, terminal continuation.
+ * @param maxEvents The maximum number of aggregated continuations to process per run of the job.
+ */
+ Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents)
+ {
+ _session = session;
+ _completionHandler = completionHandler;
+ _maxEvents = maxEvents;
+ }
+
+ /**
+ * Enqueus a continuation for sequential processing by this job.
+ *
+ * @param evt The continuation to enqueue.
+ */
+ void add(Event evt)
+ {
+ _eventQueue.add(evt);
+ }
+
+ /**
+ * Sequentially processes, up to the maximum number per job, the aggregated continuations in enqueued in this job.
+ */
+ void processAll()
+ {
+ // limit the number of events processed in one run
+ for (int i = 0; i < _maxEvents; i++)
+ {
+ Event e = _eventQueue.poll();
+ if (e == null)
+ {
+ break;
+ }
+ else
+ {
+ e.process(_session);
+ }
+ }
+ }
+
+ /**
+ * Tests if there are no more enqueued continuations to process.
+ *
+ * @return <tt>true</tt> if there are no enqueued continuations in this job, <tt>false</tt> otherwise.
+ */
+ public boolean isComplete()
+ {
+ return _eventQueue.peek() == null;
+ }
+
+ /**
+ * Marks this job as active if it is inactive. This method is thread safe.
+ *
+ * @return <tt>true</tt> if this job was inactive and has now been marked as active, <tt>false</tt> otherwise.
+ */
+ public boolean activate()
+ {
+ return _active.compareAndSet(false, true);
+ }
+
+ /**
+ * Marks this job as inactive. This method is thread safe.
+ */
+ public void deactivate()
+ {
+ _active.set(false);
+ }
+
+ /**
+ * Processes a batch of aggregated continuations, marks this job as inactive and call the terminal continuation.
+ */
+ public void run()
+ {
+ processAll();
+ deactivate();
+ _completionHandler.completed(_session, this);
+ }
+
+ /**
+ * Another interface for a continuation.
+ *
+ * @todo Get rid of this interface as there are other interfaces that could be used instead, such as FutureTask,
+ * Runnable or a custom Continuation interface.
+ */
+ static interface JobCompletionHandler
+ {
+ public void completed(IoSession session, Job job);
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/Final/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
new file mode 100644
index 0000000000..cbe08a192e
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
@@ -0,0 +1,471 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.pool;
+
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoSession;
+
+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;
+
+/**
+ * PoolingFilter, is a no-op pass through filter that hands all events down the Mina filter chain by default. As it
+ * adds no behaviour by default to the filter chain, it is abstract.
+ *
+ * <p/>PoolingFilter provides a capability, available to sub-classes, to handle events in the chain asynchronously, by
+ * adding them to a job. If a job is not active, adding an event to it activates it. If it is active, the event is
+ * added to the job, which will run to completion and eventually process the event. The queue on the job itself acts as
+ * a buffer between stages of the pipeline.
+ *
+ * <p/>There are two convenience methods, {@link #createAynschReadPoolingFilter} and
+ * {@link #createAynschWritePoolingFilter}, for obtaining pooling filters that handle 'messageReceived' and
+ * 'filterWrite' events, making it possible to process these event streams seperately.
+ *
+ * <p/>Pooling filters have a name, in order to distinguish different filter types. They set up a {@link Job} on the
+ * Mina session they are working with, and store it in the session against their identifying name. This allows different
+ * filters with different names to be set up on the same filter chain, on the same Mina session, that batch their
+ * workloads in different jobs.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Implement default, pass through filter.
+ * <tr><td> Create pooling filters and a specific thread pool. <td> {@link ReferenceCountingExecutorService}
+ * <tr><td> Provide the ability to batch Mina events for asynchronous processing. <td> {@link Job}, {@link Event}
+ * <tr><td> Provide a terminal continuation to keep jobs running till empty.
+ * <td> {@link Job}, {@link Job.JobCompletionHandler}
+ * </table>
+ *
+ * @todo This seems a bit bizarre. ReadWriteThreadModel creates seperate pooling filters for read and write events.
+ * The pooling filters themselves batch read and write events into jobs, but hand these jobs to a common thread
+ * pool for execution. So the same thread pool ends up handling read and write events, albeit with many threads
+ * so there is concurrency. But why go to the trouble of seperating out the read and write events in that case?
+ * Why not just batch them into jobs together? Perhaps its so that seperate thread pools could be used for these
+ * stages.
+ *
+ * @todo Why set an event limit of 10 on the Job? This also seems bizarre, as the job can have more than 10 events in
+ * it. Its just that it runs them 10 at a time, but the completion hander here checks if there are more to run
+ * and trips off another batch of 10 until they are all done. Why not just have a straight forward
+ * consumer/producer queue scenario without the batches of 10? So instead of having many jobs with batches of 10
+ * in them, just have one queue of events and worker threads taking the next event. There will be coordination
+ * between worker threads and new events arriving on the job anyway, so the simpler scenario may have the same
+ * amount of contention. I can see that the batches of 10 is done, so that no job is allowed to hog the worker
+ * pool for too long. I'm not convinced this fairly complex scheme will actually add anything, and it might be
+ * better to encapsulate it under a Queue interface anyway, so that different queue implementations can easily
+ * be substituted in.
+ *
+ * @todo The static helper methods are pointless. Could just call new.
+ */
+public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCompletionHandler
+{
+ /** 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;
+
+ /** Used to hold a name for identifying differeny pooling filter types. */
+ 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);
+
+ /**
+ * Creates a named pooling filter, on the specified shared thread pool.
+ *
+ * @param refCountingPool The thread pool reference.
+ * @param name The identifying name of the filter type.
+ */
+ public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
+ {
+ _poolReference = refCountingPool;
+ _name = name;
+ }
+
+ /**
+ * Helper method to get an instance of a pooling filter that handles read events asynchronously.
+ *
+ * @param refCountingPool A managed reference to the thread pool.
+ * @param name The filter types identifying name.
+ *
+ * @return A pooling filter for asynchronous read events.
+ */
+ public static PoolingFilter createAynschReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
+ {
+ return new AsynchReadPoolingFilter(refCountingPool, name);
+ }
+
+ /**
+ * Helper method to get an instance of a pooling filter that handles write events asynchronously.
+ *
+ * @param refCountingPool A managed reference to the thread pool.
+ * @param name The filter types identifying name.
+ *
+ * @return A pooling filter for asynchronous write events.
+ */
+ public static PoolingFilter createAynschWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
+ {
+ return new AsynchWritePoolingFilter(refCountingPool, name);
+ }
+
+ /**
+ * Called by Mina to initialize this filter. Takes a reference to the thread pool.
+ */
+ public void init()
+ {
+ _logger.debug("Init called on PoolingFilter " + toString());
+
+ // Called when the filter is initialised in the chain. If the reference count is
+ // zero this acquire will initialise the pool.
+ _poolReference.acquireExecutorService();
+ }
+
+ /**
+ * Called by Mina to clean up this filter. Releases the reference to the thread pool.
+ */
+ public void destroy()
+ {
+ _logger.debug("Destroy called on PoolingFilter " + toString());
+
+ // When the reference count gets to zero we release the executor service.
+ _poolReference.releaseExecutorService();
+ }
+
+ /**
+ * 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 event The event to hand off asynchronously.
+ */
+ void fireAsynchEvent(IoSession session, 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())
+ {
+ _poolReference.getPool().execute(job);
+ }
+
+ }
+
+ /**
+ * Creates a Job on the Mina session, identified by this filters name, in which this filter places asynchronously
+ * handled events.
+ *
+ * @param session The Mina session.
+ */
+ public void createNewJobForSession(IoSession session)
+ {
+ Job job = new Job(session, this, _maxEvents);
+ session.setAttribute(_name, job);
+ }
+
+ /**
+ * Retrieves this filters Job, by this filters name, from the Mina session.
+ *
+ * @param session The Mina session.
+ *
+ * @return The Job for this filter to place asynchronous events into.
+ */
+ private Job getJobForSession(IoSession session)
+ {
+ return (Job) session.getAttribute(_name);
+ }
+
+ /*private Job createJobForSession(IoSession session)
+ {
+ return addJobForSession(session, new Job(session, this, _maxEvents));
+ }*/
+
+ /*private Job addJobForSession(IoSession session, Job job)
+ {
+ // atomic so ensures all threads agree on the same job
+ Job existing = _jobs.putIfAbsent(session, job);
+
+ return (existing == null) ? job : existing;
+ }*/
+
+ /**
+ * Implements a terminal continuation for the {@link Job} for this filter. Whenever the Job completes its processing
+ * of a batch of events this is called. This method simply re-activates the job, if it has more events to process.
+ *
+ * @param session The Mina session to work in.
+ * @param job The job that completed.
+ */
+ public void completed(IoSession session, Job job)
+ {
+ // if (job.isComplete())
+ // {
+ // job.release();
+ // if (!job.isReferenced())
+ // {
+ // _jobs.remove(session);
+ // }
+ // }
+ // else
+ if (!job.isComplete())
+ {
+ // 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())
+ {
+ _poolReference.getPool().execute(job);
+ }
+ }
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void sessionOpened(final NextFilter nextFilter, final IoSession session) throws Exception
+ {
+ nextFilter.sessionOpened(session);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void sessionClosed(final NextFilter nextFilter, final IoSession session) throws Exception
+ {
+ nextFilter.sessionClosed(session);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ * @param status The session idle status.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void sessionIdle(final NextFilter nextFilter, final IoSession session, final IdleStatus status) throws Exception
+ {
+ nextFilter.sessionIdle(session, status);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ * @param cause The underlying exception.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void exceptionCaught(final NextFilter nextFilter, final IoSession session, final Throwable cause) throws Exception
+ {
+ nextFilter.exceptionCaught(session, cause);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ * @param message The message received.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void messageReceived(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception
+ {
+ nextFilter.messageReceived(session, message);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ * @param message The message sent.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void messageSent(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception
+ {
+ nextFilter.messageSent(session, message);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ * @param writeRequest The write request event.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest)
+ throws Exception
+ {
+ nextFilter.filterWrite(session, writeRequest);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void filterClose(NextFilter nextFilter, IoSession session) throws Exception
+ {
+ nextFilter.filterClose(session);
+ }
+
+ /**
+ * No-op pass through filter to the next filter in the chain.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ *
+ * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow
+ * overriding sub-classes the ability to.
+ */
+ public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception
+ {
+ nextFilter.sessionCreated(session);
+ }
+
+ /**
+ * Prints the filter types identifying name to a string, mainly for debugging purposes.
+ *
+ * @return The filter types identifying name.
+ */
+ public String toString()
+ {
+ return _name;
+ }
+
+ /**
+ * AsynchReadPoolingFilter is a pooling filter that handles 'messageReceived' and 'sessionClosed' events
+ * asynchronously.
+ */
+ public static class AsynchReadPoolingFilter extends PoolingFilter
+ {
+ /**
+ * Creates a pooling filter that handles read events asynchronously.
+ *
+ * @param refCountingPool A managed reference to the thread pool.
+ * @param name The filter types identifying name.
+ */
+ public AsynchReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
+ {
+ super(refCountingPool, name);
+ }
+
+ /**
+ * Hands off this event for asynchronous execution.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ * @param message The message received.
+ */
+ public void messageReceived(NextFilter nextFilter, final IoSession session, Object message)
+ {
+
+ fireAsynchEvent(session, new Event.ReceivedEvent(nextFilter, message));
+ }
+
+ /**
+ * Hands off this event for asynchronous execution.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ */
+ public void sessionClosed(final NextFilter nextFilter, final IoSession session)
+ {
+ fireAsynchEvent(session, new CloseEvent(nextFilter));
+ }
+ }
+
+ /**
+ * AsynchWritePoolingFilter is a pooling filter that handles 'filterWrite' and 'sessionClosed' events
+ * asynchronously.
+ */
+ public static class AsynchWritePoolingFilter extends PoolingFilter
+ {
+ /**
+ * Creates a pooling filter that handles write events asynchronously.
+ *
+ * @param refCountingPool A managed reference to the thread pool.
+ * @param name The filter types identifying name.
+ */
+ public AsynchWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
+ {
+ super(refCountingPool, name);
+ }
+
+ /**
+ * Hands off this event for asynchronous execution.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ * @param writeRequest The write request event.
+ */
+ public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest)
+ {
+ fireAsynchEvent(session, new Event.WriteEvent(nextFilter, writeRequest));
+ }
+
+ /**
+ * Hands off this event for asynchronous execution.
+ *
+ * @param nextFilter The next filter in the chain.
+ * @param session The Mina session.
+ */
+ public void sessionClosed(final NextFilter nextFilter, final IoSession session)
+ {
+ fireAsynchEvent(session, new CloseEvent(nextFilter));
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java b/Final/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java
new file mode 100644
index 0000000000..8cea70e597
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.pool;
+
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.ThreadModel;
+import org.apache.mina.filter.ReferenceCountingIoFilter;
+
+/**
+ * ReadWriteThreadModel is a Mina i/o filter chain factory, which creates a filter chain with seperate filters to
+ * handle read and write events. The seperate filters are {@link PoolingFilter}s, which have thread pools to handle
+ * these events. The effect of this is that reading and writing may happen concurrently.
+ *
+ * <p/>Socket i/o will only happen with concurrent reads and writes if Mina has seperate selector threads for each.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create a filter chain with seperate read and write thread pools for read/write Mina events.
+ * <td> {@link PoolingFilter}
+ * </table>
+ */
+public class ReadWriteThreadModel implements ThreadModel
+{
+ /** Holds the singleton instance of this factory. */
+ private static final ReadWriteThreadModel _instance = new ReadWriteThreadModel();
+
+ /** Holds the thread pooling filter for reads. */
+ private final PoolingFilter _asynchronousReadFilter;
+
+ /** Holds the thread pooloing filter for writes. */
+ private final PoolingFilter _asynchronousWriteFilter;
+
+ /**
+ * Creates a new factory for concurrent i/o, thread pooling filter chain construction. This is private, so that
+ * only a singleton instance of the factory is ever created.
+ */
+ private ReadWriteThreadModel()
+ {
+ final ReferenceCountingExecutorService executor = ReferenceCountingExecutorService.getInstance();
+ _asynchronousReadFilter = PoolingFilter.createAynschReadPoolingFilter(executor, "AsynchronousReadFilter");
+ _asynchronousWriteFilter = PoolingFilter.createAynschWritePoolingFilter(executor, "AsynchronousWriteFilter");
+ }
+
+ /**
+ * Gets the singleton instance of this filter chain factory.
+ *
+ * @return The singleton instance of this filter chain factory.
+ */
+ public static ReadWriteThreadModel getInstance()
+ {
+ return _instance;
+ }
+
+ /**
+ * Gets the read filter.
+ *
+ * @return The read filter.
+ */
+ public PoolingFilter getAsynchronousReadFilter()
+ {
+ return _asynchronousReadFilter;
+ }
+
+ /**
+ * Gets the write filter.
+ *
+ * @return The write filter.
+ */
+ public PoolingFilter getAsynchronousWriteFilter()
+ {
+ return _asynchronousWriteFilter;
+ }
+
+ /**
+ * Adds the concurrent read and write filters to a filter chain.
+ *
+ * @param chain The Mina filter chain to add to.
+ */
+ public void buildFilterChain(IoFilterChain chain)
+ {
+ chain.addFirst("AsynchronousReadFilter", new ReferenceCountingIoFilter(_asynchronousReadFilter));
+ chain.addLast("AsynchronousWriteFilter", new ReferenceCountingIoFilter(_asynchronousWriteFilter));
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java b/Final/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java
new file mode 100644
index 0000000000..84c9e1f465
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.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.pool;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * ReferenceCountingExecutorService wraps an ExecutorService in order to provide shared reference to it. It counts
+ * the references taken, instantiating the service on the first reference, and shutting it down when the last
+ * reference is released.
+ *
+ * <p/>It is important to ensure that an executor service is correctly shut down as failing to do so prevents the JVM
+ * from terminating due to the existence of non-daemon threads.
+ *
+ * <p/><table id="crc><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a shared exector service. <td> {@link Executors}
+ * <tr><td> Shutdown the executor service when not needed. <td> {@link ExecutorService}
+ * <tr><td> Track references to the executor service.
+ * <tr><td> Provide configuration of the executor service.
+ * </table>
+ *
+ * @todo Might be more elegant to make this actually implement ExecutorService, providing better hiding of the
+ * implementation details. Also this class introduces a pattern (albeit specific to this usage) that could be
+ * generalized to reference count anything. That is, on first instance call a create method, on release of last
+ * instance call a destroy method. This could definitely be abstracted out as a re-usable piece of code; a
+ * reference counting factory. It could then be re-used to do reference counting in other places (such as
+ * messages). Countable objects have a simple create/destroy life cycle, capturable by an interface that the
+ * ref counting factory can call to manage the lifecycle.
+ *
+ * @todo {@link #_poolSize} should be static?
+ *
+ * @todo The {@link #getPool()} method breaks the encapsulation of the reference counter. Generally when getPool is used
+ * further checks are applied to ensure that the exector service has not been shutdown. This passes responsibility
+ * for managing the lifecycle of the reference counted object onto the caller rather than neatly encapsulating it
+ * here. Could think about adding more state to the lifecycle, to mark ref counted objects as invalid, and have an
+ * isValid method, or could make calling code deal with RejectedExecutionException raised by shutdown executors.
+ */
+public class ReferenceCountingExecutorService
+{
+ /** Defines the smallest thread pool that will be allocated, irrespective of the number of processors. */
+ private static final int MINIMUM_POOL_SIZE = 4;
+
+ /** Holds the number of processors on the machine. */
+ private static final int NUM_CPUS = Runtime.getRuntime().availableProcessors();
+
+ /** Defines the thread pool size to use, which is the larger of the number of CPUs or the minimum size. */
+ private static final int DEFAULT_POOL_SIZE = Math.max(NUM_CPUS, MINIMUM_POOL_SIZE);
+
+ /**
+ * Holds the singleton instance of this reference counter. This is only created once, statically, so the
+ * {@link #getInstance()} method does not need to be synchronized.
+ */
+ private static final ReferenceCountingExecutorService _instance = new ReferenceCountingExecutorService();
+
+ /** This lock is used to ensure that reference counts are updated atomically with create/destroy operations. */
+ private final Object _lock = new Object();
+
+ /** The shared executor service that is reference counted. */
+ private ExecutorService _pool;
+
+ /** Holds the number of references given out to the executor service. */
+ private int _refCount = 0;
+
+ /** Holds the number of executor threads to create. */
+ private int _poolSize = Integer.getInteger("amqj.read_write_pool_size", DEFAULT_POOL_SIZE);
+
+ /**
+ * Retrieves the singleton instance of this reference counter.
+ *
+ * @return The singleton instance of this reference counter.
+ */
+ public static ReferenceCountingExecutorService getInstance()
+ {
+ return _instance;
+ }
+
+ /**
+ * Private constructor to ensure that only a singleton instance can be created.
+ */
+ private ReferenceCountingExecutorService()
+ { }
+
+ /**
+ * Provides a reference to a shared executor service, incrementing the reference count.
+ *
+ * @return An executor service.
+ */
+ ExecutorService acquireExecutorService()
+ {
+ synchronized (_lock)
+ {
+ if (_refCount++ == 0)
+ {
+ _pool = Executors.newFixedThreadPool(_poolSize);
+ }
+
+ return _pool;
+ }
+ }
+
+ /**
+ * Releases a reference to a shared executor service, decrementing the reference count. If the refence count falls
+ * to zero, the executor service is shut down.
+ */
+ void releaseExecutorService()
+ {
+ synchronized (_lock)
+ {
+ if (--_refCount == 0)
+ {
+ _pool.shutdownNow();
+ }
+ }
+ }
+
+ /**
+ * Provides access to the executor service, without touching the reference count.
+ *
+ * @return The shared executor service, or <tt>null</tt> if none has been instantiated yet.
+ */
+ public ExecutorService getPool()
+ {
+ return _pool;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java
new file mode 100644
index 0000000000..375df2a45d
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java
@@ -0,0 +1,227 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.protocol;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.qpid.framing.AMQShortString;
+
+/**
+ * Defines constants for AMQP codes and also acts as a factory for creating such constants from the raw codes. Each
+ * constant also defines a short human readable description of the constant.
+ *
+ * @todo Why would a constant be defined that is not in the map? Seems more natural that getConstant should raise an
+ * exception for an unknown constant. Or else provide an explanation of why this is so. Also, there is no way for
+ * callers to determine the unknown status of a code except by comparing its name to "unknown code", which would
+ * seem to render this scheme a little bit pointless?
+ *
+ * @todo Java has a nice enum construct for doing this sort of thing. Maybe this is done in the old style for Java 1.4
+ * backward compatability? Now that is handled through retrotranslater it may be time to use enum.
+ *
+ * <p/><tabld id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Define the set of AMQP status codes.
+ * <tr><td> Provide a factory to lookup constants by their code.
+ * <tr><td>
+ */
+public final class AMQConstant
+{
+ /** Defines a map from codes to constants. */
+ private static Map _codeMap = new HashMap();
+
+ /** Indicates that the method completed successfully. */
+ public static final AMQConstant REPLY_SUCCESS = new AMQConstant(200, "reply success", true);
+
+ public static final AMQConstant FRAME_END = new AMQConstant(206, "frame end", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant NOT_DELIVERED = new AMQConstant(310, "not delivered", true);
+
+ /**
+ * The client attempted to transfer content larger than the server could accept at the present time. The client
+ * may retry at a later time.
+ */
+ public static final AMQConstant MESSAGE_TOO_LARGE = new AMQConstant(311, "message too large", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant NO_ROUTE = new AMQConstant(312, "no route", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant NO_CONSUMERS = new AMQConstant(313, "no consumers", true);
+
+ /**
+ * An operator intervened to close the connection for some reason. The client may retry at some later date.
+ */
+ public static final AMQConstant CONTEXT_IN_USE = new AMQConstant(320, "context in use", true);
+
+ /** The client tried to work with an unknown virtual host or cluster. */
+ public static final AMQConstant INVALID_PATH = new AMQConstant(402, "invalid path", true);
+
+ /** The client attempted to work with a server entity to which it has no access due to security settings. */
+ public static final AMQConstant ACCESS_REFUSED = new AMQConstant(403, "access refused", true);
+
+ /** The client attempted to work with a server entity that does not exist. */
+ public static final AMQConstant NOT_FOUND = new AMQConstant(404, "not found", true);
+
+ /**
+ * The client attempted to work with a server entity to which it has no access because another client is
+ * working with it.
+ */
+ public static final AMQConstant ALREADY_EXISTS = new AMQConstant(405, "Already exists", true);
+
+ /** The client requested a method that was not allowed because some precondition failed. */
+ public static final AMQConstant IN_USE = new AMQConstant(406, "In use", true);
+
+ public static final AMQConstant INVALID_ROUTING_KEY = new AMQConstant(407, "routing key invalid", true);
+
+ public static final AMQConstant REQUEST_TIMEOUT = new AMQConstant(408, "Request Timeout", true);
+
+ public static final AMQConstant INVALID_ARGUMENT = new AMQConstant(409, "argument invalid", true);
+
+ /**
+ * The client sent a malformed frame that the server could not decode. This strongly implies a programming error
+ * in the client.
+ */
+ public static final AMQConstant FRAME_ERROR = new AMQConstant(501, "frame error", true);
+
+ /**
+ * The client sent a frame that contained illegal values for one or more fields. This strongly implies a
+ * programming error in the client.
+ */
+ public static final AMQConstant SYNTAX_ERROR = new AMQConstant(502, "syntax error", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant COMMAND_INVALID = new AMQConstant(503, "command invalid", true);
+
+ /**
+ * The client attempted to work with a channel that had not been correctly opened. This most likely indicates a
+ * fault in the client layer.
+ */
+ public static final AMQConstant CHANNEL_ERROR = new AMQConstant(504, "channel error", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant RESOURCE_ERROR = new AMQConstant(506, "resource error", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant NOT_ALLOWED = new AMQConstant(530, "not allowed", true);
+
+ /** The client tried to use functionality that is not implemented in the server. */
+ public static final AMQConstant NOT_IMPLEMENTED = new AMQConstant(540, "not implemented", true);
+
+ /**
+ * 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.
+ */
+ public static final AMQConstant INTERNAL_ERROR = new AMQConstant(541, "internal error", true);
+
+ public static final AMQConstant FRAME_MIN_SIZE = new AMQConstant(4096, "frame min size", true);
+
+ /** The AMQP status code. */
+ private int _code;
+
+ /** A short description of the status code. */
+ private AMQShortString _name;
+
+ /**
+ * Creates a new AMQP status code.
+ *
+ * @param code The code.
+ * @param name A short description of the code.
+ * @param map <tt>true</tt> to register the code as a known code, <tt>false</tt> otherwise.
+ */
+ private AMQConstant(int code, String name, boolean map)
+ {
+ _code = code;
+ _name = new AMQShortString(name);
+ if (map)
+ {
+ _codeMap.put(new Integer(code), this);
+ }
+ }
+
+ /**
+ * Creates a constant for a status code by looking up the code in the map of known codes. If the code is not known
+ * a constant is still created for it, but it is marked as unknown.
+ *
+ * @param code The AMQP status code.
+ *
+ * @return The AMQP status code encapsulated as a constant.
+ */
+ public static AMQConstant getConstant(int code)
+ {
+ AMQConstant c = (AMQConstant) _codeMap.get(new Integer(code));
+ if (c == null)
+ {
+ c = new AMQConstant(code, "unknown code", false);
+ }
+
+ return c;
+ }
+
+ /**
+ * Gets the underlying AMQP status code.
+ *
+ * @return The AMQP status code.
+ */
+ public int getCode()
+ {
+ return _code;
+ }
+
+ /**
+ * Gets a short description of the status code.
+ *
+ * @return A short description of the status code.
+ */
+ public AMQShortString getName()
+ {
+ return _name;
+ }
+
+ /**
+ * Renders the constant as a string, mainly for debugging purposes.
+ *
+ * @return The status code and its description.
+ */
+ public String toString()
+ {
+ return _code + ": " + _name;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java
new file mode 100644
index 0000000000..fd6907a152
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.protocol;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+/**
+ * AMQMethodEvent encapsulates an AMQP method call, and the channel on which that method call occurred.
+ *
+ * <p/>Supplies the:
+ * <ul>
+ * <li>channel id</li>
+ * <li>protocol method</li>
+ * </ul>
+ *
+ * <p/>As the event contains the context in which it occurred, event listeners do not need to be statefull.
+ * to listeners. Events are often handled by {@link AMQMethodListener}s.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Encapsulate an AMQP method call and the channel as the context for the method call.
+ * </table>
+ */
+public class AMQMethodEvent<M extends AMQMethodBody>
+{
+ /** Holds the method call. */
+ private final M _method;
+
+ /** Holds the channel handle for the method call. */
+ private final int _channelId;
+
+ /**
+ * Creates a method event to encasulate a method call and channel.
+ *
+ * @param channelId The channel on which the method call occurred.
+ * @param method The method call.
+ */
+ public AMQMethodEvent(int channelId, M method)
+ {
+ _channelId = channelId;
+ _method = method;
+ }
+
+ /**
+ * Gets the method call.
+ *
+ * @return The method call.
+ */
+ public M getMethod()
+ {
+ return _method;
+ }
+
+ /**
+ * Gets the channel handle for the method call.
+ *
+ * @return The channel handle for the method call.
+ */
+ public int getChannelId()
+ {
+ return _channelId;
+ }
+
+ /**
+ * Prints the method call as a string, mainly for debugging purposes.
+ *
+ * @return The method call as a string, mainly for debugging purposes.
+ */
+ public String toString()
+ {
+ StringBuilder buf = new StringBuilder("Method event: ");
+ buf.append("\nChannel id: ").append(_channelId);
+ buf.append("\nMethod: ").append(_method);
+
+ return buf.toString();
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java
new file mode 100644
index 0000000000..2fbeeda1d4
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java
@@ -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.
+ *
+ */
+package org.apache.qpid.protocol;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+/**
+ * AMQMethodListener is a listener that receives notifications of AMQP methods. The methods are packaged as events in
+ * {@link AMQMethodEvent}.
+ *
+ * <p/>An event listener may be associated with a particular context, usually an AMQP channel, and in addition to
+ * receiving method events will be notified of errors on that context. This enables listeners to perform any clean
+ * up that they need to do before the context is closed or retried.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Accept notification of AMQP method events. <td> {@link AMQMethodEvent}
+ * <tr><td> Accept notification of errors on the event context.
+ * </table>
+ *
+ * @todo Document why the exception is passed to the error method. Is it so that the exception can be passed
+ * from the event handling thread to another thread and rethown from there? It is unusual to pass exceptions as
+ * method arguments, because they have their own mechanism for propagating through the call stack, so some
+ * explanation ought to be provided.
+ */
+public interface AMQMethodListener
+{
+ /**
+ * Notifies the listener that an AMQP method event has occurred.
+ *
+ * @param evt The AMQP method event (contains the method and channel).
+ *
+ * @return <tt>true</tt> if the handler processes the method frame, <tt>false<tt> otherwise. Note that this does
+ * not prohibit the method event being delivered to subsequent listeners but can be used to determine if
+ * nobody has dealt with an incoming method frame.
+ *
+ * @throws Exception if an error has occurred. This exception may be delivered to all registered listeners using
+ * the error() method (see below) allowing them to perform cleanup if necessary.
+ *
+ * @todo Consider narrowing the exception.
+ */
+ <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt) throws Exception;
+
+ /**
+ * Notifies the listener of an error on the event context to which it is listening. The listener should perform
+ * any necessary clean-up for the context.
+ *
+ * @param e The underlying exception that is the source of the error.
+ */
+ void error(Exception e);
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java
new file mode 100644
index 0000000000..65884e4950
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.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.protocol;
+
+import org.apache.qpid.framing.AMQDataBlock;
+
+/**
+ * AMQProtocolWriter provides a method to write a frame of data 'to the wire', in the context of the object
+ * that implements the method, usually some sort of session. The block of data, encapsulated by {@link AMQDataBlock},
+ * will be encoded as it is written.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Write an encoded block of data to the write, in the context of a session.
+ * </table>
+ */
+public interface AMQProtocolWriter
+{
+ /**
+ * Writes a frame to the wire, encoding it as necessary, for example, into a sequence of bytes.
+ *
+ * @param frame The frame to be encoded and written.
+ */
+ public void writeFrame(AMQDataBlock frame);
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java
new file mode 100644
index 0000000000..7c1d6fdaa0
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.protocol;
+
+import org.apache.qpid.framing.VersionSpecificRegistry;
+
+/**
+ * AMQVersionAwareProtocolSession is implemented by all AMQP session classes, that need to provide an awareness to
+ * callers of the version of the AMQP protocol that they are able to work with.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide the method registry for a specific version of the AMQP.
+ * </table>
+ *
+ * @todo Why is this a seperate interface to {@link ProtocolVersionAware}, could they be combined into a single
+ * interface and one of them eliminated? Move getRegistry method to ProtocolVersionAware, make the sessions
+ * implement AMQProtocolWriter directly and drop this interface.
+ */
+public interface AMQVersionAwareProtocolSession extends AMQProtocolWriter, ProtocolVersionAware
+{
+ /**
+ * Gets the method registry for a specific version of the AMQP.
+ *
+ * @return The method registry for a specific version of the AMQP.
+ */
+ public VersionSpecificRegistry getRegistry();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java b/Final/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java
new file mode 100644
index 0000000000..60a7f30185
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java
@@ -0,0 +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.protocol;
+
+/**
+ * ProtocolVersionAware is implemented by all AMQP handling classes, that need to provide an awareness to callers of
+ * the version of the AMQP protocol that they are able to handle.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Report the major and minor AMQP version handled.
+ * </table>
+ */
+public interface ProtocolVersionAware
+{
+ /**
+ * Reports the AMQP minor version, that the implementer can handle.
+ *
+ * @return The AMQP minor version.
+ */
+ public byte getProtocolMinorVersion();
+
+ /**
+ * Reports the AMQP major version, that the implementer can handle.
+ *
+ * @return The AMQP major version.
+ */
+ public byte getProtocolMajorVersion();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/Final/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java
new file mode 100644
index 0000000000..950279fff1
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.ssl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
+/**
+ * Factory used to create SSLContexts. SSL needs to be configured
+ * before this will work.
+ *
+ */
+public class SSLContextFactory {
+
+ /**
+ * Path to the Java keystore file
+ */
+ private String _keystorePath;
+
+ /**
+ * Password for the keystore
+ */
+ private String _keystorePassword;
+
+ /**
+ * Cert type to use
+ */
+ private String _certType;
+
+ /**
+ * Create a factory instance
+ * @param keystorePath path to the Java keystore file
+ * @param keystorePassword password for the Java keystore
+ * @param certType certificate type
+ */
+ public SSLContextFactory(String keystorePath, String keystorePassword,
+ String certType)
+ {
+ _keystorePath = keystorePath;
+ _keystorePassword = keystorePassword;
+ if (_keystorePassword.equals("none"))
+ {
+ _keystorePassword = null;
+ }
+ _certType = certType;
+ if (keystorePath == null) {
+ throw new IllegalArgumentException("Keystore path must be specified");
+ }
+ if (certType == null) {
+ throw new IllegalArgumentException("Cert type must be specified");
+ }
+ }
+
+ /**
+ * Builds a SSLContext appropriate for use with a server
+ * @return SSLContext
+ * @throws GeneralSecurityException
+ * @throws IOException
+ */
+ public SSLContext buildServerContext() throws GeneralSecurityException, IOException
+ {
+ // Create keystore
+ KeyStore ks = getInitializedKeyStore();
+
+ // Set up key manager factory to use our key store
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(_certType);
+ kmf.init(ks, _keystorePassword.toCharArray());
+
+ // Initialize the SSLContext to work with our key managers.
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(_certType);
+ tmf.init(ks);
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+ return sslContext;
+ }
+
+ /**
+ * Creates a SSLContext factory appropriate for use with a client
+ * @return SSLContext
+ * @throws GeneralSecurityException
+ * @throws IOException
+ */
+ public SSLContext buildClientContext() throws GeneralSecurityException, IOException
+ {
+ KeyStore ks = getInitializedKeyStore();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(_certType);
+ tmf.init(ks);
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tmf.getTrustManagers(), null);
+ return context;
+ }
+
+ private KeyStore getInitializedKeyStore() throws GeneralSecurityException, IOException
+ {
+ KeyStore ks = KeyStore.getInstance("JKS");
+ InputStream in = null;
+ try
+ {
+ File f = new File(_keystorePath);
+ if (f.exists())
+ {
+ in = new FileInputStream(f);
+ }
+ else
+ {
+ in = Thread.currentThread().getContextClassLoader().getResourceAsStream(_keystorePath);
+ }
+ if (in == null)
+ {
+ throw new IOException("Unable to load keystore resource: " + _keystorePath);
+ }
+ ks.load(in, _keystorePassword.toCharArray());
+ }
+ finally
+ {
+ if (in != null)
+ {
+ //noinspection EmptyCatchBlock
+ try
+ {
+ in.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+ return ks;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java b/Final/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java
new file mode 100644
index 0000000000..1774fa1194
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java
@@ -0,0 +1,294 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.url;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+
+public class AMQBindingURL implements BindingURL
+{
+ private static final Logger _logger = LoggerFactory.getLogger(AMQBindingURL.class);
+
+ String _url;
+ AMQShortString _exchangeClass;
+ AMQShortString _exchangeName;
+ AMQShortString _destinationName;
+ AMQShortString _queueName;
+ private HashMap<String, String> _options;
+
+ public AMQBindingURL(String url) throws URLSyntaxException
+ {
+ // format:
+ // <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']*
+ _logger.debug("Parsing URL: " + url);
+ _url = url;
+ _options = new HashMap<String, String>();
+
+ parseBindingURL();
+ }
+
+ private void parseBindingURL() throws URLSyntaxException
+ {
+ try
+ {
+ URI connection = new URI(_url);
+
+ String exchangeClass = connection.getScheme();
+
+ if (exchangeClass == null)
+ {
+ _url = ExchangeDefaults.DIRECT_EXCHANGE_CLASS + "://" + "" + "//" + _url;
+ // URLHelper.parseError(-1, "Exchange Class not specified.", _url);
+ parseBindingURL();
+
+ return;
+ }
+ else
+ {
+ setExchangeClass(exchangeClass);
+ }
+
+ String exchangeName = connection.getHost();
+
+ if (exchangeName == null)
+ {
+ if (getExchangeClass().equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS))
+ {
+ setExchangeName("");
+ }
+ else
+ {
+ throw URLHelper.parseError(-1, "Exchange Name not specified.", _url);
+ }
+ }
+ else
+ {
+ setExchangeName(exchangeName);
+ }
+
+ String queueName;
+
+ if ((connection.getPath() == null) || connection.getPath().equals(""))
+ {
+ throw URLHelper.parseError(_url.indexOf(_exchangeName.toString()) + _exchangeName.length(),
+ "Destination or Queue requried", _url);
+ }
+ else
+ {
+ int slash = connection.getPath().indexOf("/", 1);
+ if (slash == -1)
+ {
+ throw URLHelper.parseError(_url.indexOf(_exchangeName.toString()) + _exchangeName.length(),
+ "Destination requried", _url);
+ }
+ else
+ {
+ String path = connection.getPath();
+ setDestinationName(path.substring(1, slash));
+
+ // We don't set queueName yet as the actual value we use depends on options set
+ // when we are dealing with durable subscriptions
+
+ queueName = path.substring(slash + 1);
+
+ }
+ }
+
+ URLHelper.parseOptions(_options, connection.getQuery());
+
+ processOptions();
+
+ // We can now call setQueueName as the URL is full parsed.
+
+ setQueueName(queueName);
+
+ // Fragment is #string (not used)
+ _logger.debug("URL Parsed: " + this);
+
+ }
+ catch (URISyntaxException uris)
+ {
+
+ throw URLHelper.parseError(uris.getIndex(), uris.getReason(), uris.getInput());
+
+ }
+ }
+
+ private void setExchangeClass(String exchangeClass)
+ {
+ setExchangeClass(new AMQShortString(exchangeClass));
+ }
+
+ private void setQueueName(String name) throws URLSyntaxException
+ {
+ setQueueName(new AMQShortString(name));
+ }
+
+ private void setDestinationName(String name)
+ {
+ setDestinationName(new AMQShortString(name));
+ }
+
+ private void setExchangeName(String exchangeName)
+ {
+ setExchangeName(new AMQShortString(exchangeName));
+ }
+
+ private void processOptions()
+ {
+ // this is where we would parse any options that needed more than just storage.
+ }
+
+ public String getURL()
+ {
+ return _url;
+ }
+
+ public AMQShortString getExchangeClass()
+ {
+ return _exchangeClass;
+ }
+
+ private void setExchangeClass(AMQShortString exchangeClass)
+ {
+
+ _exchangeClass = exchangeClass;
+ if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
+ {
+ setOption(BindingURL.OPTION_EXCLUSIVE, "true");
+ }
+
+ }
+
+ public AMQShortString getExchangeName()
+ {
+ return _exchangeName;
+ }
+
+ private void setExchangeName(AMQShortString name)
+ {
+ _exchangeName = name;
+ }
+
+ public AMQShortString getDestinationName()
+ {
+ return _destinationName;
+ }
+
+ private void setDestinationName(AMQShortString name)
+ {
+ _destinationName = name;
+ }
+
+ public AMQShortString getQueueName()
+ {
+ return _queueName;
+ }
+
+ public void setQueueName(AMQShortString name) throws URLSyntaxException
+ {
+ if (_exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS))
+ {
+ if (Boolean.parseBoolean(getOption(OPTION_DURABLE)))
+ {
+ if (containsOption(BindingURL.OPTION_CLIENTID) && containsOption(BindingURL.OPTION_SUBSCRIPTION))
+ {
+ _queueName =
+ new AMQShortString(getOption(BindingURL.OPTION_CLIENTID + ":" + BindingURL.OPTION_SUBSCRIPTION));
+ }
+ else
+ {
+ throw URLHelper.parseError(-1, "Durable subscription must have values for " + BindingURL.OPTION_CLIENTID
+ + " and " + BindingURL.OPTION_SUBSCRIPTION + ".", _url);
+
+ }
+ }
+ else
+ {
+ _queueName = null;
+ }
+ }
+ else
+ {
+ _queueName = name;
+ }
+
+ }
+
+ public String getOption(String key)
+ {
+ return _options.get(key);
+ }
+
+ public void setOption(String key, String value)
+ {
+ _options.put(key, value);
+ }
+
+ public boolean containsOption(String key)
+ {
+ return _options.containsKey(key);
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ if (_exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS))
+ {
+ return getQueueName();
+ }
+
+ if (containsOption(BindingURL.OPTION_ROUTING_KEY))
+ {
+ return new AMQShortString(getOption(OPTION_ROUTING_KEY));
+ }
+
+ return getDestinationName();
+ }
+
+ public void setRoutingKey(AMQShortString key)
+ {
+ setOption(OPTION_ROUTING_KEY, key.toString());
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(_exchangeClass);
+ sb.append("://");
+ sb.append(_exchangeName);
+ sb.append('/');
+ sb.append(_destinationName);
+ sb.append('/');
+ sb.append(_queueName);
+
+ sb.append(URLHelper.printOptions(_options));
+
+ return sb.toString();
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/BindingURL.java b/Final/java/common/src/main/java/org/apache/qpid/url/BindingURL.java
new file mode 100644
index 0000000000..67be2db86f
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/url/BindingURL.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.url;
+
+import org.apache.qpid.framing.AMQShortString;
+
+/*
+ Binding URL format:
+ <exch_class>://<exch_name>/[<destination>]/[<queue>]?<option>='<value>'[,<option>='<value>']*
+*/
+public interface BindingURL
+{
+ public static final String OPTION_EXCLUSIVE = "exclusive";
+ public static final String OPTION_AUTODELETE = "autodelete";
+ public static final String OPTION_DURABLE = "durable";
+ public static final String OPTION_CLIENTID = "clientid";
+ public static final String OPTION_SUBSCRIPTION = "subscription";
+ public static final String OPTION_ROUTING_KEY = "routingkey";
+
+
+ String getURL();
+
+ AMQShortString getExchangeClass();
+
+ AMQShortString getExchangeName();
+
+ AMQShortString getDestinationName();
+
+ AMQShortString getQueueName();
+
+ String getOption(String key);
+
+ boolean containsOption(String key);
+
+ AMQShortString getRoutingKey();
+
+ String toString();
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/URLHelper.java b/Final/java/common/src/main/java/org/apache/qpid/url/URLHelper.java
new file mode 100644
index 0000000000..c08b443acf
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/url/URLHelper.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.url;
+
+import java.util.HashMap;
+
+public class URLHelper
+{
+ public static char DEFAULT_OPTION_SEPERATOR = '&';
+ public static char ALTERNATIVE_OPTION_SEPARATOR = ',';
+ public static char BROKER_SEPARATOR = ';';
+
+ public static void parseOptions(HashMap<String, String> optionMap, String options) throws URLSyntaxException
+ {
+ // options looks like this
+ // brokerlist='tcp://host:port?option='value',option='value';vm://:3/virtualpath?option='value'',failover='method?option='value',option='value''
+
+ if ((options == null) || (options.indexOf('=') == -1))
+ {
+ return;
+ }
+
+ int optionIndex = options.indexOf('=');
+
+ String option = options.substring(0, optionIndex);
+
+ int length = options.length();
+
+ int nestedQuotes = 0;
+
+ // to store index of final "'"
+ int valueIndex = optionIndex;
+
+ // Walk remainder of url.
+ while ((nestedQuotes > 0) || (valueIndex < length))
+ {
+ valueIndex++;
+
+ if (valueIndex >= length)
+ {
+ break;
+ }
+
+ if (options.charAt(valueIndex) == '\'')
+ {
+ if ((valueIndex + 1) < options.length())
+ {
+ if ((options.charAt(valueIndex + 1) == DEFAULT_OPTION_SEPERATOR)
+ || (options.charAt(valueIndex + 1) == ALTERNATIVE_OPTION_SEPARATOR)
+ || (options.charAt(valueIndex + 1) == BROKER_SEPARATOR)
+ || (options.charAt(valueIndex + 1) == '\''))
+ {
+ nestedQuotes--;
+
+ if (nestedQuotes == 0)
+ {
+ // We've found the value of an option
+ break;
+ }
+ }
+ else
+ {
+ nestedQuotes++;
+ }
+ }
+ else
+ {
+ // We are at the end of the string
+ // Check to see if we are corectly closing quotes
+ if (options.charAt(valueIndex) == '\'')
+ {
+ nestedQuotes--;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if ((nestedQuotes != 0) || (valueIndex < (optionIndex + 2)))
+ {
+ int sepIndex = 0;
+
+ // Try and identify illegal separator character
+ if (nestedQuotes > 1)
+ {
+ for (int i = 0; i < nestedQuotes; i++)
+ {
+ sepIndex = options.indexOf('\'', sepIndex);
+ sepIndex++;
+ }
+ }
+
+ if ((sepIndex >= options.length()) || (sepIndex == 0))
+ {
+ throw parseError(valueIndex, "Unterminated option", options);
+ }
+ else
+ {
+ throw parseError(sepIndex, "Unterminated option. Possible illegal option separator:'"
+ + options.charAt(sepIndex) + "'", options);
+ }
+ }
+
+ // optionIndex +2 to skip "='"
+ String value = options.substring(optionIndex + 2, valueIndex);
+
+ optionMap.put(option, value);
+
+ if (valueIndex < (options.length() - 1))
+ {
+ // Recurse to get remaining options
+ parseOptions(optionMap, options.substring(valueIndex + 2));
+ }
+ }
+
+ public static URLSyntaxException parseError(int index, String error, String url)
+ {
+ return parseError(index, 1, error, url);
+ }
+
+ public static URLSyntaxException parseError(int index, int length, String error, String url)
+ {
+ return new URLSyntaxException(url, error, index, length);
+ }
+
+ public static String printOptions(HashMap<String, String> options)
+ {
+ if (options.isEmpty())
+ {
+ return "";
+ }
+ else
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append('?');
+ for (String key : options.keySet())
+ {
+ sb.append(key);
+
+ sb.append("='");
+
+ sb.append(options.get(key));
+
+ sb.append("'");
+ sb.append(DEFAULT_OPTION_SEPERATOR);
+ }
+
+ sb.deleteCharAt(sb.length() - 1);
+
+ return sb.toString();
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java b/Final/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java
new file mode 100644
index 0000000000..3ff7195794
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.url;
+
+import java.net.URISyntaxException;
+
+public class URLSyntaxException extends URISyntaxException
+{
+ private int _length;
+
+ public URLSyntaxException(String url, String error, int index, int length)
+ {
+ super(url, error, index);
+
+ _length = length;
+ }
+
+ private static String getPositionString(int index, int length)
+ {
+ StringBuffer sb = new StringBuffer(index + 1);
+
+ for (int i = 0; i < index; i++)
+ {
+ sb.append(" ");
+ }
+
+ if (length > -1)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ sb.append('^');
+ }
+ }
+
+ return sb.toString();
+ }
+
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(getReason());
+
+ if (getIndex() > -1)
+ {
+ if (_length != -1)
+ {
+ sb.append(" between indicies ");
+ sb.append(getIndex());
+ sb.append(" and ");
+ sb.append(_length);
+ }
+ else
+ {
+ sb.append(" at index ");
+ sb.append(getIndex());
+ }
+ }
+
+ sb.append(" ");
+ if (getIndex() != -1)
+ {
+ sb.append("\n");
+ }
+
+ sb.append(getInput());
+
+ if (getIndex() != -1)
+ {
+ sb.append("\n");
+ sb.append(getPositionString(getIndex(), _length));
+ }
+
+ return sb.toString();
+ }
+
+
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java b/Final/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java
new file mode 100644
index 0000000000..dc73bce28f
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java
@@ -0,0 +1,689 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.*;
+
+/**
+ * CommandLineParser provides a utility for specifying the format of a command line and parsing command lines to ensure
+ * that they fit their specified format. A command line is made up of flags and options, both may be refered to as
+ * options. A flag is an option that does not take an argument (specifying it means it has the value 'true' and not
+ * specifying it means it has the value 'false'). Options must take arguments but they can be set up with defaults so
+ * that they take a default value when not set. Options may be mandatory in wich case it is an error not to specify
+ * them on the command line. Flags are never mandatory because they are implicitly set to false when not specified.
+ *
+ * <p/>Some example command lines 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 are ignored.
+ * <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>
+ */
+public class CommandLineParser
+{
+ /** Holds a mapping from command line option names to detailed information about those options. */
+ private Map<String, CommandLineOption> optionMap = new HashMap<String, CommandLineOption>();
+
+ /** Holds a list of parsing errors. */
+ private List<String> parsingErrors = new ArrayList<String>();
+
+ /** Holds the regular expression matcher to match command line options with. */
+ private Matcher optionMatcher = null;
+
+ /** Holds the parsed command line properties after parsing. */
+ private Properties parsedProperties = null;
+
+ /** Flag used to indicate that errors should be created for unknown options. False by default. */
+ private boolean errorsOnUnknowns = false;
+
+ /**
+ * Creates a command line options parser from a command line specification. This is passed to this constructor
+ * as an array of arrays of strings. Each array of strings specifies the command line for a single option. A static
+ * array may therefore easily be used to configure the command line parser in a single method call with an easily
+ * readable format.
+ *
+ * <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 expression 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 (int i = 0; i < config.length; i++)
+ {
+ String[] nextOptionSpec = config[i];
+
+ addOption(nextOptionSpec[0], nextOptionSpec[1], (nextOptionSpec.length > 2) ? nextOptionSpec[2] : null,
+ (nextOptionSpec.length > 3) ? ("true".equals(nextOptionSpec[3]) ? true : false) : false,
+ (nextOptionSpec.length > 4) ? nextOptionSpec[4] : null);
+ }
+ }
+
+ /**
+ * Lists all the parsing errors from the most recent parsing in a string.
+ *
+ * @return All the parsing errors from the most recent parsing.
+ */
+ public String getErrors()
+ {
+ // Return the empty string if there are no errors.
+ if (parsingErrors.isEmpty())
+ {
+ return "";
+ }
+
+ // Concatenate all the parsing errors together.
+ String result = "";
+
+ for (String s : parsingErrors)
+ {
+ result += s;
+ }
+
+ return result;
+ }
+
+ /**
+ * Lists the properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ *
+ * @return The properties set from the most recent parsing or an empty string if no parsing has been done yet.
+ */
+ public String getOptionsInForce()
+ {
+ // Check if there are no properties to report and return and empty string if so.
+ if (parsedProperties == null)
+ {
+ return "";
+ }
+
+ // List all the properties.
+ String result = "Options in force:\n";
+
+ for (Map.Entry<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";
+
+ // Print usage on each of the command line options.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ result +=
+ optionInfo.option + " " + ((optionInfo.argument != null) ? (optionInfo.argument + " ") : "")
+ + optionInfo.comment + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Control the behaviour of the errors on unkowns reporting. When turned on this reports all unkowns options
+ * as errors. When turned off, all unknowns are simply ignored.
+ *
+ * @param errors The setting of the errors on unkown flag. True to turn it on.
+ */
+ public void setErrorsOnUnknowns(boolean errors)
+ {
+ errorsOnUnknowns = errors;
+ }
+
+ /**
+ * Parses a set of command line arguments into a set of properties, keyed by the argument flag. The free arguments
+ * are keyed by integers as strings starting at "1" and then "2", ... and so on.
+ *
+ * <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 expression 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 expression matcher if it is not a free argument definition.
+ if (notFree)
+ {
+ regexp += nextOption + (i.hasNext() ? "|" : "");
+ optionsAdded++;
+ }
+ }
+
+ // There has to be more that one option in the regular expression or else the compiler complains that the close
+ // cannot be nullable if the '?' token is used to make the matched option string optional.
+ regexp += ")" + ((optionsAdded > 0) ? "?" : "") + "(.*)";
+ Pattern pattern = Pattern.compile(regexp);
+
+ // Loop through all the command line arguments.
+ for (int i = 0; i < args.length; i++)
+ {
+ // Check if the next command line argument begins with a '-' character and is therefore the start of
+ // an option.
+ if (args[i].startsWith("-"))
+ {
+ // Extract the value of the option without the leading '-'.
+ String arg = args[i].substring(1);
+
+ // Match up to the longest matching option.
+ optionMatcher = pattern.matcher(arg);
+ optionMatcher.matches();
+
+ String matchedOption = optionMatcher.group(1);
+
+ // Match any argument directly appended onto the longest matching option.
+ String matchedArg = optionMatcher.group(2);
+
+ // Check that a known option was matched.
+ if ((matchedOption != null) && !"".equals(matchedOption))
+ {
+ // Get the command line option information for the matched option.
+ CommandLineOption optionInfo = optionMap.get(matchedOption);
+
+ // Check if this option is expecting arguments.
+ if (optionInfo.expectsArgs)
+ {
+ // The option is expecting arguments so swallow the next command line argument as an
+ // argument to this option.
+ expectingArgs = true;
+ optionExpectingArgs = matchedOption;
+
+ // In the mean time set this options argument to the empty string in case no argument is ever
+ // supplied.
+ // options.put(matchedOption, "");
+ }
+
+ // Check if the option was matched on its own and is a flag in which case set that flag.
+ if ("".equals(matchedArg) && !optionInfo.expectsArgs)
+ {
+ options.put(matchedOption, "true");
+ }
+ // The option was matched as a substring with its argument appended to it or is a flag that is
+ // condensed together with other flags.
+ else if (!"".equals(matchedArg))
+ {
+ // Check if the option is a flag and therefore is allowed to be condensed together
+ // with other flags.
+ if (!optionInfo.expectsArgs)
+ {
+ // Set the first matched flag.
+ options.put(matchedOption, "true");
+
+ // Repeat the longest matching process on the remainder but ensure that the remainder
+ // consists only of flags as only flags may be condensed together in this fashion.
+ do
+ {
+ // Match the remainder against the options.
+ optionMatcher = pattern.matcher(matchedArg);
+ optionMatcher.matches();
+
+ matchedOption = optionMatcher.group(1);
+ matchedArg = optionMatcher.group(2);
+
+ // Check that an option was matched.
+ if (matchedOption != null)
+ {
+ // Get the command line option information for the next matched option.
+ optionInfo = optionMap.get(matchedOption);
+
+ // Ensure that the next option is a flag or raise an error if not.
+ if (optionInfo.expectsArgs == true)
+ {
+ parsingErrors.add("Option " + matchedOption + " cannot be combined with flags.\n");
+ }
+
+ options.put(matchedOption, "true");
+ }
+ // The remainder could not be matched against a flag it is either an unknown flag
+ // or an illegal argument to a flag.
+ else
+ {
+ parsingErrors.add("Illegal argument to a flag in the option " + arg + "\n");
+
+ break;
+ }
+ }
+ // Continue until the remainder of the argument has all been matched with flags.
+ while (!"".equals(matchedArg));
+ }
+ // The option is expecting an argument, so store the unmatched portion against it
+ // as its argument.
+ else
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, matchedArg);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(matchedOption, matchedArg);
+
+ // The argument to this flag has already been supplied to it. Do not swallow the
+ // next command line argument as an argument to this flag.
+ expectingArgs = false;
+ }
+ }
+ }
+ else // No matching option was found.
+ {
+ // Add this to the list of parsing errors if errors on unkowns is being used.
+ if (errorsOnUnknowns)
+ {
+ parsingErrors.add("Option " + matchedOption + " is not a recognized option.\n");
+ }
+ }
+ }
+ // The command line argument did not being with a '-' so it is an argument to the previous flag or it
+ // is a free argument.
+ else
+ {
+ // Check if a previous flag is expecting to swallow this next argument as its argument.
+ if (expectingArgs)
+ {
+ // Get the option info for the option waiting for arguments.
+ CommandLineOption optionInfo = optionMap.get(optionExpectingArgs);
+
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, args[i]);
+
+ // Store the argument against its option (regardless of its format).
+ options.put(optionExpectingArgs, args[i]);
+
+ // Clear the expecting args flag now that the argument has been swallowed.
+ expectingArgs = false;
+ optionExpectingArgs = null;
+ }
+ // This command line option is not an argument to any option. Add it to the set of 'free' options.
+ else
+ {
+ // Get the option info for the free option, if there is any.
+ CommandLineOption optionInfo = optionMap.get(Integer.toString(free));
+
+ if (optionInfo != null)
+ {
+ // Check the arguments format is correct against any specified format.
+ checkArgumentFormat(optionInfo, args[i]);
+ }
+
+ // Add to the list of free options.
+ options.put(Integer.toString(free), args[i]);
+
+ // Move on to the next free argument.
+ free++;
+ }
+ }
+ }
+
+ // Scan through all the specified options to check that all mandatory options have been set and that all flags
+ // that were not set are set to false in the set of properties.
+ for (CommandLineOption optionInfo : optionMap.values())
+ {
+ // Check if this is a flag.
+ if (!optionInfo.expectsArgs)
+ {
+ // Check if the flag is not set in the properties and set it to false if so.
+ if (!options.containsKey(optionInfo.option))
+ {
+ options.put(optionInfo.option, "false");
+ }
+ }
+ // Check if this is a mandatory option and was not set.
+ else if (optionInfo.mandatory && !options.containsKey(optionInfo.option))
+ {
+ // Create an error for the missing option.
+ parsingErrors.add("Option " + optionInfo.option + " is mandatory but not was not specified.\n");
+ }
+ }
+
+ // Check if there were any errors.
+ if (!parsingErrors.isEmpty())
+ {
+ // Throw an illegal argument exception to signify that there were parsing errors.
+ throw new IllegalArgumentException();
+ }
+
+ // Convert any name/value pairs in the free arguments into properties in the parsed options.
+ options = takeFreeArgsAsProperties(options, 1);
+
+ parsedProperties = options;
+
+ return options;
+ }
+
+ /**
+ * If a command line has been parsed, calling this method sets all of its parsed options into the specified properties.
+ */
+ public void addCommandLineToProperties(Properties properties)
+ {
+ if (parsedProperties != null)
+ {
+ for (Object propKey : parsedProperties.keySet())
+ {
+ String name = (String) propKey;
+ String value = parsedProperties.getProperty(name);
+
+ properties.setProperty(name, value);
+ }
+ }
+ }
+
+ /**
+ * Resets this command line parser after it has been used to parse a command line. This method will only need
+ * to be called to use this parser a second time which is not likely seeing as a command line is usually only
+ * specified once. However, it is exposed as a public method for the rare case where this may be done.
+ *
+ * <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 expression.
+ */
+ protected void addOption(String option, String comment, String argument, boolean mandatory, String formatRegexp)
+ {
+ // Check if usage text has been set in which case this option is expecting arguments.
+ boolean expectsArgs = ((argument == null) || argument.equals("")) ? false : true;
+
+ // Add the option to the map of command line options.
+ CommandLineOption opt = new CommandLineOption(option, expectsArgs, comment, argument, mandatory, formatRegexp);
+ optionMap.put(option, opt);
+ }
+
+ /**
+ * Converts the free arguments into property declarations. After parsing the command line the free arguments
+ * are numbered from 1, such that the parsed properties contain values for the keys "1", "2", ... This method
+ * converts any free arguments declared using the 'name=value' syntax into properties with key 'name', value
+ * 'value'.
+ *
+ * <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)
+ {
+ for (int i = from; true; i++)
+ {
+ String nextFreeArg = properties.getProperty(Integer.toString(i));
+
+ // Terminate the loop once all free arguments have been consumed.
+ if (nextFreeArg == null)
+ {
+ break;
+ }
+
+ // Split it on the =, strip any whitespace and set it as a system property.
+ String[] nameValuePair = nextFreeArg.split("=");
+
+ if (nameValuePair.length == 2)
+ {
+ properties.setProperty(nameValuePair[0], nameValuePair[1]);
+ }
+ }
+
+ return properties;
+ }
+
+ /**
+ * Checks the format of an argument to an option against its specified regular expression format if one has
+ * been set. Any errors are added to the list of parsing errors.
+ *
+ * @param optionInfo The command line option information for the option which is havings its argument checked.
+ * @param matchedArg The string argument to the option.
+ */
+ private void checkArgumentFormat(CommandLineOption optionInfo, String matchedArg)
+ {
+ // Check if this option enforces a format for its argument.
+ if (optionInfo.argumentFormatRegexp != null)
+ {
+ Pattern pattern = Pattern.compile(optionInfo.argumentFormatRegexp);
+ Matcher argumentMatcher = pattern.matcher(matchedArg);
+
+ // Check if the argument does not meet its required format.
+ if (!argumentMatcher.matches())
+ {
+ // Create an error for this badly formed argument.
+ parsingErrors.add("The argument to option " + optionInfo.option + " does not meet its required format.\n");
+ }
+ }
+ }
+
+ /**
+ * Extracts all name=value pairs from the command line, sets them all as system properties and also returns
+ * a map of properties containing them.
+ *
+ * @param args The command line.
+ * @param commandLine The command line parser.
+ * @param properties The properties object to inject all parsed properties into (optional may be <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 trailing command line options (name=value pairs) to system properties. They may be picked up
+ // from there.
+ commandLine.addCommandLineToProperties(properties);
+ }
+ catch (IllegalArgumentException e)
+ {
+ System.out.println(commandLine.getErrors());
+ System.out.println(commandLine.getUsage());
+ System.exit(1);
+ }
+
+ return options;
+ }
+
+ /**
+ * Holds information about a command line options. This includes what its name is, whether or not it is a flag,
+ * whether or not it is mandatory, what its user comment is, what its argument reminder text is and what its
+ * regular expression format is.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Hold details of a command line option.
+ * </table>
+ */
+ protected class CommandLineOption
+ {
+ /** Holds the text for the flag to match this argument with. */
+ public String option = null;
+
+ /** Holds a string describing how to use this command line argument. */
+ public String argument = null;
+
+ /** Flag that determines whether or not this command line argument can take arguments. */
+ public boolean expectsArgs = false;
+
+ /** Holds a short comment describing what this command line argument is for. */
+ public String comment = null;
+
+ /** Flag that determines whether or not this is an mandatory command line argument. */
+ public boolean mandatory = false;
+
+ /** A regular expression describing what format the argument to this option muist have. */
+ public String argumentFormatRegexp = null;
+
+ /**
+ * Create a command line option object that holds specific information about a command line option.
+ *
+ * @param option The text that matches the option.
+ * @param expectsArgs Whether or not the option expects arguments. It is a flag if this is false.
+ * @param comment A comment explaining how to use this option.
+ * @param argument A short reminder of the format of the argument to this option/
+ * @param mandatory Set to true if this option is mandatory.
+ * @param formatRegexp The regular expression that the argument to this option must meet to be valid.
+ */
+ public CommandLineOption(String option, boolean expectsArgs, String comment, String argument, boolean mandatory,
+ String formatRegexp)
+ {
+ this.option = option;
+ this.expectsArgs = expectsArgs;
+ this.comment = comment;
+ this.argument = argument;
+ this.mandatory = mandatory;
+ this.argumentFormatRegexp = formatRegexp;
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java
new file mode 100644
index 0000000000..461cf9591d
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ConcurrentLinkedMessageQueueAtomicSize<E> extends ConcurrentLinkedQueueAtomicSize<E> implements MessageQueue<E>
+{
+ private static final Logger _logger = LoggerFactory.getLogger(ConcurrentLinkedMessageQueueAtomicSize.class);
+
+ protected Queue<E> _messageHead = new ConcurrentLinkedQueueAtomicSize<E>();
+
+ protected AtomicInteger _messageHeadSize = new AtomicInteger(0);
+
+ @Override
+ public int size()
+ {
+ return super.size() + _messageHeadSize.get();
+ }
+
+ public int headSize()
+ {
+ return _messageHeadSize.get();
+ }
+
+ @Override
+ public E poll()
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.poll();
+ }
+ else
+ {
+ E e = _messageHead.poll();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Providing item(" + e + ")from message head");
+ }
+
+ if (e != null)
+ {
+ _messageHeadSize.decrementAndGet();
+ }
+
+ return e;
+ }
+ }
+
+ @Override
+ public boolean remove(Object o)
+ {
+
+ if (_messageHead.isEmpty())
+ {
+ return super.remove(o);
+ }
+ else
+ {
+ if (_messageHead.remove(o))
+ {
+ _messageHeadSize.decrementAndGet();
+
+ return true;
+ }
+
+ return super.remove(o);
+ }
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c)
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.removeAll(c);
+ }
+ else
+ {
+ // fixme this is super.removeAll but iterator here doesn't work
+ // we need to be able to correctly decrement _messageHeadSize
+ // boolean modified = false;
+ // Iterator<?> e = iterator();
+ // while (e.hasNext())
+ // {
+ // if (c.contains(e.next()))
+ // {
+ // e.remove();
+ // modified = true;
+ // _size.decrementAndGet();
+ // }
+ // }
+ // return modified;
+
+ throw new RuntimeException("Not implemented");
+ }
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return (_messageHead.isEmpty() && super.isEmpty());
+ }
+
+ @Override
+ public void clear()
+ {
+ super.clear();
+ _messageHead.clear();
+ }
+
+ @Override
+ public boolean contains(Object o)
+ {
+ return _messageHead.contains(o) || super.contains(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> o)
+ {
+ return _messageHead.containsAll(o) || super.containsAll(o);
+ }
+
+ @Override
+ public E element()
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.element();
+ }
+ else
+ {
+ return _messageHead.element();
+ }
+ }
+
+ @Override
+ public E peek()
+ {
+ if (_messageHead.isEmpty())
+ {
+ return super.peek();
+ }
+ else
+ {
+ E o = _messageHead.peek();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Peeking item (" + o + ") from message head");
+ }
+
+ return o;
+ }
+
+ }
+
+ @Override
+ public Iterator<E> iterator()
+ {
+ final Iterator<E> mainMessageIterator = super.iterator();
+
+ return new Iterator<E>()
+ {
+ final Iterator<E> _headIterator = _messageHead.iterator();
+ final Iterator<E> _mainIterator = mainMessageIterator;
+
+ Iterator<E> last;
+
+ public boolean hasNext()
+ {
+ return _headIterator.hasNext() || _mainIterator.hasNext();
+ }
+
+ public E next()
+ {
+ if (_headIterator.hasNext())
+ {
+ last = _headIterator;
+
+ return _headIterator.next();
+ }
+ else
+ {
+ last = _mainIterator;
+
+ return _mainIterator.next();
+ }
+ }
+
+ public void remove()
+ {
+ last.remove();
+ }
+ };
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c)
+ {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Override
+ public Object[] toArray()
+ {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ public boolean pushHead(E o)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Adding item(" + o + ") to head of queue");
+ }
+
+ if (_messageHead.offer(o))
+ {
+ _messageHeadSize.incrementAndGet();
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java
new file mode 100644
index 0000000000..c4d7683a02
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ConcurrentLinkedQueueAtomicSize<E> extends ConcurrentLinkedQueue<E>
+{
+ AtomicInteger _size = new AtomicInteger(0);
+
+ public int size()
+ {
+ return _size.get();
+ }
+
+ public boolean offer(E o)
+ {
+
+ if (super.offer(o))
+ {
+ _size.incrementAndGet();
+ return true;
+ }
+
+ return false;
+ }
+
+ public E poll()
+ {
+ E e = super.poll();
+
+ if (e != null)
+ {
+ _size.decrementAndGet();
+ }
+
+ return e;
+ }
+
+ @Override
+ public boolean remove(Object o)
+ {
+ if (super.remove(o))
+ {
+ _size.decrementAndGet();
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java
new file mode 100644
index 0000000000..1f168345a1
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class ConcurrentLinkedQueueNoSize<E> extends ConcurrentLinkedQueue<E>
+{
+ public int size()
+ {
+ if (isEmpty())
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/FileUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/FileUtils.java
new file mode 100644
index 0000000000..3b8ebc1666
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/FileUtils.java
@@ -0,0 +1,195 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.io.*;
+
+/**
+ * FileUtils provides some simple helper methods for working with files. It follows the convention of wrapping all
+ * checked exceptions as runtimes, so code using these methods is free of try-catch blocks but does not expect to
+ * recover from errors.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Read a text file as a string.
+ * <tr><td> Open a file or default resource as an input stream.
+ * </table>
+ */
+public class FileUtils
+{
+ /**
+ * Reads a text file as a string.
+ *
+ * @param filename The name of the file.
+ *
+ * @return The contents of the file.
+ */
+ public static String readFileAsString(String filename)
+ {
+ BufferedInputStream is = null;
+
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(filename));
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ return readStreamAsString(is);
+ }
+
+ /**
+ * Reads a text file as a string.
+ *
+ * @param file The file.
+ *
+ * @return The contents of the file.
+ */
+ public static String readFileAsString(File file)
+ {
+ BufferedInputStream is = null;
+
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(file));
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ return readStreamAsString(is);
+ }
+
+ /**
+ * Reads the contents of a reader, one line at a time until the end of stream is encountered, and returns all
+ * together as a string.
+ *
+ * @param is The reader.
+ *
+ * @return The contents of the reader.
+ */
+ private static String readStreamAsString(BufferedInputStream is)
+ {
+ try
+ {
+ byte[] data = new byte[4096];
+
+ StringBuffer inBuffer = new StringBuffer();
+
+ String line;
+ int read;
+
+ while ((read = is.read(data)) != -1)
+ {
+ String s = new String(data, 0, read);
+ inBuffer.append(s);
+ }
+
+ return inBuffer.toString();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Either opens the specified filename as an input stream, or uses the default resource loaded using the
+ * specified class loader, if opening the file fails or no file name is specified.
+ *
+ * @param filename The name of the file to open.
+ * @param defaultResource The name of the default resource on the classpath if the file cannot be opened.
+ * @param cl The classloader to load the default resource with.
+ *
+ * @return An input stream for the file or resource, or null if one could not be opened.
+ */
+ public static InputStream openFileOrDefaultResource(String filename, String defaultResource, ClassLoader cl)
+ {
+ InputStream is = null;
+
+ // Flag to indicate whether the default resource should be used. By default this is true, so that the default
+ // is used when opening the file fails.
+ boolean useDefault = true;
+
+ // Try to open the file if one was specified.
+ if (filename != null)
+ {
+ try
+ {
+ is = new BufferedInputStream(new FileInputStream(new File(filename)));
+
+ // Clear the default flag because the file was succesfully opened.
+ useDefault = false;
+ }
+ catch (FileNotFoundException e)
+ {
+ // Ignore this exception, the default will be used instead.
+ }
+ }
+
+ // Load the default resource if a file was not specified, or if opening the file failed.
+ if (useDefault)
+ {
+ is = cl.getResourceAsStream(defaultResource);
+ }
+
+ return is;
+ }
+
+ /**
+ * Copies the specified source file to the specified destintaion file. If the destinationst file does not exist,
+ * it is created.
+ *
+ * @param src The source file name.
+ * @param dst The destination file name.
+ */
+ public static void copy(File src, File dst)
+ {
+ try
+ {
+ InputStream in = new FileInputStream(src);
+ if (!dst.exists())
+ {
+ dst.createNewFile();
+ }
+
+ OutputStream out = new FileOutputStream(dst);
+
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0)
+ {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.close();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java b/Final/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java
new file mode 100644
index 0000000000..b5efaa61b6
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/MessageQueue.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.util;
+
+import java.util.Queue;
+
+/**
+ * Defines a queue that has a push operation to add an element to the head of the queue.
+ *
+ * @todo Seems like this may be pointless, the implementation uses this method to increment the message count
+ * then calls offer. Why not simply override offer and drop this interface?
+ */
+public interface MessageQueue<E> extends Queue<E>
+{
+ /**
+ * Inserts the specified element into this queue, if possible. When using queues that may impose insertion
+ * restrictions (for example capacity bounds), method offer is generally preferable to method Collection.add(E),
+ * which can fail to insert an element only by throwing an exception.
+ *
+ * @param o The element to insert.
+ *
+ * @return <tt>true</tt> if it was possible to add the element to this queue, else <tt>false</tt>
+ */
+ boolean pushHead(E o);
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java
new file mode 100644
index 0000000000..10f6a27293
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+/**
+ * Contains pretty printing convenienve methods for producing formatted logging output, mostly for debugging purposes.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ *
+ * @todo Drop this. There are already array pretty printing methods it java.utils.Arrays.
+ */
+public class PrettyPrintingUtils
+{
+ /**
+ * Pretty prints an array of ints as a string.
+ *
+ * @param array The array to pretty print.
+ *
+ * @return The pretty printed string.
+ */
+ public static String printArray(int[] array)
+ {
+ String result = "[";
+ for (int i = 0; i < array.length; i++)
+ {
+ result += array[i];
+ result += (i < (array.length - 1)) ? ", " : "";
+ }
+
+ result += "]";
+
+ return result;
+ }
+
+ /**
+ * Pretty prints an array of strings as a string.
+ *
+ * @param array The array to pretty print.
+ *
+ * @return The pretty printed string.
+ */
+ public static String printArray(String[] array)
+ {
+ String result = "[";
+ for (int i = 0; i < array.length; i++)
+ {
+ result += array[i];
+ result += (i < (array.length - 1)) ? ", " : "";
+ }
+
+ result += "]";
+
+ return result;
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java
new file mode 100644
index 0000000000..63cf6f252b
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java
@@ -0,0 +1,200 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * PropertiesHelper defines some static methods which are useful when working with properties
+ * files.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Read properties from an input stream
+ * <tr><td> Read properties from a file
+ * <tr><td> Read properties from a URL
+ * <tr><td> Read properties given a path to a file
+ * <tr><td> Trim any whitespace from property values
+ * </table>
+ */
+public class PropertiesUtils
+{
+ /** Used for logging. */
+ private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class);
+
+ /**
+ * Get properties from an input stream.
+ *
+ * @param is The input stream.
+ *
+ * @return The properties loaded from the input stream.
+ *
+ * @throws IOException If the is an I/O error reading from the stream.
+ */
+ public static Properties getProperties(InputStream is) throws IOException
+ {
+ log.debug("getProperties(InputStream): called");
+
+ // Create properties object laoded from input stream
+ Properties properties = new Properties();
+
+ properties.load(is);
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a file.
+ *
+ * @param file The file.
+ *
+ * @return The properties loaded from the file.
+ *
+ * @throws IOException If there is an I/O error reading from the file.
+ */
+ public static Properties getProperties(File file) throws IOException
+ {
+ log.debug("getProperties(File): called");
+
+ // Open the file as an input stream
+ InputStream is = new FileInputStream(file);
+
+ // Create properties object loaded from the stream
+ Properties properties = getProperties(is);
+
+ // Close the file
+ is.close();
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a url.
+ *
+ * @param url The URL.
+ *
+ * @return The properties loaded from the url.
+ *
+ * @throws IOException If there is an I/O error reading from the URL.
+ */
+ public static Properties getProperties(URL url) throws IOException
+ {
+ log.debug("getProperties(URL): called");
+
+ // Open the URL as an input stream
+ InputStream is = url.openStream();
+
+ // Create properties object loaded from the stream
+ Properties properties = getProperties(is);
+
+ // Close the url
+ is.close();
+
+ return properties;
+ }
+
+ /**
+ * Get properties from a path name. The path name may refer to either a file or a URL.
+ *
+ * @param pathname The path name.
+ *
+ * @return The properties loaded from the file or URL.
+ *
+ * @throws IOException If there is an I/O error reading from the URL or file named by the path.
+ */
+ public static Properties getProperties(String pathname) throws IOException
+ {
+ log.debug("getProperties(String): called");
+
+ // Check that the path is not null
+ if (pathname == null)
+ {
+ return null;
+ }
+
+ // Check if the path is a URL
+ if (isURL(pathname))
+ {
+ // The path is a URL
+ return getProperties(new URL(pathname));
+ }
+ else
+ {
+ // Assume the path is a file name
+ return getProperties(new File(pathname));
+ }
+ }
+
+ /**
+ * Trims whitespace from property values. This method returns a new set of properties
+ * the same as the properties specified as an argument but with any white space removed by
+ * the {@link java.lang.String#trim} method.
+ *
+ * @param properties The properties to trim whitespace from.
+ *
+ * @return The white space trimmed properties.
+ */
+ public static Properties trim(Properties properties)
+ {
+ Properties trimmedProperties = new Properties();
+
+ // Loop over all the properties
+ for (Iterator i = properties.keySet().iterator(); i.hasNext();)
+ {
+ String next = (String) i.next();
+ String nextValue = properties.getProperty(next);
+
+ // Trim the value if it is not null
+ if (nextValue != null)
+ {
+ nextValue.trim();
+ }
+
+ // Store the trimmed value in the trimmed properties
+ trimmedProperties.setProperty(next, nextValue);
+ }
+
+ return trimmedProperties;
+ }
+
+ /**
+ * Helper method. Guesses whether a string is a URL or not. A String is considered to be a url if it begins with
+ * http:, ftp:, or uucp:.
+ *
+ * @param name The string to test for being a URL.
+ *
+ * @return True if the string is a URL and false if not.
+ */
+ private static boolean isURL(String name)
+ {
+ return (name.toLowerCase().startsWith("http:") || name.toLowerCase().startsWith("ftp:")
+ || name.toLowerCase().startsWith("uucp:"));
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java
new file mode 100644
index 0000000000..495918911a
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.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.qpid.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Provides helper methods for operating on classes and methods using reflection. Reflection methods tend to return
+ * a lot of checked exception so writing code to use them can be tedious and harder to read, especially when such errors
+ * are not expected to occur. This class always works with {@link ReflectionUtilsException}, which is a runtime exception,
+ * to wrap the checked exceptions raised by the standard Java reflection methods. Code using it does not normally
+ * expect these errors to occur, usually does not have a recovery mechanism for them when they do, but is cleaner,
+ * quicker to write and easier to read in the majority of cases.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Look up Classes by name.
+ * <tr><td> Instantiate Classes by no-arg constructor.
+ * </table>
+ */
+public class ReflectionUtils
+{
+ /**
+ * Gets the Class object for a named class.
+ *
+ * @param className The class to get the Class object for.
+ *
+ * @return The Class object for the named class.
+ */
+ public static Class<?> forName(String className)
+ {
+ try
+ {
+ return Class.forName(className);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new ReflectionUtilsException("ClassNotFoundException whilst finding class.", e);
+ }
+ }
+
+ /**
+ * Creates an instance of a Class, instantiated through its no-args constructor.
+ *
+ * @param cls The Class to instantiate.
+ * @param <T> The Class type.
+ *
+ * @return An instance of the class.
+ */
+ public static <T> T newInstance(Class<? extends T> cls)
+ {
+ try
+ {
+ return cls.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ throw new ReflectionUtilsException("InstantiationException whilst instantiating class.", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException whilst instantiating class.", e);
+ }
+ }
+
+ /**
+ * Calls a named method on an object with a specified set of parameters, any Java access modifier are overridden.
+ *
+ * @param o The object to call.
+ * @param method The method name to call.
+ * @param params The parameters to pass.
+ * @param paramClasses The argument types.
+ *
+ * @return The return value from the method call.
+ */
+ public static Object callMethodOverridingIllegalAccess(Object o, String method, Object[] params, Class[] paramClasses)
+ {
+ // Get the objects class.
+ Class cls = o.getClass();
+
+ // Get the classes of the parameters.
+ /*Class[] paramClasses = new Class[params.length];
+
+ for (int i = 0; i < params.length; i++)
+ {
+ paramClasses[i] = params[i].getClass();
+ }*/
+
+ try
+ {
+ // Try to find the matching method on the class.
+ Method m = cls.getDeclaredMethod(method, paramClasses);
+
+ // Make it accessible.
+ m.setAccessible(true);
+
+ // Invoke it with the parameters.
+ return m.invoke(o, params);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new ReflectionUtilsException("NoSuchMethodException.", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException.", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new ReflectionUtilsException("InvocationTargetException", e);
+ }
+ }
+
+ /**
+ * Calls a named method on an object with a specified set of parameters.
+ *
+ * @param o The object to call.
+ * @param method The method name to call.
+ * @param params The parameters to pass.
+ *
+ * @return The return value from the method call.
+ */
+ public static Object callMethod(Object o, String method, Object[] params)
+ {
+ // Get the objects class.
+ Class cls = o.getClass();
+
+ // Get the classes of the parameters.
+ Class[] paramClasses = new Class[params.length];
+
+ for (int i = 0; i < params.length; i++)
+ {
+ paramClasses[i] = params[i].getClass();
+ }
+
+ try
+ {
+ // Try to find the matching method on the class.
+ Method m = cls.getMethod(method, paramClasses);
+
+ // Invoke it with the parameters.
+ return m.invoke(o, params);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new ReflectionUtilsException("NoSuchMethodException.", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new ReflectionUtilsException("InvocationTargetException", e);
+ }
+ }
+
+ /**
+ * Calls a constuctor witht the specified arguments.
+ *
+ * @param constructor The constructor.
+ * @param args The arguments.
+ * @param <T> The Class type.
+ *
+ * @return An instance of the class that the constructor is for.
+ */
+ public static <T> T newInstance(Constructor<T> constructor, Object[] args)
+ {
+ try
+ {
+ return constructor.newInstance(args);
+ }
+ catch (InstantiationException e)
+ {
+ throw new ReflectionUtilsException("InstantiationException", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ReflectionUtilsException("IllegalAccessException", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new ReflectionUtilsException("InvocationTargetException", e);
+ }
+ }
+
+ /**
+ * Gets the constructor of a class that takes the specified set of arguments if any matches. If no matching
+ * constructor is found then a runtime exception is raised.
+ *
+ * @param cls The class to get a constructor from.
+ * @param args The arguments to match.
+ * @param <T> The class type.
+ *
+ * @return The constructor.
+ */
+ public static <T> Constructor<T> getConstructor(Class<T> cls, Class[] args)
+ {
+ try
+ {
+ return cls.getConstructor(args);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new ReflectionUtilsException("NoSuchMethodException", e);
+ }
+ }
+}
diff --git a/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java
new file mode 100644
index 0000000000..20499641ac
--- /dev/null
+++ b/Final/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+/**
+ * Wraps a checked exception that occurs when {@link ReflectionUtils} encounters checked exceptions using standard
+ * Java reflection methods.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Wrap a checked reflection exception.
+ * </table>
+ */
+public class ReflectionUtilsException extends RuntimeException
+{
+ /**
+ * Creates a runtime reflection exception, from a checked one.
+ *
+ * @param message The message.
+ * @param cause The causing exception.
+ */
+ public ReflectionUtilsException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/Final/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert b/Final/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert
new file mode 100644
index 0000000000..e6702108e6
--- /dev/null
+++ b/Final/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert
Binary files differ
diff --git a/Final/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java b/Final/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java
new file mode 100644
index 0000000000..4fd1f60d69
--- /dev/null
+++ b/Final/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java
@@ -0,0 +1,188 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+
+
+public class BasicContentHeaderPropertiesTest extends TestCase
+{
+
+ BasicContentHeaderProperties _testProperties;
+ FieldTable _testTable;
+ String _testString = "This is a test string";
+ int _testint = 666;
+
+ /**
+ * Currently only test setting/getting String, int and boolean props
+ */
+ public BasicContentHeaderPropertiesTest()
+ {
+ _testProperties = new BasicContentHeaderProperties();
+ }
+
+ public void setUp()
+ {
+ _testTable = new FieldTable();
+ _testTable.setString("TestString", _testString);
+ _testTable.setInteger("Testint", _testint);
+ _testProperties = new BasicContentHeaderProperties();
+ _testProperties.setHeaders(_testTable);
+ }
+
+ public void testGetPropertyListSize()
+ {
+ //needs a better test but at least we're exercising the code !
+ // FT length is encoded in an int
+ int expectedSize = EncodingUtils.encodedIntegerLength();
+
+ expectedSize += EncodingUtils.encodedShortStringLength("TestInt");
+ // 1 is for the Encoding Letter. here an 'i'
+ expectedSize += 1 + EncodingUtils.encodedIntegerLength();
+
+ expectedSize += EncodingUtils.encodedShortStringLength("TestString");
+ // 1 is for the Encoding Letter. here an 'S'
+ expectedSize += 1 + EncodingUtils.encodedLongStringLength(_testString);
+
+
+ int size = _testProperties.getPropertyListSize();
+
+ assertEquals(expectedSize, size);
+ }
+
+ public void testGetSetPropertyFlags()
+ {
+ _testProperties.setPropertyFlags(99);
+ assertEquals(99, _testProperties.getPropertyFlags());
+ }
+
+ public void testWritePropertyListPayload()
+ {
+ ByteBuffer buf = ByteBuffer.allocate(300);
+ _testProperties.writePropertyListPayload(buf);
+ }
+
+ public void testPopulatePropertiesFromBuffer() throws Exception
+ {
+ ByteBuffer buf = ByteBuffer.allocate(300);
+ _testProperties.populatePropertiesFromBuffer(buf, 99, 99);
+ }
+
+ public void testSetGetContentType()
+ {
+ String contentType = "contentType";
+ _testProperties.setContentType(contentType);
+ assertEquals(contentType, _testProperties.getContentTypeAsString());
+ }
+
+ public void testSetGetEncoding()
+ {
+ String encoding = "encoding";
+ _testProperties.setEncoding(encoding);
+ assertEquals(encoding, _testProperties.getEncodingAsString());
+ }
+
+ public void testSetGetHeaders()
+ {
+ _testProperties.setHeaders(_testTable);
+ assertEquals(_testTable, _testProperties.getHeaders());
+ }
+
+ public void testSetGetDeliveryMode()
+ {
+ byte deliveryMode = 1;
+ _testProperties.setDeliveryMode(deliveryMode);
+ assertEquals(deliveryMode, _testProperties.getDeliveryMode());
+ }
+
+ public void testSetGetPriority()
+ {
+ byte priority = 1;
+ _testProperties.setPriority(priority);
+ assertEquals(priority, _testProperties.getPriority());
+ }
+
+ public void testSetGetCorrelationId()
+ {
+ String correlationId = "correlationId";
+ _testProperties.setCorrelationId(correlationId);
+ assertEquals(correlationId, _testProperties.getCorrelationIdAsString());
+ }
+
+ public void testSetGetReplyTo()
+ {
+ String replyTo = "replyTo";
+ _testProperties.setReplyTo(replyTo);
+ assertEquals(replyTo, _testProperties.getReplyToAsString());
+ }
+
+ public void testSetGetExpiration()
+ {
+ long expiration = 999999999;
+ _testProperties.setExpiration(expiration);
+ assertEquals(expiration, _testProperties.getExpiration());
+ }
+
+ public void testSetGetMessageId()
+ {
+ String messageId = "messageId";
+ _testProperties.setMessageId(messageId);
+ assertEquals(messageId, _testProperties.getMessageIdAsString());
+ }
+
+ public void testSetGetTimestamp()
+ {
+ long timestamp = System.currentTimeMillis();
+ _testProperties.setTimestamp(timestamp);
+ assertEquals(timestamp, _testProperties.getTimestamp());
+ }
+
+ public void testSetGetType()
+ {
+ String type = "type";
+ _testProperties.setType(type);
+ assertEquals(type, _testProperties.getTypeAsString());
+ }
+
+ public void testSetGetUserId()
+ {
+ String userId = "userId";
+ _testProperties.setUserId(userId);
+ assertEquals(userId, _testProperties.getUserIdAsString());
+ }
+
+ public void testSetGetAppId()
+ {
+ String appId = "appId";
+ _testProperties.setAppId(appId);
+ assertEquals(appId, _testProperties.getAppIdAsString());
+ }
+
+ public void testSetGetClusterId()
+ {
+ String clusterId = "clusterId";
+ _testProperties.setClusterId(clusterId);
+ assertEquals(clusterId, _testProperties.getClusterIdAsString());
+ }
+
+}
diff --git a/Final/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java b/Final/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java
new file mode 100644
index 0000000000..e63b0df770
--- /dev/null
+++ b/Final/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java
@@ -0,0 +1,906 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.framing;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.mina.common.ByteBuffer;
+
+import org.apache.qpid.AMQPInvalidClassException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PropertyFieldTableTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(PropertyFieldTableTest.class);
+
+ /**
+ * Test that setting a similar named value replaces any previous value set on that name
+ */
+ public void testReplacement()
+ {
+ FieldTable table1 = new FieldTable();
+ // Set a boolean value
+ table1.setBoolean("value", true);
+ // Check length of table is correct (<Value length> + <type> + <Boolean length>)
+ int size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedBooleanLength();
+ Assert.assertEquals(size, table1.getEncodedSize());
+
+ // reset value to an integer
+ table1.setInteger("value", Integer.MAX_VALUE);
+
+ // Check the length has changed accordingly (<Value length> + <type> + <Integer length>)
+ size = EncodingUtils.encodedShortStringLength("value") + 1 + EncodingUtils.encodedIntegerLength();
+ Assert.assertEquals(size, table1.getEncodedSize());
+
+ // Check boolean value is null
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ // ... and integer value is good
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, table1.getInteger("value"));
+ }
+
+ /**
+ * Set a boolean and check that we can only get it back as a boolean and a string
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testBoolean()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setBoolean("value", true);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Test Getting right value back
+ Assert.assertEquals((Boolean) true, table1.getBoolean("value"));
+
+ // Check we don't get anything back for other gets
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // except value as a string
+ Assert.assertEquals("true", table1.getString("value"));
+
+ table1.remove("value");
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getBoolean("Rubbish"));
+ }
+
+ /**
+ * Set a byte and check that we can only get it back as a byte and a string
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testByte()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setByte("value", Byte.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(Byte.MAX_VALUE, (byte) table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Byte.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getByte("Rubbish"));
+ }
+
+ /**
+ * Set a short and check that we can only get it back as a short and a string
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testShort()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setShort("value", Short.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(Short.MAX_VALUE, (short) table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Short.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getShort("Rubbish"));
+ }
+
+ /**
+ * Set a char and check that we can only get it back as a char
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testChar()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setChar("value", 'c');
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals('c', (char) table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("c", table1.getString("value"));
+
+ table1.remove("value");
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getCharacter("Rubbish"));
+ }
+
+ /**
+ * Set a double and check that we can only get it back as a double
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testDouble()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setDouble("value", Double.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(Double.MAX_VALUE, (double) table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Double.MAX_VALUE, table1.getString("value"));
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getDouble("Rubbish"));
+ }
+
+ /**
+ * Set a float and check that we can only get it back as a float
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testFloat()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setFloat("value", Float.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(Float.MAX_VALUE, (float) table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Float.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getFloat("Rubbish"));
+ }
+
+ /**
+ * Set an int and check that we can only get it back as an int
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testInt()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setInteger("value", Integer.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(Integer.MAX_VALUE, (int) table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Integer.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getInteger("Rubbish"));
+ }
+
+ /**
+ * Set a long and check that we can only get it back as a long
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testLong()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setLong("value", Long.MAX_VALUE);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(Long.MAX_VALUE, (long) table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+
+ // ... and a the string value of it.
+ Assert.assertEquals("" + Long.MAX_VALUE, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getLong("Rubbish"));
+ }
+
+ /**
+ * Set a double and check that we can only get it back as a double
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testBytes()
+ {
+ byte[] bytes = { 99, 98, 97, 96, 95 };
+
+ FieldTable table1 = new FieldTable();
+ table1.setBytes("value", bytes);
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ assertBytesEqual(bytes, table1.getBytes("value"));
+
+ // ... and a the string value of it is null
+ Assert.assertEquals(null, table1.getString("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ // Table should now have zero length for encoding
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getBytes("Rubbish"));
+ }
+
+ /**
+ * Calls all methods that can be used to check the table is empty
+ * - getEncodedSize
+ * - isEmpty
+ * - length
+ *
+ * @param table to check is empty
+ */
+ private void checkEmpty(FieldTable table)
+ {
+ Assert.assertEquals(0, table.getEncodedSize());
+ Assert.assertTrue(table.isEmpty());
+ Assert.assertEquals(0, table.size());
+
+ Assert.assertEquals(0, table.keySet().size());
+ }
+
+ /**
+ * Set a String and check that we can only get it back as a String
+ * Check that attempting to lookup a non existent value returns null
+ */
+ public void testString()
+ {
+ FieldTable table1 = new FieldTable();
+ table1.setString("value", "Hello");
+ Assert.assertTrue(table1.propertyExists("value"));
+
+ // Tets lookups we shouldn't get anything back for other gets
+ // we should get right value back for this type ....
+ Assert.assertEquals(null, table1.getBoolean("value"));
+ Assert.assertEquals(null, table1.getByte("value"));
+ Assert.assertEquals(null, table1.getShort("value"));
+ Assert.assertEquals(null, table1.getCharacter("value"));
+ Assert.assertEquals(null, table1.getDouble("value"));
+ Assert.assertEquals(null, table1.getFloat("value"));
+ Assert.assertEquals(null, table1.getInteger("value"));
+ Assert.assertEquals(null, table1.getLong("value"));
+ Assert.assertEquals(null, table1.getBytes("value"));
+ Assert.assertEquals("Hello", table1.getString("value"));
+
+ // Try setting a null value and read it back
+ table1.setString("value", null);
+
+ Assert.assertEquals(null, table1.getString("value"));
+
+ // but still contains the value
+ Assert.assertTrue(table1.containsKey("value"));
+
+ table1.remove("value");
+ // but after a removeKey it doesn't
+ Assert.assertFalse(table1.containsKey("value"));
+
+ checkEmpty(table1);
+
+ // Looking up an invalid value returns null
+ Assert.assertEquals(null, table1.getString("Rubbish"));
+
+ // Additional Test that haven't been covered for string
+ table1.setObject("value", "Hello");
+ // Check that it was set correctly
+ Assert.assertEquals("Hello", table1.getString("value"));
+ }
+
+ public void testValues()
+ {
+ FieldTable table = new FieldTable();
+ table.setBoolean("bool", true);
+ table.setByte("byte", Byte.MAX_VALUE);
+ byte[] bytes = { 99, 98, 97, 96, 95 };
+ table.setBytes("bytes", bytes);
+ table.setChar("char", 'c');
+ table.setDouble("double", Double.MAX_VALUE);
+ table.setFloat("float", Float.MAX_VALUE);
+ table.setInteger("int", Integer.MAX_VALUE);
+ table.setLong("long", Long.MAX_VALUE);
+ table.setShort("short", Short.MAX_VALUE);
+ table.setString("string", "Hello");
+ table.setString("null-string", null);
+
+ table.setObject("object-bool", true);
+ table.setObject("object-byte", Byte.MAX_VALUE);
+ table.setObject("object-bytes", bytes);
+ table.setObject("object-char", 'c');
+ table.setObject("object-double", Double.MAX_VALUE);
+ table.setObject("object-float", Float.MAX_VALUE);
+ table.setObject("object-int", Integer.MAX_VALUE);
+ table.setObject("object-long", Long.MAX_VALUE);
+ table.setObject("object-short", Short.MAX_VALUE);
+ table.setObject("object-string", "Hello");
+
+ Assert.assertEquals((Boolean) true, table.getBoolean("bool"));
+ Assert.assertEquals((Byte) Byte.MAX_VALUE, table.getByte("byte"));
+ assertBytesEqual(bytes, table.getBytes("bytes"));
+ Assert.assertEquals((Character) 'c', table.getCharacter("char"));
+ Assert.assertEquals(Double.MAX_VALUE, table.getDouble("double"));
+ Assert.assertEquals(Float.MAX_VALUE, table.getFloat("float"));
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, table.getInteger("int"));
+ Assert.assertEquals((Long) Long.MAX_VALUE, table.getLong("long"));
+ Assert.assertEquals((Short) Short.MAX_VALUE, table.getShort("short"));
+ Assert.assertEquals("Hello", table.getString("string"));
+ Assert.assertEquals(null, table.getString("null-string"));
+
+ Assert.assertEquals(true, table.getObject("object-bool"));
+ Assert.assertEquals(Byte.MAX_VALUE, table.getObject("object-byte"));
+ assertBytesEqual(bytes, (byte[]) table.getObject("object-bytes"));
+ Assert.assertEquals('c', table.getObject("object-char"));
+ Assert.assertEquals(Double.MAX_VALUE, table.getObject("object-double"));
+ Assert.assertEquals(Float.MAX_VALUE, table.getObject("object-float"));
+ Assert.assertEquals(Integer.MAX_VALUE, table.getObject("object-int"));
+ Assert.assertEquals(Long.MAX_VALUE, table.getObject("object-long"));
+ Assert.assertEquals(Short.MAX_VALUE, table.getObject("object-short"));
+ Assert.assertEquals("Hello", table.getObject("object-string"));
+ }
+
+ public void testwriteBuffer()
+ {
+ byte[] bytes = { 99, 98, 97, 96, 95 };
+
+ FieldTable table = new FieldTable();
+ table.setBoolean("bool", true);
+ table.setByte("byte", Byte.MAX_VALUE);
+
+ table.setBytes("bytes", bytes);
+ table.setChar("char", 'c');
+ table.setDouble("double", Double.MAX_VALUE);
+ table.setFloat("float", Float.MAX_VALUE);
+ table.setInteger("int", Integer.MAX_VALUE);
+ table.setLong("long", Long.MAX_VALUE);
+ table.setShort("short", Short.MAX_VALUE);
+ table.setString("string", "hello");
+ table.setString("null-string", null);
+
+ final ByteBuffer buffer = ByteBuffer.allocate((int) table.getEncodedSize() + 4); // FIXME XXX: Is cast a problem?
+
+ table.writeToBuffer(buffer);
+
+ buffer.flip();
+
+ long length = buffer.getUnsignedInt();
+
+ try
+ {
+ FieldTable table2 = new FieldTable(buffer, length);
+
+ Assert.assertEquals((Boolean) true, table2.getBoolean("bool"));
+ Assert.assertEquals((Byte) Byte.MAX_VALUE, table2.getByte("byte"));
+ assertBytesEqual(bytes, table2.getBytes("bytes"));
+ Assert.assertEquals((Character) 'c', table2.getCharacter("char"));
+ Assert.assertEquals(Double.MAX_VALUE, table2.getDouble("double"));
+ Assert.assertEquals(Float.MAX_VALUE, table2.getFloat("float"));
+ Assert.assertEquals((Integer) Integer.MAX_VALUE, table2.getInteger("int"));
+ Assert.assertEquals((Long) Long.MAX_VALUE, table2.getLong("long"));
+ Assert.assertEquals((Short) Short.MAX_VALUE, table2.getShort("short"));
+ Assert.assertEquals("hello", table2.getString("string"));
+ Assert.assertEquals(null, table2.getString("null-string"));
+
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ e.printStackTrace();
+ fail("PFT should be instantiated from bytes." + e.getCause());
+ }
+ }
+
+ public void testEncodingSize()
+ {
+ FieldTable result = new FieldTable();
+ int size = 0;
+
+ result.setBoolean("boolean", true);
+ size += 1 + EncodingUtils.encodedShortStringLength("boolean") + EncodingUtils.encodedBooleanLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setByte("byte", (byte) Byte.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("byte") + EncodingUtils.encodedByteLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ byte[] _bytes = { 99, 98, 97, 96, 95 };
+
+ result.setBytes("bytes", _bytes);
+ size += 1 + EncodingUtils.encodedShortStringLength("bytes") + 4 + _bytes.length;
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setChar("char", (char) 'c');
+ size += 1 + EncodingUtils.encodedShortStringLength("char") + EncodingUtils.encodedCharLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setDouble("double", (double) Double.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("double") + EncodingUtils.encodedDoubleLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setFloat("float", (float) Float.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("float") + EncodingUtils.encodedFloatLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setInteger("int", (int) Integer.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("int") + EncodingUtils.encodedIntegerLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setLong("long", (long) Long.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("long") + EncodingUtils.encodedLongLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setShort("short", (short) Short.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("short") + EncodingUtils.encodedShortLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setString("result", "Hello");
+ size += 1 + EncodingUtils.encodedShortStringLength("result") + EncodingUtils.encodedLongStringLength("Hello");
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-bool", true);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-bool") + EncodingUtils.encodedBooleanLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-byte", Byte.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-byte") + EncodingUtils.encodedByteLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-bytes", _bytes);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-bytes") + 4 + _bytes.length;
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-char", 'c');
+ size += 1 + EncodingUtils.encodedShortStringLength("object-char") + EncodingUtils.encodedCharLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-double", Double.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-double") + EncodingUtils.encodedDoubleLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-float", Float.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-float") + EncodingUtils.encodedFloatLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-int", Integer.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-int") + EncodingUtils.encodedIntegerLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-long", Long.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-long") + EncodingUtils.encodedLongLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ result.setObject("object-short", Short.MAX_VALUE);
+ size += 1 + EncodingUtils.encodedShortStringLength("object-short") + EncodingUtils.encodedShortLength();
+ Assert.assertEquals(size, result.getEncodedSize());
+
+ }
+
+ // public void testEncodingSize1()
+ // {
+ // PropertyFieldTable table = new PropertyFieldTable();
+ // int length = 0;
+ // result.put("one", 1L);
+ // length = EncodingUtils.encodedShortStringLength("one");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("two", 2L);
+ // length += EncodingUtils.encodedShortStringLength("two");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("three", 3L);
+ // length += EncodingUtils.encodedShortStringLength("three");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("four", 4L);
+ // length += EncodingUtils.encodedShortStringLength("four");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // result.put("five", 5L);
+ // length += EncodingUtils.encodedShortStringLength("five");
+ // length += 1 + EncodingUtils.encodedLongLength();
+ // assertEquals(length, result.getEncodedSize());
+ //
+ // //fixme should perhaps be expanded to incorporate all types.
+ //
+ // final ByteBuffer buffer = ByteBuffer.allocate((int) result.getEncodedSize()); // FIXME XXX: Is cast a problem?
+ //
+ // result.writeToBuffer(buffer);
+ //
+ // buffer.flip();
+ //
+ // long length = buffer.getUnsignedInt();
+ //
+ // try
+ // {
+ // PropertyFieldTable table2 = new PropertyFieldTable(buffer, length);
+ //
+ // Assert.assertEquals((Long) 1L, table2.getLong("one"));
+ // Assert.assertEquals((Long) 2L, table2.getLong("two"));
+ // Assert.assertEquals((Long) 3L, table2.getLong("three"));
+ // Assert.assertEquals((Long) 4L, table2.getLong("four"));
+ // Assert.assertEquals((Long) 5L, table2.getLong("five"));
+ // }
+ // catch (AMQFrameDecodingException e)
+ // {
+ // e.printStackTrace();
+ // fail("PFT should be instantiated from bytes." + e.getCause());
+ // }
+ //
+ // }
+
+ /**
+ * Additional test for setObject
+ */
+ public void testSetObject()
+ {
+ FieldTable table = new FieldTable();
+
+ // Try setting a non primative object
+
+ try
+ {
+ table.setObject("value", this);
+ fail("Only primative values allowed in setObject");
+ }
+ catch (AMQPInvalidClassException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ }
+
+ /**
+ * Additional test checkPropertyName doesn't accept Null
+ */
+ public void testCheckPropertyNameasNull()
+ {
+ FieldTable table = new FieldTable();
+
+ try
+ {
+ table.setObject((String) null, "String");
+ fail("Null property name is not allowed");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ }
+
+ /**
+ * Additional test checkPropertyName doesn't accept an empty String
+ */
+ public void testCheckPropertyNameasEmptyString()
+ {
+ FieldTable table = new FieldTable();
+
+ try
+ {
+ table.setObject("", "String");
+ fail("empty property name is not allowed");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ }
+
+ /**
+ * Additional test checkPropertyName doesn't accept an empty String
+ */
+ public void testCheckPropertyNamehasMaxLength()
+ {
+ String oldVal = System.getProperty("STRICT_AMQP");
+ System.setProperty("STRICT_AMQP", "true");
+ FieldTable table = new FieldTable();
+
+ StringBuffer longPropertyName = new StringBuffer(129);
+
+ for (int i = 0; i < 129; i++)
+ {
+ longPropertyName.append("x");
+ }
+
+ try
+ {
+ table.setObject(longPropertyName.toString(), "String");
+ fail("property name must be < 128 characters");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ if (oldVal != null)
+ {
+ System.setProperty("STRICT_AMQP", oldVal);
+ }
+ else
+ {
+ System.clearProperty("STRICT_AMQP");
+ }
+ }
+
+ /**
+ * Additional test checkPropertyName starts with a letter
+ */
+ public void testCheckPropertyNameStartCharacterIsLetter()
+ {
+ String oldVal = System.getProperty("STRICT_AMQP");
+ System.setProperty("STRICT_AMQP", "true");
+ FieldTable table = new FieldTable();
+
+ // Try a name that starts with a number
+ try
+ {
+ table.setObject("1", "String");
+ fail("property name must start with a letter");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // normal path
+ }
+ // so length should be zero
+ Assert.assertEquals(0, table.getEncodedSize());
+ if (oldVal != null)
+ {
+ System.setProperty("STRICT_AMQP", oldVal);
+ }
+ else
+ {
+ System.clearProperty("STRICT_AMQP");
+ }
+ }
+
+ /**
+ * Additional test checkPropertyName starts with a hash or a dollar
+ */
+ public void testCheckPropertyNameStartCharacterIsHashorDollar()
+ {
+ String oldVal = System.getProperty("STRICT_AMQP");
+ System.setProperty("STRICT_AMQP", "true");
+ FieldTable table = new FieldTable();
+
+ // Try a name that starts with a number
+ try
+ {
+ table.setObject("#", "String");
+ table.setObject("$", "String");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ fail("property name are allowed to start with # and $s");
+ }
+
+ if (oldVal != null)
+ {
+ System.setProperty("STRICT_AMQP", oldVal);
+ }
+ else
+ {
+ System.clearProperty("STRICT_AMQP");
+ }
+ }
+
+ /**
+ * Additional test to test the contents of the table
+ */
+ public void testContents()
+ {
+ FieldTable table = new FieldTable();
+
+ table.setObject("StringProperty", "String");
+
+ Assert.assertEquals("String", table.getString("StringProperty"));
+
+ // Test Clear
+
+ table.clear();
+
+ checkEmpty(table);
+ }
+
+ /**
+ * Test the contents of the sets
+ */
+ public void testSets()
+ {
+
+ FieldTable table = new FieldTable();
+
+ table.setObject("n1", "1");
+ table.setObject("n2", "2");
+ table.setObject("n3", "3");
+
+ Assert.assertEquals("1", table.getObject("n1"));
+ Assert.assertEquals("2", table.getObject("n2"));
+ Assert.assertEquals("3", table.getObject("n3"));
+
+ }
+
+ private void assertBytesEqual(byte[] expected, byte[] actual)
+ {
+ Assert.assertEquals(expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ Assert.assertEquals(expected[index], actual[index]);
+ }
+ }
+
+ private void assertBytesNotEqual(byte[] expected, byte[] actual)
+ {
+ Assert.assertEquals(expected.length, actual.length);
+
+ for (int index = 0; index < expected.length; index++)
+ {
+ Assert.assertFalse(expected[index] == actual[index]);
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(PropertyFieldTableTest.class);
+ }
+
+}
diff --git a/Final/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java b/Final/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java
new file mode 100644
index 0000000000..6383d52298
--- /dev/null
+++ b/Final/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.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.pool;
+
+import junit.framework.TestCase;
+import junit.framework.Assert;
+import org.apache.qpid.session.TestSession;
+import org.apache.mina.common.IoFilter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.IdleStatus;
+
+import java.util.concurrent.RejectedExecutionException;
+
+public class PoolingFilterTest extends TestCase
+{
+ private PoolingFilter _pool;
+ ReferenceCountingExecutorService _executorService;
+
+ public void setUp()
+ {
+
+ //Create Pool
+ _executorService = ReferenceCountingExecutorService.getInstance();
+ _executorService.acquireExecutorService();
+ _pool = PoolingFilter.createAynschWritePoolingFilter(_executorService,
+ "AsynchronousWriteFilter");
+
+ }
+
+ public void testRejectedExecution() throws Exception
+ {
+
+ TestSession testSession = new TestSession();
+ _pool.createNewJobForSession(testSession);
+ _pool.filterWrite(new NoOpFilter(), testSession, new IoFilter.WriteRequest("Message"));
+
+ //Shutdown the pool
+ _executorService.getPool().shutdownNow();
+
+ try
+ {
+
+ testSession = new TestSession();
+ _pool.createNewJobForSession(testSession);
+ //prior to fix for QPID-172 this would throw RejectedExecutionException
+ _pool.filterWrite(null, testSession, null);
+ }
+ catch (RejectedExecutionException rje)
+ {
+ Assert.fail("RejectedExecutionException should not occur after pool has shutdown:" + rje);
+ }
+ }
+
+ private static class NoOpFilter implements IoFilter.NextFilter
+ {
+
+ public void sessionOpened(IoSession session)
+ {
+ }
+
+ public void sessionClosed(IoSession session)
+ {
+ }
+
+ public void sessionIdle(IoSession session, IdleStatus status)
+ {
+ }
+
+ public void exceptionCaught(IoSession session, Throwable cause)
+ {
+ }
+
+ public void messageReceived(IoSession session, Object message)
+ {
+ }
+
+ public void messageSent(IoSession session, Object message)
+ {
+ }
+
+ public void filterWrite(IoSession session, IoFilter.WriteRequest writeRequest)
+ {
+ }
+
+ public void filterClose(IoSession session)
+ {
+ }
+
+ public void sessionCreated(IoSession session)
+ {
+ }
+ }
+}
diff --git a/Final/java/common/src/test/java/org/apache/qpid/session/TestSession.java b/Final/java/common/src/test/java/org/apache/qpid/session/TestSession.java
new file mode 100644
index 0000000000..aafc91b03b
--- /dev/null
+++ b/Final/java/common/src/test/java/org/apache/qpid/session/TestSession.java
@@ -0,0 +1,277 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.session;
+
+import org.apache.mina.common.*;
+
+import java.net.SocketAddress;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class TestSession implements IoSession
+{
+ private final ConcurrentMap attributes = new ConcurrentHashMap();
+
+ public TestSession()
+ {
+ }
+
+ 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 getAttribute("");
+ }
+
+ public Object setAttachment(Object attachment)
+ {
+ return setAttribute("",attachment);
+ }
+
+ public Object getAttribute(String key)
+ {
+ return attributes.get(key);
+ }
+
+ public Object setAttribute(String key, Object value)
+ {
+ return attributes.put(key,value);
+ }
+
+ public Object setAttribute(String key)
+ {
+ return attributes.put(key, Boolean.TRUE);
+ }
+
+ public Object removeAttribute(String key)
+ {
+ return attributes.remove(key);
+ }
+
+ public boolean containsAttribute(String key)
+ {
+ return attributes.containsKey(key);
+ }
+
+ public Set getAttributeKeys()
+ {
+ return attributes.keySet();
+ }
+
+ 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/Final/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java b/Final/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java
new file mode 100644
index 0000000000..66fb3252df
--- /dev/null
+++ b/Final/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java
@@ -0,0 +1,554 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.util;
+
+import junit.framework.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+
+/**
+ * Unit tests the {@link CommandLineParser} class.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that parsing a single flag works ok.
+ * <tr><td> Check that parsing multiple flags condensed together works ok.
+ * <tr><td> Check that parsing an option with a space between it and its argument works ok.
+ * <tr><td> Check that parsing an option with no space between it and its argument works ok.
+ * <tr><td> Check that parsing an option with specific argument format works ok.
+ * <tr><td> Check that parsing an option with specific argument format fails on bad argument.
+ * <tr><td> Check that parsing a flag condensed together with an option fails.
+ * <tr><td> Check that parsing a free argument works ok.
+ * <tr><td> Check that parsing a free argument with specific format works ok.
+ * <tr><td> Check that parsing a free argument with specific format fails on bad argument.
+ * <tr><td> Check that parsing a mandatory option works ok.
+ * <tr><td> Check that parsing a mandatory free argument works ok.
+ * <tr><td> Check that parsing a mandatory option fails when no option is set.
+ * <tr><td> Check that parsing a mandatory free argument fails when no argument is specified.
+ * <tr><td> Check that parsing an unknown option works when unknowns not errors.
+ * <tr><td> Check that parsing an unknown flag fails when unknowns are to be reported as errors.
+ * <tr><td> Check that parsing an unknown option fails when unknowns are to be reported as errors.
+ * <tr><td> Check that get errors returns a string on errors.
+ * <tr><td> Check that get errors returns an empty string on no errors.
+ * <tr><td> Check that get usage returns a string.
+ * <tr><td> Check that get options in force returns an empty string before parsing.
+ * <tr><td> Check that get options in force return a non-empty string after parsing.
+ * </table>
+ */
+public class CommandLineParserTest extends TestCase
+{
+ private static final Logger log = LoggerFactory.getLogger(CommandLineParserTest.class);
+
+ public CommandLineParserTest(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Compile all the tests for the default test implementation of a traversable state into a test suite.
+ */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("CommandLineParser Tests");
+
+ // Add all the tests defined in this class (using the default constructor)
+ suite.addTestSuite(CommandLineParserTest.class);
+
+ return suite;
+ }
+
+ /** Check that get errors returns an empty string on no errors. */
+ public void testGetErrorsReturnsEmptyStringOnNoErrors() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Do some legal parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
+
+ // Check that the get errors message returns an empty string.
+ assertTrue("The errors method did not return an empty string.", "".equals(parser.getErrors()));
+ }
+
+ /** Check that get errors returns a string on errors. */
+ public void testGetErrorsReturnsStringOnErrors() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ try
+ {
+ // Do some illegal parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t1t2test", "-t4fail" });
+ }
+ catch (IllegalArgumentException e)
+ { }
+
+ // Check that the get errors message returns a string.
+ assertTrue("The errors method returned an empty string.",
+ !((parser.getErrors() == null) || "".equals(parser.getErrors())));
+
+ }
+
+ /** Check that get options in force returns an empty string before parsing. */
+ public void testGetOptionsInForceReturnsEmptyStringBeforeParsing() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Check that the options in force method returns an empty string.
+ assertTrue("The options in force method did not return an empty string.", "".equals(parser.getOptionsInForce()));
+ }
+
+ /** Check that get options in force return a non-empty string after parsing. */
+ public void testGetOptionsInForceReturnsNonEmptyStringAfterParsing() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Do some parsing.
+ parser.parseCommandLine(new String[] { "-t1", "-t2test", "-t3test", "-t4test" });
+
+ // Check that the options in force method returns a string.
+ assertTrue("The options in force method did not return a non empty string.",
+ !((parser.getOptionsInForce() == null) || "".equals(parser.getOptionsInForce())));
+ }
+
+ /** Check that get usage returns a string. */
+ public void testGetUsageReturnsString() throws Exception
+ {
+ // Create a command line parser for some flags and options.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" },
+ { "t3", "Test Option 3.", "test", "true" },
+ { "t4", "Test Option 4.", "test", null, "^test$" }
+ });
+
+ // Check that the usage method returns a string.
+ assertTrue("The usage method did not return a non empty string.",
+ !((parser.getUsage() == null) || "".equals(parser.getUsage())));
+ }
+
+ /** Check that parsing multiple flags condensed together works ok. */
+ public void testParseCondensedFlagsOk() throws Exception
+ {
+ // Create a command line parser for multiple flags.
+ CommandLineParser parser =
+ new CommandLineParser(
+ new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Flag 2." },
+ { "t3", "Test Flag 3." }
+ });
+
+ // Parse a command line with the flags set and condensed together.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t1t2t3" });
+
+ // Check that the flags were set in the parsed properties.
+ assertTrue("The t1 flag was not \"true\", it was: " + testProps.get("t1"), "true".equals(testProps.get("t1")));
+ assertTrue("The t2 flag was not \"true\", it was: " + testProps.get("t2"), "true".equals(testProps.get("t2")));
+ assertTrue("The t3 flag was not \"true\", it was: " + testProps.get("t3"), "true".equals(testProps.get("t3")));
+ }
+
+ /** Check that parsing a flag condensed together with an option fails. */
+ public void testParseFlagCondensedWithOptionFails() throws Exception
+ {
+ // Create a command line parser for a flag and an option.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "t1", "Test Flag 1." },
+ { "t2", "Test Option 2.", "test" }
+ });
+
+ // Check that the parser reports an error.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with the flag and option condensed together.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t1t2" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a flag and option are condensed together.", testPassed);
+ }
+
+ /** Check that parsing a free argument with specific format fails on bad argument. */
+ public void testParseFormattedFreeArgumentFailsBadArgument() throws Exception
+ {
+ // Create a command line parser for a formatted free argument.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test", null, "^test$" }
+ });
+
+ // Check that the parser signals an error for a badly formatted argument.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option set incorrectly.
+ Properties testProps = parser.parseCommandLine(new String[] { "fail" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
+ }
+
+ /** Check that parsing a free argument with specific format works ok. */
+ public void testParseFormattedFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a formatted free argument.
+ CommandLineParser parser =
+ new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test", null, "^test$" }
+ });
+
+ // Parse a command line with this argument set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing an option with specific argument format fails on bad argument. */
+ public void testParseFormattedOptionArgumentFailsBadArgument() throws Exception
+ {
+ // Create a command line parser for a formatted option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", null, "^test$" }
+ });
+
+ // Check that the parser signals an error for a badly formatted argument.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option set incorrectly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "fail" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown when a badly formatted argument was set.", testPassed);
+ }
+
+ /** Check that parsing an option with specific argument format works ok. */
+ public void testParseFormattedOptionArgumentOk() throws Exception
+ {
+ // Create a command line parser for a formatted option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", null, "^test$" }
+ });
+
+ // Parse a command line with this option set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a free argument works ok. */
+ public void testParseFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Free Argument.", "test" }
+ });
+
+ // Parse a command line with this argument set.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing a mandatory option works ok. */
+ public void testParseMandatoryOptionOk() throws Exception
+ {
+ // Create a command line parser for a mandatory option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", "true" }
+ });
+
+ // Parse a command line with this option set correctly.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a mandatory free argument works ok. */
+ public void testParseMandatoryFreeArgumentOk() throws Exception
+ {
+ // Create a command line parser for a mandatory free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Option.", "test", "true" }
+ });
+
+ // Parse a command line with this argument set.
+ Properties testProps = parser.parseCommandLine(new String[] { "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The first free argument was not equal to \"test\" but was: " + testProps.get("1"),
+ "test".equals(testProps.get("1")));
+ }
+
+ /** Check that parsing a mandatory free argument fails when no argument is specified. */
+ public void testParseManadatoryFreeArgumentFailsNoArgument() throws Exception
+ {
+ // Create a command line parser for a mandatory free argument.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "1", "Test Option.", "test", "true" }
+ });
+
+ // Check that parsing fails when this mandatory free argument is missing.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this free argument not set.
+ Properties testProps = parser.parseCommandLine(new String[] {});
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
+ }
+
+ /** Check that parsing a mandatory option fails when no option is set. */
+ public void testParseMandatoryFailsNoOption() throws Exception
+ {
+ // Create a command line parser for a mandatory option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test", "true" }
+ });
+
+ // Check that parsing fails when this mandatory option is missing.
+ boolean testPassed = false;
+
+ try
+ {
+ // Parse a command line with this option not set.
+ Properties testProps = parser.parseCommandLine(new String[] {});
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("IllegalArgumentException not thrown for a missing mandatory option.", testPassed);
+ }
+
+ /** Check that parsing an option with no space between it and its argument works ok. */
+ public void testParseOptionWithNoSpaceOk() throws Exception
+ {
+ // Create a command line parser for an option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test" }
+ });
+
+ // Parse a command line with this option set with no space.
+ Properties testProps = parser.parseCommandLine(new String[] { "-ttest" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing an option with a space between it and its argument works ok. */
+ public void testParseOptionWithSpaceOk() throws Exception
+ {
+ // Create a command line parser for an option.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Option.", "test" }
+ });
+
+ // Parse a command line with this option set with a space.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t", "test" });
+
+ // Check that the resultant properties contains the correctly parsed option.
+ assertTrue("The test option was not equal to \"test\" but was: " + testProps.get("t"),
+ "test".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing a single flag works ok. */
+ public void testParseSingleFlagOk() throws Exception
+ {
+ // Create a command line parser for a single flag.
+ CommandLineParser parser = new CommandLineParser(new String[][]
+ {
+ { "t", "Test Flag." }
+ });
+
+ // Parse a command line with the single flag set.
+ Properties testProps = parser.parseCommandLine(new String[] { "-t" });
+
+ // Check that the flag is set in the parsed properties.
+ assertTrue("The t flag was not \"true\", it was: " + testProps.get("t"), "true".equals(testProps.get("t")));
+
+ // Reset the parser.
+ parser.reset();
+
+ // Parse a command line with the single flag not set.
+ testProps = parser.parseCommandLine(new String[] {});
+
+ // Check that the flag is cleared in the parsed properties.
+ assertTrue("The t flag was not \"false\", it was: " + testProps.get("t"), "false".equals(testProps.get("t")));
+ }
+
+ /** Check that parsing an unknown option works when unknowns not errors. */
+ public void testParseUnknownOptionOk() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Check that parsing does not fail on an unknown flag.
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ fail("The parser threw an IllegalArgumentException on an unknown flag when errors on unkowns is off.");
+ }
+ }
+
+ /** Check that parsing an unknown flag fails when unknowns are to be reported as errors. */
+ public void testParseUnknownFlagFailsWhenUnknownsAreErrors() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Turn on fail on unknowns mode.
+ parser.setErrorsOnUnknowns(true);
+
+ // Check that parsing fails on an unknown flag.
+ boolean testPassed = false;
+
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown for an unknown flag when errors on unknowns mode is on.",
+ testPassed);
+ }
+
+ /** Check that parsing an unknown option fails when unknowns are to be reported as errors. */
+ public void testParseUnknownOptionFailsWhenUnknownsAreErrors() throws Exception
+ {
+ // Create a command line parser for no flags or options
+ CommandLineParser parser = new CommandLineParser(new String[][] {});
+
+ // Turn on fail on unknowns mode.
+ parser.setErrorsOnUnknowns(true);
+
+ // Check that parsing fails on an unknown flag.
+ boolean testPassed = false;
+
+ try
+ {
+ parser.parseCommandLine(new String[] { "-t", "test" });
+ }
+ catch (IllegalArgumentException e)
+ {
+ testPassed = true;
+ }
+
+ assertTrue("IllegalArgumentException not thrown for an unknown option when errors on unknowns mode is on.",
+ testPassed);
+ }
+}
diff --git a/Final/java/distribution/pom.xml b/Final/java/distribution/pom.xml
new file mode 100644
index 0000000000..ecfdfd5345
--- /dev/null
+++ b/Final/java/distribution/pom.xml
@@ -0,0 +1,210 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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-distribution</artifactId>
+ <packaging>jar</packaging>
+ <name>Qpid Distribution</name>
+ <version>1.0-incubating-M2</version>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <java.source.version>1.5</java.source.version>
+ <qpid.version>${pom.version}</qpid.version>
+ <qpid.targetDir>${project.build.directory}</qpid.targetDir>
+
+ <!-- This is an assembly/distribution pom so no test code exists -->
+ <maven.test.skip>true</maven.test.skip>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>repo1.maven.org</id>
+ <name>Maven eclipse Repository</name>
+ <url>http://repo1.maven.org/eclipse</url>
+ </repository>
+ </repositories>
+
+ <dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <version>${pom.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-client</artifactId>
+ <version>${pom.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}.management</groupId>
+ <artifactId>org.apache.qpid.management.ui</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <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>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>${assembly.version}</version>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/bin.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ <outputDirectory>${qpid.targetDir}</outputDirectory>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <finalName>qpid-incubating</finalName>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/bin.xml</descriptor>
+ <descriptor>src/main/assembly/src.xml</descriptor>
+ <descriptor>src/main/assembly/management-eclipse-plugin.xml</descriptor>
+ <descriptor>src/main/assembly/management-eclipse-plugin-unix.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <defaultGoal>assembly:assembly</defaultGoal>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>tests</id>
+
+ <dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <version>${pom.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-client</artifactId>
+ <version>${pom.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-client</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-perftests</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>qpid-systests</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/bin-test.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ </profile>
+ </profiles>
+
+</project>
diff --git a/Final/java/distribution/src/main/assembly/bin-test.xml b/Final/java/distribution/src/main/assembly/bin-test.xml
new file mode 100644
index 0000000000..04d83916e4
--- /dev/null
+++ b/Final/java/distribution/src/main/assembly/bin-test.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>java-bin-with-tests</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <!-- Include the currently hardwired Apache Legal bits -->
+ <fileSet>
+ <directory>../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ <include>README</include>
+ </includes>
+ <fileMode>0444</fileMode>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>src/main/release</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ </fileSet>
+
+ <!-- Include the classpath jar qpid-incubating.jar -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-incubating.jar</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include etc -->
+ <fileSet>
+ <directory>../common/etc</directory>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <fileMode>0640</fileMode>
+ </fileSet>
+ <fileSet>
+ <directory>../broker/etc</directory>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <fileMode>0640</fileMode>
+ </fileSet>
+
+ <!-- Include bin scripts-->
+ <fileSet>
+ <directory>../broker/bin</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <fileMode>0750</fileMode>
+ </fileSet>
+ <fileSet>
+ <directory>../common/bin</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <fileMode>0750</fileMode>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <!-- Exclude the jar output of this build process -->
+ <exclude>org.apache.qpid:qpid-distribution</exclude>
+ <!-- Exclude the Console dependancies -->
+ <exclude>org.apache.qpid.management:org.apache.qpid.management.ui</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.commands</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.contenttype</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.expressions</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.jobs</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime.compatibility.auth</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime.compatibility.registry</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.common</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.preferences</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.registry</exclude>
+ <exclude>org.eclipse.help:org.eclipse.help</exclude>
+ <exclude>org.eclipse.jface:org.eclipse.jface</exclude>
+ <exclude>org.eclipse.osgi:org.eclipse.osgi</exclude>
+ <exclude>org.eclipse.swt:org.eclipse.swt</exclude>
+ <exclude>org.eclipse.swt:org.eclipse.swt.win32.win32.x86</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui.forms</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui.workbench</exclude>
+ </excludes>
+ <scope>runtime</scope>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/distribution/src/main/assembly/bin.xml b/Final/java/distribution/src/main/assembly/bin.xml
new file mode 100644
index 0000000000..3620659e48
--- /dev/null
+++ b/Final/java/distribution/src/main/assembly/bin.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>java-bin</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <!-- Include the currently hardwired Apache Legal bits -->
+ <fileSet>
+ <directory>../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ <include>README</include>
+ </includes>
+ <fileMode>0444</fileMode>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>src/main/release</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>..//release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ </fileSet>
+
+ <!-- Include the classpath jar qpid-incubating.jar -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-incubating.jar</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include etc -->
+ <fileSet>
+ <directory>../common/etc</directory>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <fileMode>0640</fileMode>
+ </fileSet>
+ <fileSet>
+ <directory>../broker/etc</directory>
+ <outputDirectory>qpid-${qpid.version}/etc</outputDirectory>
+ <fileMode>0640</fileMode>
+ </fileSet>
+
+ <!-- Include bin scripts-->
+ <fileSet>
+ <directory>../broker/bin</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <fileMode>0750</fileMode>
+ </fileSet>
+ <fileSet>
+ <directory>../common/bin</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <fileMode>0750</fileMode>
+ </fileSet>
+
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <!-- Exclude the jar output of this build process -->
+ <exclude>org.apache.qpid:qpid-distribution</exclude>
+ <!-- Exclude the Console dependancies -->
+ <exclude>org.apache.qpid.management:org.apache.qpid.management.ui</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.commands</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.contenttype</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.expressions</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.jobs</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime.compatibility.auth</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime.compatibility.registry</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.common</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.preferences</exclude>
+ <exclude>org.eclipse.equinox:org.eclipse.equinox.registry</exclude>
+ <exclude>org.eclipse.help:org.eclipse.help</exclude>
+ <exclude>org.eclipse.jface:org.eclipse.jface</exclude>
+ <exclude>org.eclipse.osgi:org.eclipse.osgi</exclude>
+ <exclude>org.eclipse.swt:org.eclipse.swt</exclude>
+ <exclude>org.eclipse.swt:org.eclipse.swt.win32.win32.x86</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui.forms</exclude>
+ <exclude>org.eclipse.ui:org.eclipse.ui.workbench</exclude>
+ </excludes>
+ <scope>runtime</scope>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/distribution/src/main/assembly/management-eclipse-plugin-unix.xml b/Final/java/distribution/src/main/assembly/management-eclipse-plugin-unix.xml
new file mode 100644
index 0000000000..02ede019e5
--- /dev/null
+++ b/Final/java/distribution/src/main/assembly/management-eclipse-plugin-unix.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>management-console-unix</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ </formats>
+<!--
+ <moduleSets>
+ <moduleSet>
+ <includes>
+ <include>org.apache.qpid.management:org.apache.qpid.management.ui</include>
+ </includes>
+ <binaries>
+ <includeDependencies>true</includeDependencies>
+ <unpack>false</unpack>
+ </binaries>
+ </moduleSet>
+ </moduleSets>
+ -->
+ <fileSets>
+
+
+ <!-- Include the currently hardwired Apache Legal bits -->
+ <fileSet>
+ <directory>../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ <include>README</include>
+ </includes>
+ <fileMode>0444</fileMode>
+ </fileSet>
+
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>src/main/release</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>../release-docs</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin/src/main/resources/unix/configuration</directory>
+ <outputDirectory>qpidmc/configuration</outputDirectory>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin/src/main/resources</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ <includes>
+ <include>license.eclipse.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ <includes>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin/bin</directory>
+ <outputDirectory>qpidmc/bin</outputDirectory>
+ <includes>
+ <include>qpidmc*.sh</include>
+ </includes>
+ <fileMode>0777</fileMode>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpidmc/eclipse/plugins/org.eclipse.core.runtime.compatibility.registry_3.2.0</outputDirectory>
+ <outputFileNameMapping>${artifactId}_${version}/</outputFileNameMapping>
+ <unpack>true</unpack>
+ <includes>
+ <include>org.eclipse.core:org.eclipse.core.runtime.compatibility.registry</include>
+ </includes>
+ <scope>runtime</scope>
+ </dependencySet>
+
+ <dependencySet>
+ <outputDirectory>qpidmc/eclipse/plugins</outputDirectory>
+ <outputFileNameMapping>${artifactId}_${version}.${extension}</outputFileNameMapping>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-distribution</exclude>
+ </excludes>
+ <includes>
+ <include>org.eclipse.ui:org.eclipse.ui.forms</include>
+ <include>org.apache.qpid.management:org.apache.qpid.management.ui</include>
+ </includes>
+ <scope>runtime</scope>
+ </dependencySet>
+</dependencySets>
+</assembly>
diff --git a/Final/java/distribution/src/main/assembly/management-eclipse-plugin.xml b/Final/java/distribution/src/main/assembly/management-eclipse-plugin.xml
new file mode 100644
index 0000000000..05df2c030b
--- /dev/null
+++ b/Final/java/distribution/src/main/assembly/management-eclipse-plugin.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>management-console-win32</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+
+ <!-- Include the currently hardwired Apache Legal bits -->
+ <fileSet>
+ <directory>../resources</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ <include>README</include>
+ </includes>
+ <fileMode>0444</fileMode>
+ </fileSet>
+
+ <fileSet>
+ <directory>src/main/release</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>../release-docs</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ </fileSet>
+
+
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin/src/main/resources/win32/configuration</directory>
+ <outputDirectory>qpidmc/configuration</outputDirectory>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin/src/main/resources</directory>
+ <outputDirectory>qpidmc/eclipse</outputDirectory>
+ <includes>
+ <include>*.*</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin</directory>
+ <outputDirectory>qpidmc</outputDirectory>
+ <includes>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../management/eclipse-plugin/bin</directory>
+ <outputDirectory>qpidmc/bin</outputDirectory>
+ <excludes>
+ <!-- Exclude the Unix scripts -->
+ <exclude>qpidmc*.sh</exclude>
+ </excludes>
+ <fileMode>0777</fileMode>
+ </fileSet>
+
+ <!-- Required to make eclipse load the JMXRemote SASL pluging -->
+ <!-- This should be removed as JMXRemote SASL module is not ASF license friendly -->
+ <!-- However management console doesn't seem to work without it -->
+ <fileSet>
+ <directory>../management/eclipse-plugin/src/main/resources/sasl</directory>
+ <outputDirectory>qpidmc/eclipse/plugins/jmxremote.sasl_1.0.1/META-INF</outputDirectory>
+ <includes>
+ <include>MANIFEST.MF</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpidmc/eclipse/plugins</outputDirectory>
+ <outputFileNameMapping>${artifactId}_${version}.${extension}</outputFileNameMapping>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-distribution</exclude>
+ <exclude>org.apache.qpid:qpid-common</exclude>
+ <exclude>org.apache.qpid:qpid-broker</exclude>
+ <exclude>org.apache.qpid:qpid-client</exclude>
+ <exclude>commons-cli:commons-cli</exclude>
+ <exclude>commons-configuration:commons-configuration</exclude>
+ <exclude>commons-lang:commons-lang</exclude>
+ <exclude>org.apache.mina:mina-filter-ssl</exclude>
+ <exclude>org.apache.mina:mina-java5</exclude>
+ <exclude>backport-util-concurrent:backport-util-concurrent</exclude>
+ <exclude>org.slf4j:slf4j-simple</exclude>
+ <exclude>junit:junit</exclude>
+ <exclude>org.easymock:easymockclassextension</exclude>
+ <exclude>commons-codec:commons-codec</exclude>
+ <exclude>org.apache.geronimo.specs:geronimo-jms_1.1_spec</exclude>
+ <exclude>commons-collections:commons-collections</exclude>
+ <exclude>commons-lang:commons-lang</exclude>
+ <exclude>org.apache.mina:mina-core</exclude>
+ <exclude>commons-beanutils:commons-beanutils</exclude>
+ <exclude>commons-beanutils:commons-beanutils-core</exclude>
+ <exclude>commons-digester:commons-digester</exclude>
+ <exclude>commons-logging:commons-logging</exclude>
+ <exclude>commons-logging:commons-logging-api</exclude>
+ <exclude>dom4j:dom4j</exclude>
+ <exclude>isorelax:isorelax</exclude>
+ <exclude>jaxen:jaxen</exclude>
+ <exclude>log4j:log4j</exclude>
+ <exclude>msv:msv</exclude>
+ <exclude>xalan:xalan</exclude>
+ <exclude>xml-apis:xml-apis</exclude>
+ <exclude>saxpath:saxpath</exclude>
+ <exclude>servletapi:servletapi</exclude>
+ <exclude>relaxngDatatype:relaxngDatatype</exclude>
+ <exclude>xerces:xercesImpl</exclude>
+ <exclude>javax.servlet:servlet-api</exclude>
+ <exclude>org.eclipse.core:org.eclipse.core.runtime.compatibility.registry</exclude>
+ </excludes>
+ <scope>runtime</scope>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>qpidmc/eclipse/plugins/org.eclipse.core.runtime.compatibility.registry_3.2.0</outputDirectory>
+ <outputFileNameMapping>${artifactId}_${version}/</outputFileNameMapping>
+ <unpack>true</unpack>
+ <includes>
+ <include>org.eclipse.core:org.eclipse.core.runtime.compatibility.registry</include>
+ </includes>
+ <scope>runtime</scope>
+ </dependencySet>
+ </dependencySets>
+
+</assembly>
diff --git a/Final/java/distribution/src/main/assembly/src.xml b/Final/java/distribution/src/main/assembly/src.xml
new file mode 100644
index 0000000000..efdc4d98f4
--- /dev/null
+++ b/Final/java/distribution/src/main/assembly/src.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <!-- id typically identifies the "type" (src vs bin etc) of the assembly -->
+ <id>java-src</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <directory>../resources</directory>
+ <outputDirectory>qpid-${qpid.version}-src</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ <include>README</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>src/main/release</directory>
+ <outputDirectory>qpid-${qpid.version}-src</outputDirectory>
+ </fileSet>
+
+ <!-- Include any release information -->
+ <fileSet>
+ <directory>../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}-src</outputDirectory>
+ </fileSet>
+
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>qpid-${qpid.version}-src/java</outputDirectory>
+ <excludes>
+ <exclude>build.xml</exclude>
+ <exclude>distribution/build.xml</exclude>
+ <exclude>benchmark</exclude>
+ <exclude>benchmark/**/*</exclude>
+ <exclude>**/target</exclude>
+ <exclude>**/target/**/*</exclude>
+ <exclude>**/build</exclude>
+ <exclude>**/build/**/*</exclude>
+ <exclude>**/.settings</exclude>
+ <exclude>**/.classpath</exclude>
+ <exclude>**/.project</exclude>
+ <exclude>**/.wtpmodules</exclude>
+ <exclude>**/surefire*</exclude>
+ <exclude>**/cobertura.ser</exclude>
+ <exclude>bin</exclude>
+ <exclude>bin/*</exclude>
+ <exclude>lib</exclude>
+ <exclude>lib/**/*</exclude>
+ <exclude>**/var/journal</exclude>
+ <exclude>**/build.out*</exclude>
+ <exclude>**/eclipse-plugin/bin/**</exclude>
+ <exclude>**/eclipse-plugin/plugins/**</exclude>
+ <exclude>**/eclipse-plugin/src/main/resources/**</exclude>
+ </excludes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../../gentools</directory>
+ <outputDirectory>qpid-${qpid.version}-src/gentools</outputDirectory>
+ <excludes>
+ <exclude>**/build</exclude>
+ <exclude>**/build/**/*</exclude>
+ <exclude>**/*.class</exclude>
+ </excludes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../../specs</directory>
+ <outputDirectory>qpid-${qpid.version}-src/specs</outputDirectory>
+ <includes>
+ <include>amqp.0-8.xml</include>
+ <include>cluster.0-8.xml</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../../python</directory>
+ <outputDirectory>qpid-${qpid.version}-src/python</outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/Final/java/doc/AMQBlazeDetailedDesign.vsd b/Final/java/doc/AMQBlazeDetailedDesign.vsd
new file mode 100644
index 0000000000..90577c69cf
--- /dev/null
+++ b/Final/java/doc/AMQBlazeDetailedDesign.vsd
Binary files differ
diff --git a/Final/java/doc/FramingClassDiagram.vsd b/Final/java/doc/FramingClassDiagram.vsd
new file mode 100644
index 0000000000..8db65255ea
--- /dev/null
+++ b/Final/java/doc/FramingClassDiagram.vsd
Binary files differ
diff --git a/Final/java/etc/coding_standards.xml b/Final/java/etc/coding_standards.xml
new file mode 100644
index 0000000000..8f8b808884
--- /dev/null
+++ b/Final/java/etc/coding_standards.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.1//EN" "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+<module name="Checker">
+
+ <!-- Checks package.html defined for all packages. -->
+ <!-- <module name="PackageHtml"/> -->
+
+ <module name="TreeWalker">
+
+ <!-- Whitespace conventions. -->
+ <module name="TabCharacter"/>
+
+ <!-- License conventions. Checks that the license is included in every file. -->
+ <module name="Header">
+ <property name="headerFile" value="${checkstyle.header.file}"/>
+ </module>
+
+ <!-- Coding style conventions. -->
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.PackageDeclarationCheck">
+ <property name="severity" value="error"/>
+ </module>
+
+ <!-- These rules ensure that everything is javadoc'ed. -->
+ <!--
+ <module name="RequiredRegexp">
+ <property name="format" value="&lt;table id=&quot;crc&quot;&gt;&lt;caption&gt;CRC Card&lt;/caption&gt;"/>
+ </module>
+ -->
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck">
+ <property name="excludeScope" value="nothing"/>
+ <property name="scope" value="private"/>
+ <property name="severity" value="error"/>
+ <property name="tokens" value="CLASS_DEF, INTERFACE_DEF"/>
+ <property name="allowMissingParamTags" value="true"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck">
+ <property name="excludeScope" value="nothing"/>
+ <property name="scope" value="private"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMethodCheck">
+ <property name="allowMissingParamTags" value="false"/>
+ <property name="allowMissingReturnTag" value="false"/>
+ <property name="allowMissingThrowsTags" value="false"/>
+ <property name="allowThrowsTagsForSubclasses" value="false"/>
+ <property name="allowUndeclaredRTE" value="true"/>
+ <property name="allowMissingJavadoc" value="false"/>
+ <property name="allowMissingPropertyJavadoc" value="true"/>
+ <property name="excludeScope" value="nothing"/>
+ <property name="scope" value="private"/>
+ <property name="severity" value="error"/>
+ <property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
+ </module>
+
+ <module name="JavadocStyle">
+ <property name="scope" value="private"/>
+ <property name="checkHtml" value="false"/>
+ <property name="checkFirstSentence" value="true"/>
+ <property name="checkEmptyJavadoc" value="true"/>
+ </module>
+
+ <!-- These rules enforce the conventions for the naming of variables. -->
+ <!--
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck">
+ <property name="format" value="^log$|^[A-Z](_?[A-Z0-9]+)*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.LocalFinalVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck">
+ <property name="applyToPackage" value="true"/>
+ <property name="applyToPrivate" value="true"/>
+ <property name="applyToProtected" value="true"/>
+ <property name="applyToPublic" value="true"/>
+ <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.MethodNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.PackageNameCheck">
+ <property name="format" value="^[a-z]+(\.[a-zA-Z_][a-zA-Z0-9_]*)*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.ParameterNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.StaticVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.TypeNameCheck">
+ <property name="format" value="^[A-Z][a-zA-Z0-9_]*$"/>
+ <property name="severity" value="error"/>
+ <property name="tokens" value="CLASS_DEF, INTERFACE_DEF"/>
+ </module>
+ -->
+ </module>
+</module>
diff --git a/Final/java/etc/license_header.txt b/Final/java/etc/license_header.txt
new file mode 100644
index 0000000000..02ee6e8f98
--- /dev/null
+++ b/Final/java/etc/license_header.txt
@@ -0,0 +1,20 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */ \ No newline at end of file
diff --git a/Final/java/etc/log4j.xml b/Final/java/etc/log4j.xml
new file mode 100644
index 0000000000..266c466a3a
--- /dev/null
+++ b/Final/java/etc/log4j.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Log4j Configuration -->
+<!-- -->
+<!-- ===================================================================== -->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+ <!-- ============================== -->
+ <!-- Append messages to the console -->
+ <!-- ============================== -->
+
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <param name="Target" value="System.out"/>
+ <param name="Threshold" value="ALL"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <!-- The default pattern: Date Priority [Category] Message\n -->
+ <param name="ConversionPattern" value="%m%n"/>
+ </layout>
+
+ </appender>
+
+ <!-- ================ -->
+ <!-- Limit categories -->
+ <!-- ================ -->
+
+ <category name="org.apache.qpid">
+ <priority value="${amqj.logging.level}" logger="CONSOLE"/>
+ </category>
+
+ <!-- ======================= -->
+ <!-- Setup the Root category -->
+ <!-- ======================= -->
+
+ <root level="${root.logging.level}">
+ <appender-ref ref="CONSOLE"/>
+ </root>
+
+</log4j:configuration>
diff --git a/Final/java/integrationtests/README.txt b/Final/java/integrationtests/README.txt
new file mode 100644
index 0000000000..00a21883a9
--- /dev/null
+++ b/Final/java/integrationtests/README.txt
@@ -0,0 +1,13 @@
+This module contains integration tests, for testing a java client againt *any* broker
+implementation or against other clients. These tests must not rely on starting the
+Java broker in-vm but must depend on a broker being started independantly before running
+the tests in this module. By default tests in this module will expect the broker to be
+started on localhost on the default port, but this can be overridden by passing in a
+sys property to maven. Interop tests are in this module. Java broker specific tests that
+use an in-vm broker should go in the systests module.
+
+Don't set the tests in this module to run by default as part of the maven build, until
+there is a script to start and stop the broker; needed to fully automate these tests.
+Interop tests will always be run using a seperate script (not from maven) but it might
+be worthwile to script into the maven build starting of the Java broker, and running
+these tests against it. \ No newline at end of file
diff --git a/Final/java/integrationtests/docs/RunningSustainedTests.txt b/Final/java/integrationtests/docs/RunningSustainedTests.txt
new file mode 100644
index 0000000000..db4405a32d
--- /dev/null
+++ b/Final/java/integrationtests/docs/RunningSustainedTests.txt
@@ -0,0 +1,17 @@
+In addition to the integration tests the framework provided by this package also allows for
+sustained tests to be run. Currently avaible tests:
+- org.apache.qpid.sustained.SustainedClientTestCase : Pub Sub test to determine steady state throughput.
+
+
+Running Tests.
+
+Run the tests as per the integration tests.
+- Start a broker
+- Start at least one test client [java org.apache.qpid.interop.TestClient], ensuring unique naming.
+
+- Start the test coordinator with the 'fanout' engine, on the sustained test case [java org.apache.qpid.test.framework.distributedtesting.Coordinator]
+
+- Additional Test clients can be started and joined into the running test: [java org.apache.qpid.interop.TestClient -j]
+
+
+
diff --git a/Final/java/integrationtests/jar-with-dependencies.xml b/Final/java/integrationtests/jar-with-dependencies.xml
new file mode 100644
index 0000000000..3e95e7ab22
--- /dev/null
+++ b/Final/java/integrationtests/jar-with-dependencies.xml
@@ -0,0 +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.
+-->
+<!-- This is an assembly descriptor that produces a jar file that contains all the
+ dependencies, fully expanded into a single jar, required to run the tests of
+ a maven project.
+ -->
+<assembly>
+ <id>all-test-deps</id>
+ <formats>
+ <format>jar</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory></outputDirectory>
+ <outputFileNameMapping></outputFileNameMapping>
+ <unpack>true</unpack>
+ <scope>test</scope>
+ </dependencySet>
+ </dependencySets>
+ <fileSets>
+ <fileSet>
+ <directory>target/classes</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>target/test-classes</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/Final/java/integrationtests/pom.xml b/Final/java/integrationtests/pom.xml
new file mode 100644
index 0000000000..c13e300386
--- /dev/null
+++ b/Final/java/integrationtests/pom.xml
@@ -0,0 +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</version>
+ <name>Qpid Integration Tests</name>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</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>
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java
new file mode 100644
index 0000000000..45bbfcd148
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java
@@ -0,0 +1,135 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.clienttestcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Session;
+
+/**
+ * Implements tet case 1, dummy run. This test case sends no test messages, it exists to confirm that the test harness
+ * is interacting with the coordinator correctly.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Perform test case actions.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public class TestCase1DummyRun implements TestClientControlledTest
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TestCase1DummyRun.class);
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ log.debug("public String getName(): called");
+
+ return "TC1_DummyRun";
+ }
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ log.debug("public boolean acceptInvite(Message inviteMessage): called");
+
+ // Test parameters don't matter, accept all invites.
+ return true;
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ log.debug("public void assignRole(Roles role, Message assignRoleMessage): called");
+
+ // Do nothing, both roles are the same.
+ }
+
+ /**
+ * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
+ *
+ * @param numMessages The number of test messages to send.
+ */
+ public void start(int numMessages)
+ {
+ log.debug("public void start(): called");
+
+ // Do nothing.
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ log.debug("public Message getReport(Session controlSession): called");
+
+ // Generate a dummy report, the coordinator expects a report but doesn't care what it is.
+ return session.createTextMessage("Dummy Run, Ok.");
+ }
+
+ /**
+ * Handles incoming test messages. Does nothing.
+ *
+ * @param message The incoming test message.
+ */
+ public void onMessage(Message message)
+ {
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ // Ignore any messages.
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java
new file mode 100644
index 0000000000..1d30ff7ca6
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.clienttestcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.TestUtils;
+import org.apache.qpid.test.framework.distributedtesting.TestClient;
+import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
+
+import javax.jms.*;
+
+/**
+ * Implements test case 2, basic P2P. Sends/received a specified number of messages to a specified route on the
+ * default direct exchange. Produces reports on the actual number of messages sent/received.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Send required number of test messages.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public class TestCase2BasicP2P implements TestClientControlledTest, MessageListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TestCase2BasicP2P.class);
+
+ /** Holds the count of test messages received. */
+ private int messageCount;
+
+ /** The role to be played by the test. */
+ private Roles role;
+
+ /** The number of test messages to send. */
+ private int numMessages;
+
+ /** The connection to send the test messages on. */
+ private Connection connection;
+
+ /** The controlSession to send the test messages on. */
+ private Session session;
+
+ /** The producer to send the test messages with. */
+ MessageProducer producer;
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ log.debug("public String getName(): called");
+
+ return "TC2_BasicP2P";
+ }
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
+
+ // All invites are acceptable.
+ return true;
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ *
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
+ + "): called");
+
+ // Reset the message count for a new test.
+ messageCount = 0;
+
+ // Take note of the role to be played.
+ this.role = role;
+
+ // Create a new connection to pass the test messages on.
+ connection = TestUtils.createConnection(TestClient.testContextProperties);
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Extract and retain the test parameters.
+ numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES");
+ Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME"));
+
+ log.debug("numMessages = " + numMessages);
+ log.debug("sendDestination = " + sendDestination);
+ log.debug("role = " + role);
+
+ switch (role)
+ {
+ // Check if the sender role is being assigned, and set up a message producer if so.
+ case SENDER:
+ producer = session.createProducer(sendDestination);
+ break;
+
+ // Otherwise the receivers role is being assigned, so set this up to listen for messages.
+ case RECEIVER:
+ MessageConsumer consumer = session.createConsumer(sendDestination);
+ consumer.setMessageListener(this);
+ break;
+ }
+
+ connection.start();
+ }
+
+ /**
+ * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
+ *
+ * @param numMessages The number of test messages to send.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void start(int numMessages) throws JMSException
+ {
+ log.debug("public void start(): called");
+
+ // Check that the sender role is being performed.
+ if (role.equals(Roles.SENDER))
+ {
+ Message testMessage = session.createTextMessage("test");
+
+ for (int i = 0; i < this.numMessages; i++)
+ {
+ producer.send(testMessage);
+
+ // Increment the message count.
+ messageCount++;
+ }
+ }
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ log.debug("public Message getReport(Session controlSession): called");
+
+ // Close the test connection.
+ connection.close();
+
+ // Generate a report message containing the count of the number of messages passed.
+ Message report = session.createMessage();
+ report.setStringProperty("CONTROL_TYPE", "REPORT");
+ report.setIntProperty("MESSAGE_COUNT", messageCount);
+
+ return report;
+ }
+
+ /**
+ * Counts incoming test messages.
+ *
+ * @param message The incoming test message.
+ */
+ public void onMessage(Message message)
+ {
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ // Increment the message count.
+ messageCount++;
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java
new file mode 100644
index 0000000000..622ddc2f64
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.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.interop.clienttestcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.TestUtils;
+import org.apache.qpid.test.framework.distributedtesting.TestClient;
+import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
+
+import javax.jms.*;
+
+/**
+ * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the
+ * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of
+ * messages sent/received.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Send required number of test messages using pub/sub.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public class TestCase3BasicPubSub implements TestClientControlledTest, MessageListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TestCase3BasicPubSub.class);
+
+ /** Holds the count of test messages received. */
+ private int messageCount;
+
+ /** The role to be played by the test. */
+ private Roles role;
+
+ /** The number of test messages to send. */
+ private int numMessages;
+
+ /** The connections to send/receive the test messages on. */
+ private Connection[] connection;
+
+ /** The sessions to send/receive the test messages on. */
+ private Session[] session;
+
+ /** The producer to send the test messages with. */
+ MessageProducer producer;
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ log.debug("public String getName(): called");
+
+ return "TC3_BasicPubSub";
+ }
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
+
+ // All invites are acceptable.
+ return true;
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ *
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
+ + "): called");
+
+ // Reset the message count for a new test.
+ messageCount = 0;
+
+ // Take note of the role to be played.
+ this.role = role;
+
+ // Extract and retain the test parameters.
+ numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES");
+ int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS");
+ String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY");
+
+ log.debug("numMessages = " + numMessages);
+ log.debug("numReceivers = " + numReceivers);
+ log.debug("sendKey = " + sendKey);
+ log.debug("role = " + role);
+
+ switch (role)
+ {
+ // Check if the sender role is being assigned, and set up a single message producer if so.
+ case SENDER:
+ // Create a new connection to pass the test messages on.
+ connection = new Connection[1];
+ session = new Session[1];
+
+ connection[0] = TestUtils.createConnection(TestClient.testContextProperties);
+ session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Extract and retain the test parameters.
+ Destination sendDestination = session[0].createTopic(sendKey);
+
+ producer = session[0].createProducer(sendDestination);
+ break;
+
+ // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number
+ // of receivers connections.
+ case RECEIVER:
+ // Create the required number of receivers connections.
+ connection = new Connection[numReceivers];
+ session = new Session[numReceivers];
+
+ for (int i = 0; i < numReceivers; i++)
+ {
+ connection[i] = TestUtils.createConnection(TestClient.testContextProperties);
+ session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ sendDestination = session[i].createTopic(sendKey);
+
+ MessageConsumer consumer = session[i].createConsumer(sendDestination);
+ consumer.setMessageListener(this);
+ }
+
+ break;
+ }
+
+ // Start all the connection dispatcher threads running.
+ for (Connection conn : connection)
+ {
+ conn.start();
+ }
+ }
+
+ /**
+ * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
+ *
+ * @param numMessages The number of test messages to send.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void start(int numMessages) throws JMSException
+ {
+ log.debug("public void start(): called");
+
+ // Check that the sender role is being performed.
+ if (role.equals(Roles.SENDER))
+ {
+ Message testMessage = session[0].createTextMessage("test");
+
+ for (int i = 0; i < this.numMessages; i++)
+ {
+ producer.send(testMessage);
+
+ // Increment the message count.
+ messageCount++;
+ }
+ }
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ log.debug("public Message getReport(Session controlSession): called");
+
+ // Close the test connections.
+ for (Connection conn : connection)
+ {
+ conn.close();
+ }
+
+ // Generate a report message containing the count of the number of messages passed.
+ Message report = session.createMessage();
+ report.setStringProperty("CONTROL_TYPE", "REPORT");
+ report.setIntProperty("MESSAGE_COUNT", messageCount);
+
+ return report;
+ }
+
+ /**
+ * Counts incoming test messages.
+ *
+ * @param message The incoming test message.
+ */
+ public void onMessage(Message message)
+ {
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ // Increment the message count.
+ messageCount++;
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java
new file mode 100644
index 0000000000..0388c56678
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java
@@ -0,0 +1,214 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.clienttestcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.TestUtils;
+import org.apache.qpid.test.framework.distributedtesting.TestClient;
+import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
+
+import javax.jms.*;
+
+/**
+ * Implements test case 4, P2P messages with message size. Sends/received a specified number of messages to a specified
+ * route on the default direct exchange, of a specified size. Produces reports on the actual number of messages
+ * sent/received.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Send required number of test messages.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public class TestCase4P2PMessageSize implements TestClientControlledTest, MessageListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TestCase4P2PMessageSize.class);
+
+ /** Holds the count of test messages received. */
+ private int messageCount;
+
+ /** The role to be played by the test. */
+ private Roles role;
+
+ /** The number of test messages to send. */
+ private int numMessages;
+
+ /** The size of the test messages to send. */
+ private int messageSize;
+
+ /** The connection to send the test messages on. */
+ private Connection connection;
+
+ /** The controlSession to send the test messages on. */
+ private Session session;
+
+ /** The producer to send the test messages with. */
+ MessageProducer producer;
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ log.debug("public String getName(): called");
+
+ return "TC4_P2PMessageSize";
+ }
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
+
+ // All invites are acceptable.
+ return true;
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ *
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
+ + "): called");
+
+ // Reset the message count for a new test.
+ messageCount = 0;
+
+ // Take note of the role to be played.
+ this.role = role;
+
+ // Create a new connection to pass the test messages on.
+ connection = TestUtils.createConnection(TestClient.testContextProperties);
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Extract and retain the test parameters.
+ numMessages = assignRoleMessage.getIntProperty("P2P_NUM_MESSAGES");
+ messageSize = assignRoleMessage.getIntProperty("messageSize");
+ Destination sendDestination = session.createQueue(assignRoleMessage.getStringProperty("P2P_QUEUE_AND_KEY_NAME"));
+
+ log.debug("numMessages = " + numMessages);
+ log.debug("sendDestination = " + sendDestination);
+ log.debug("role = " + role);
+
+ switch (role)
+ {
+ // Check if the sender role is being assigned, and set up a message producer if so.
+ case SENDER:
+ producer = session.createProducer(sendDestination);
+ break;
+
+ // Otherwise the receivers role is being assigned, so set this up to listen for messages.
+ case RECEIVER:
+ MessageConsumer consumer = session.createConsumer(sendDestination);
+ consumer.setMessageListener(this);
+ break;
+ }
+
+ connection.start();
+ }
+
+ /**
+ * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
+ *
+ * @param numMessages The number of test messages to send.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void start(int numMessages) throws JMSException
+ {
+ log.debug("public void start(): called");
+
+ // Check that the sender role is being performed.
+ if (role.equals(Roles.SENDER))
+ {
+ Message testMessage = TestUtils.createTestMessageOfSize(session, messageSize);
+
+ for (int i = 0; i < this.numMessages; i++)
+ {
+ producer.send(testMessage);
+
+ // Increment the message count.
+ messageCount++;
+ }
+ }
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ log.debug("public Message getReport(Session controlSession): called");
+
+ // Close the test connection.
+ connection.close();
+
+ // Generate a report message containing the count of the number of messages passed.
+ Message report = session.createMessage();
+ report.setStringProperty("CONTROL_TYPE", "REPORT");
+ report.setIntProperty("MESSAGE_COUNT", messageCount);
+
+ return report;
+ }
+
+ /**
+ * Counts incoming test messages.
+ *
+ * @param message The incoming test message.
+ */
+ public void onMessage(Message message)
+ {
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ // Increment the message count.
+ messageCount++;
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java
new file mode 100644
index 0000000000..ab59e16ab3
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java
@@ -0,0 +1,243 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.clienttestcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.TestUtils;
+import org.apache.qpid.test.framework.distributedtesting.TestClient;
+import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
+
+import javax.jms.*;
+
+/**
+ * Implements test case 5, pub/sub with message size. Sends/received a specified number of messages to a specified route
+ * on the default topic exchange, using the specified number of receivers connections, and the specified message size.
+ * Produces reports on the actual number of messages sent/received.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Send required number of test messages using pub/sub.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public class TestCase5PubSubMessageSize implements TestClientControlledTest, MessageListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TestCase5PubSubMessageSize.class);
+
+ /** Holds the count of test messages received. */
+ private int messageCount;
+
+ /** The role to be played by the test. */
+ private Roles role;
+
+ /** The number of test messages to send. */
+ private int numMessages;
+
+ /** The size of the test messages to send. */
+ private int messageSize;
+
+ /** The connections to send/receive the test messages on. */
+ private Connection[] connection;
+
+ /** The sessions to send/receive the test messages on. */
+ private Session[] session;
+
+ /** The producer to send the test messages with. */
+ MessageProducer producer;
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ log.debug("public String getName(): called");
+
+ return "TC5_PubSubMessageSize";
+ }
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ log.debug("public boolean acceptInvite(Message inviteMessage = " + inviteMessage + "): called");
+
+ // All invites are acceptable.
+ return true;
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ *
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws javax.jms.JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
+ + "): called");
+
+ // Reset the message count for a new test.
+ messageCount = 0;
+
+ // Take note of the role to be played.
+ this.role = role;
+
+ // Extract and retain the test parameters.
+ numMessages = assignRoleMessage.getIntProperty("PUBSUB_NUM_MESSAGES");
+ messageSize = assignRoleMessage.getIntProperty("messageSize");
+ int numReceivers = assignRoleMessage.getIntProperty("PUBSUB_NUM_RECEIVERS");
+ String sendKey = assignRoleMessage.getStringProperty("PUBSUB_KEY");
+
+ log.debug("numMessages = " + numMessages);
+ log.debug("numReceivers = " + numReceivers);
+ log.debug("sendKey = " + sendKey);
+ log.debug("role = " + role);
+
+ switch (role)
+ {
+ // Check if the sender role is being assigned, and set up a single message producer if so.
+ case SENDER:
+ // Create a new connection to pass the test messages on.
+ connection = new Connection[1];
+ session = new Session[1];
+
+ connection[0] = TestUtils.createConnection(TestClient.testContextProperties);
+ session[0] = connection[0].createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Extract and retain the test parameters.
+ Destination sendDestination = session[0].createTopic(sendKey);
+
+ producer = session[0].createProducer(sendDestination);
+ break;
+
+ // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number
+ // of receivers connections.
+ case RECEIVER:
+ // Create the required number of receivers connections.
+ connection = new Connection[numReceivers];
+ session = new Session[numReceivers];
+
+ for (int i = 0; i < numReceivers; i++)
+ {
+ connection[i] = TestUtils.createConnection(TestClient.testContextProperties);
+ session[i] = connection[i].createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ sendDestination = session[i].createTopic(sendKey);
+
+ MessageConsumer consumer = session[i].createConsumer(sendDestination);
+ consumer.setMessageListener(this);
+ }
+
+ break;
+ }
+
+ // Start all the connection dispatcher threads running.
+ for (Connection conn : connection)
+ {
+ conn.start();
+ }
+ }
+
+ /**
+ * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
+ *
+ * @param numMessages The number of test messages to send.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void start(int numMessages) throws JMSException
+ {
+ log.debug("public void start(): called");
+
+ // Check that the sender role is being performed.
+ if (role.equals(Roles.SENDER))
+ {
+ Message testMessage = TestUtils.createTestMessageOfSize(session[0], messageSize);
+
+ for (int i = 0; i < this.numMessages; i++)
+ {
+ producer.send(testMessage);
+
+ // Increment the message count.
+ messageCount++;
+ }
+ }
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws javax.jms.JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ log.debug("public Message getReport(Session controlSession): called");
+
+ // Close the test connections.
+ for (Connection conn : connection)
+ {
+ conn.close();
+ }
+
+ // Generate a report message containing the count of the number of messages passed.
+ Message report = session.createMessage();
+ report.setStringProperty("CONTROL_TYPE", "REPORT");
+ report.setIntProperty("MESSAGE_COUNT", messageCount);
+
+ return report;
+ }
+
+ /**
+ * Counts incoming test messages.
+ *
+ * @param message The incoming test message.
+ */
+ public void onMessage(Message message)
+ {
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ // Increment the message count.
+ messageCount++;
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java
new file mode 100644
index 0000000000..60cd9f47f3
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.testcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+
+import java.util.Properties;
+
+/**
+ * Coordinates test case 1, from the interop test specification. This test connects up the sender and receivers roles,
+ * and gets some dummy test reports from them, in order to check that the test framework itself is operational.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Exercises the interop testing framework without actually sending any test messages.
+ * <td> {@link FrameworkBaseCase}
+ * </table>
+ */
+public class InteropTestCase1DummyRun extends FrameworkBaseCase
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(InteropTestCase1DummyRun.class);
+
+ /**
+ * Creates a new coordinating test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public InteropTestCase1DummyRun(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Performs the basic P2P test case, "Test Case 2" in the specification.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testDummyRun() throws Exception
+ {
+ log.debug("public void testDummyRun(): called");
+
+ Properties testConfig = new Properties();
+ testConfig.put("TEST_NAME", "TC1_DummyRun");
+
+ /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig);
+
+ // Compare sender and receivers reports.
+ // Assert.assertEquals("Expected to get 2 dummy reports.", 2, reports.length);
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as defined in the
+ * interop testing specification. For example the method "testP2P" might map onto the interop test case name
+ * "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return "TC1_DummyRun";
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java
new file mode 100644
index 0000000000..8983249daa
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.testcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+
+import java.util.Properties;
+
+/**
+ * Implements test case 2, from the interop test specification. This test sets up the TC2_BasicP2P test for 50
+ * messages. It checks that the sender and receivers reports both indicate that all the test messages were transmitted
+ * successfully.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Setup p2p test parameters and compare with test output. <td> {@link FrameworkBaseCase}
+ * </table>
+ */
+public class InteropTestCase2BasicP2P extends FrameworkBaseCase
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(InteropTestCase2BasicP2P.class);
+
+ /**
+ * Creates a new coordinating test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public InteropTestCase2BasicP2P(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Performs the basic P2P test case, "Test Case 2" in the specification.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testBasicP2P() throws Exception
+ {
+ log.debug("public void testBasicP2P(): called");
+
+ Properties testConfig = new Properties();
+ testConfig.setProperty("TEST_NAME", "TC2_BasicP2P");
+ testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue");
+ testConfig.put("P2P_NUM_MESSAGES", 50);
+
+ /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig);
+
+ // Compare sender and receivers reports.
+ /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
+ int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
+
+ Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent);
+ Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);*/
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as defined in the
+ * interop testing specification. For example the method "testP2P" might map onto the interop test case name
+ * "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return "TC2_BasicP2P";
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java
new file mode 100644
index 0000000000..6e87c3e3ee
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java
@@ -0,0 +1,88 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.testcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+
+import java.util.Properties;
+
+/**
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Setup pub/sub test parameters and compare with test output. <td> {@link FrameworkBaseCase}
+ * </table>
+ */
+public class InteropTestCase3BasicPubSub extends FrameworkBaseCase
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(InteropTestCase3BasicPubSub.class);
+
+ /**
+ * Creates a new coordinating test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public InteropTestCase3BasicPubSub(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Performs the basic P2P test case, "Test Case 2" in the specification.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testBasicPubSub() throws Exception
+ {
+ log.debug("public void testBasicPubSub(): called");
+
+ Properties testConfig = new Properties();
+ testConfig.put("TEST_NAME", "TC3_BasicPubSub");
+ testConfig.put("PUBSUB_KEY", "tc3route");
+ testConfig.put("PUBSUB_NUM_MESSAGES", 10);
+ testConfig.put("PUBSUB_NUM_RECEIVERS", 5);
+
+ /*Message[] reports =*/ getCircuitFactory().sequenceTest(null, null, testConfig);
+
+ // Compare sender and receivers reports.
+ /*int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
+ int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
+
+ Assert.assertEquals("The requested number of messages were not sent.", 10, messagesSent);
+ Assert.assertEquals("Received messages did not match up to num sent * num receivers.", messagesSent * 5,
+ messagesReceived);*/
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as defined in the
+ * interop testing specification. For example the method "testP2P" might map onto the interop test case name
+ * "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return "TC3_BasicPubSub";
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java
new file mode 100644
index 0000000000..c3f450ec42
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java
@@ -0,0 +1,193 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.testcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+
+import java.util.Properties;
+
+/**
+ * Implements test case 4, from the interop test specification. This test sets up the TC2_P2PMessageSize test for 50
+ * messages, and a variety of message sizes. It checks that the sender and receivers reports both indicate that all
+ * the test messages were transmitted successfully.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Setup p2p test parameters and compare with test output. <td> {@link FrameworkBaseCase}
+ * </table>
+ */
+public class InteropTestCase4P2PMessageSize extends FrameworkBaseCase
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(InteropTestCase4P2PMessageSize.class);
+
+ /**
+ * Creates a new coordinating test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public InteropTestCase4P2PMessageSize(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize0K() throws Exception
+ {
+ runTestForMessagesOfSize(0);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize63K() throws Exception
+ {
+ runTestForMessagesOfSize(63 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize64K() throws Exception
+ {
+ runTestForMessagesOfSize(64 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize65K() throws Exception
+ {
+ runTestForMessagesOfSize(65 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize127K() throws Exception
+ {
+ runTestForMessagesOfSize(127 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize128K() throws Exception
+ {
+ runTestForMessagesOfSize(128 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize129K() throws Exception
+ {
+ runTestForMessagesOfSize(129 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize255K() throws Exception
+ {
+ runTestForMessagesOfSize(255 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize256K() throws Exception
+ {
+ runTestForMessagesOfSize(256 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testP2PMessageSize257K() throws Exception
+ {
+ runTestForMessagesOfSize(257 * 1024);
+ }
+
+ /**
+ * Sends 50 test messages of the specified size, and asserts that all were received.
+ *
+ * @param size The size of the messages to send in bytes.
+ */
+ private void runTestForMessagesOfSize(int size)
+ {
+ Properties testConfig = new Properties();
+ testConfig.setProperty("TEST_NAME", "TC4_P2PMessageSize");
+ testConfig.setProperty("P2P_QUEUE_AND_KEY_NAME", "tc2queue");
+ testConfig.put("P2P_NUM_MESSAGES", 50);
+ testConfig.put("messageSize", size);
+
+ /*Message[] reports =*/
+ getCircuitFactory().sequenceTest(null, null, testConfig);
+
+ // Compare sender and receivers reports.
+ /*
+ int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
+ int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
+
+ Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent);
+ Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);
+ */
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as defined in the
+ * interop testing specification. For example the method "testP2P" might map onto the interop test case name
+ * "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ *
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return "TC4_P2PMessageSize";
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java
new file mode 100644
index 0000000000..86a0a60ea4
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java
@@ -0,0 +1,193 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.interop.testcases;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+
+import java.util.Properties;
+
+/**
+ * Implements test case 5, from the interop test specification. This test sets up the TC2_PubSubMessageSize test for 10
+ * messages, sent to 5 consumers, and a variety of message sizes. It checks that the sender and receivers reports both
+ * indicate that all the test messages were transmitted successfully.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Setup pub/sub test parameters and compare with test output. <td> {@link FrameworkBaseCase}
+ * </table>
+ */
+public class InteropTestCase5PubSubMessageSize extends FrameworkBaseCase
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(InteropTestCase5PubSubMessageSize.class);
+
+ /**
+ * Creates a new coordinating test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public InteropTestCase5PubSubMessageSize(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 0K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize0K() throws Exception
+ {
+ runTestForMessagesOfSize(0);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 63K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize63K() throws Exception
+ {
+ runTestForMessagesOfSize(63 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 64K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize64K() throws Exception
+ {
+ runTestForMessagesOfSize(64 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 65K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize65K() throws Exception
+ {
+ runTestForMessagesOfSize(65 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 127K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize127K() throws Exception
+ {
+ runTestForMessagesOfSize(127 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 128K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize128K() throws Exception
+ {
+ runTestForMessagesOfSize(128 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 129K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize129K() throws Exception
+ {
+ runTestForMessagesOfSize(129 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 255K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize255K() throws Exception
+ {
+ runTestForMessagesOfSize(255 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 256K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize256K() throws Exception
+ {
+ runTestForMessagesOfSize(256 * 1024);
+ }
+
+ /**
+ * Performs the P2P message test case, "Test Case 4" in the specification, for messages 257K in size.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testPubSubMessageSize257K() throws Exception
+ {
+ runTestForMessagesOfSize(257 * 1024);
+ }
+
+ /**
+ * Sends 50 test messages of the specified size, and asserts that all were received.
+ *
+ * @param size The size of the messages to send in bytes.
+ */
+ private void runTestForMessagesOfSize(int size)
+ {
+ Properties testConfig = new Properties();
+ testConfig.put("TEST_NAME", "TC5_PubSubMessageSize");
+ testConfig.put("PUBSUB_KEY", "tc3route");
+ testConfig.put("PUBSUB_NUM_MESSAGES", 10);
+ testConfig.put("PUBSUB_NUM_RECEIVERS", 5);
+ testConfig.put("messageSize", size);
+
+ /*Message[] reports =*/
+ getCircuitFactory().sequenceTest(null, null, testConfig);
+
+ // Compare sender and receivers reports.
+ /*
+ int messagesSent = reports[0].getIntProperty("MESSAGE_COUNT");
+ int messagesReceived = reports[1].getIntProperty("MESSAGE_COUNT");
+
+ Assert.assertEquals("The requested number of messages were not sent.", 50, messagesSent);
+ Assert.assertEquals("Sender and receivers messages sent did not match up.", messagesSent, messagesReceived);
+ */
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as defined in the
+ * interop testing specification. For example the method "testP2P" might map onto the interop test case name
+ * "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return "TC5_PubSubMessageSize";
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java
new file mode 100644
index 0000000000..2764f2c3aa
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java
@@ -0,0 +1,906 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.sustained;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQNoConsumersException;
+import org.apache.qpid.client.AMQNoRouteException;
+import org.apache.qpid.test.framework.distributedtesting.TestClient;
+import org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub;
+import org.apache.qpid.test.framework.TestUtils;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Implements test case 3, basic pub/sub. Sends/received a specified number of messages to a specified route on the
+ * default topic exchange, using the specified number of receivers connections. Produces reports on the actual number of
+ * messages sent/received.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Send required number of test messages using pub/sub. <tr><td> Generate test reports.
+ * </table>
+ */
+public class SustainedClientTestCase extends TestCase3BasicPubSub implements ExceptionListener, MessageListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(SustainedClientTestCase.class);
+
+ /** Used to log to the console. */
+ private static final Logger console = Logger.getLogger("SustainedTest");
+
+ /** The role to be played by the test. */
+ private Roles role;
+
+ /** The number of receivers connection to use. */
+ private int numReceivers;
+
+ /** The routing key to send them to on the default direct exchange. */
+ private Destination sendDestination;
+
+ /** The routing key to send updates to on the default direct exchange. */
+ private Destination sendUpdateDestination;
+
+ /** The connections to send/receive the test messages on. */
+ private Connection[] connection;
+
+ /** The sessions to send/receive the test messages on. */
+ private Session[] session;
+
+ /** The producer to send the test messages with. */
+ MessageProducer producer;
+
+ /** Adapter that adjusts the send rate based on the updates from clients. */
+ SustainedRateAdapter _rateAdapter;
+
+ /** */
+ int _batchSize;
+
+ private static final long TEN_MILLI_SEC = 10000000;
+ private static final int DEBUG_LOG_UPATE_INTERVAL = 10;
+ private static final int LOG_UPATE_INTERVAL = 10;
+ private static final boolean SLEEP_PER_MESSAGE = Boolean.getBoolean("sleepPerMessage");
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the interop
+ * testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ log.debug("public String getName(): called");
+
+ return "Perf_SustainedPubSub";
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the assignment
+ * message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ log.debug("public void assignRole(Roles role = " + role + ", Message assignRoleMessage = " + assignRoleMessage
+ + "): called");
+
+ // Take note of the role to be played.
+ this.role = role;
+
+ // Extract and retain the test parameters.
+ numReceivers = assignRoleMessage.getIntProperty("SUSTAINED_NUM_RECEIVERS");
+ _batchSize = assignRoleMessage.getIntProperty("SUSTAINED_UPDATE_INTERVAL");
+ String sendKey = assignRoleMessage.getStringProperty("SUSTAINED_KEY");
+ String sendUpdateKey = assignRoleMessage.getStringProperty("SUSTAINED_UPDATE_KEY");
+ int ackMode = assignRoleMessage.getIntProperty("ACKNOWLEDGE_MODE");
+ String clientName = assignRoleMessage.getStringProperty("CLIENT_NAME");
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("numReceivers = " + numReceivers);
+ log.debug("_batchSize = " + _batchSize);
+ log.debug("ackMode = " + ackMode);
+ log.debug("sendKey = " + sendKey);
+ log.debug("sendUpdateKey = " + sendUpdateKey);
+ log.debug("role = " + role);
+ }
+
+ switch (role)
+ {
+ // Check if the sender role is being assigned, and set up a single message producer if so.
+ case SENDER:
+ console.info("Creating Sender");
+ // Create a new connection to pass the test messages on.
+ connection = new Connection[1];
+ session = new Session[1];
+
+ connection[0] = TestUtils.createConnection(TestClient.testContextProperties);
+ session[0] = connection[0].createSession(false, ackMode);
+
+ // Extract and retain the test parameters.
+ sendDestination = session[0].createTopic(sendKey);
+
+ connection[0].setExceptionListener(this);
+
+ producer = session[0].createProducer(sendDestination);
+
+ sendUpdateDestination = session[0].createTopic(sendUpdateKey);
+ MessageConsumer updateConsumer = session[0].createConsumer(sendUpdateDestination);
+
+ _rateAdapter = new SustainedRateAdapter(this);
+ updateConsumer.setMessageListener(_rateAdapter);
+
+ break;
+
+ // Otherwise the receivers role is being assigned, so set this up to listen for messages on the required number
+ // of receivers connections.
+ case RECEIVER:
+ console.info("Creating Receiver");
+ // Create the required number of receivers connections.
+ connection = new Connection[numReceivers];
+ session = new Session[numReceivers];
+
+ for (int i = 0; i < numReceivers; i++)
+ {
+ connection[i] = TestUtils.createConnection(TestClient.testContextProperties);
+ session[i] = connection[i].createSession(false, ackMode);
+
+ sendDestination = session[i].createTopic(sendKey);
+
+ sendUpdateDestination = session[i].createTopic(sendUpdateKey);
+
+ MessageConsumer consumer = session[i].createConsumer(sendDestination);
+
+ consumer.setMessageListener(new SustainedListener(clientName + "-" + i, _batchSize, session[i],
+ sendUpdateDestination));
+ }
+
+ break;
+ }
+
+ // Start all the connection dispatcher threads running.
+ for (int i = 0; i < connection.length; i++)
+ {
+ connection[i].start();
+ }
+ }
+
+ /** Performs the test case actions.
+ * @param numMessages*/
+ public void start(int numMessages) throws JMSException
+ {
+ log.debug("public void start(): called");
+
+ // Check that the sender role is being performed.
+ switch (role)
+ {
+ // Check if the sender role is being assigned, and set up a single message producer if so.
+ case SENDER:
+ _rateAdapter.run();
+ break;
+ case RECEIVER:
+
+ }
+
+ // return from here when you have finished the test.. this will signal the controller and
+ }
+
+ public void terminate() throws JMSException, InterruptedException
+ {
+ if (_rateAdapter != null)
+ {
+ _rateAdapter.stop();
+ }
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ log.debug("public Message getReport(Session controlSession): called");
+
+ // Close the test connections.
+ for (int i = 0; i < connection.length; i++)
+ {
+ connection[i].close();
+ }
+
+ Message report = session.createMessage();
+ report.setStringProperty("CONTROL_TYPE", "REPORT");
+
+ return report;
+ }
+
+ public void onException(JMSException jmsException)
+ {
+ Exception linked = jmsException.getLinkedException();
+
+ if (linked != null)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Linked Exception:" + linked);
+ }
+
+ if ((linked instanceof AMQNoRouteException) || (linked instanceof AMQNoConsumersException))
+ {
+ if (log.isDebugEnabled())
+ {
+ if (linked instanceof AMQNoConsumersException)
+ {
+ log.warn("No clients currently available for message:"
+ + ((AMQNoConsumersException) linked).getUndeliveredMessage());
+ }
+ else
+ {
+ log.warn("No route for message");
+ }
+ }
+
+ // Tell the rate adapter that there are no clients ready yet
+ _rateAdapter.NO_CLIENTS = true;
+ }
+ }
+ else
+ {
+ log.warn("Exception:" + linked);
+ }
+ }
+
+ /**
+ * Inner class that listens for messages and sends a report for the time taken between receiving the 'start' and
+ * 'end' messages.
+ */
+ class SustainedListener implements MessageListener
+ {
+ /** Number of messages received */
+ private long _received = 0;
+ /** The number of messages in the batch */
+ private int _batchSize = 0;
+ /** Record of the when the 'start' messagse was sen */
+ private Long _startTime;
+ /** Message producer to use to send reports */
+ MessageProducer _updater;
+ /** Session to create the report message on */
+ Session _session;
+ /** Record of the client ID used for this SustainedListnener */
+ String _client;
+
+ /**
+ * Main Constructor
+ *
+ * @param clientname The _client id used to identify this connection.
+ * @param batchSize The number of messages that are to be sent per batch. Note: This is not used to
+ * control the interval between sending reports.
+ * @param session The controlSession used for communication.
+ * @param sendDestination The destination that update reports should be sent to.
+ *
+ * @throws JMSException My occur if creatingthe Producer fails
+ */
+ public SustainedListener(String clientname, int batchSize, Session session, Destination sendDestination)
+ throws JMSException
+ {
+ _batchSize = batchSize;
+ _client = clientname;
+ _session = session;
+ _updater = session.createProducer(sendDestination);
+ }
+
+ public void onMessage(Message message)
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("Message " + _received + "received in listener");
+ }
+
+ if (message instanceof TextMessage)
+ {
+ try
+ {
+ _received++;
+ if (((TextMessage) message).getText().equals("start"))
+ {
+ log.debug("Starting Batch");
+ _startTime = System.nanoTime();
+ }
+ else if (((TextMessage) message).getText().equals("end"))
+ {
+ if (_startTime != null)
+ {
+ long currentTime = System.nanoTime();
+ sendStatus(currentTime - _startTime, _received, message.getIntProperty("BATCH"));
+ log.debug("End Batch");
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ // ignore error
+ }
+ }
+
+ }
+
+ /**
+ * sendStatus creates and sends the report back to the publisher
+ *
+ * @param time taken for the the last batch
+ * @param received Total number of messages received.
+ * @param batchNumber the batch number
+ * @throws JMSException if an error occurs during the send
+ */
+ private void sendStatus(long time, long received, int batchNumber) throws JMSException
+ {
+ Message updateMessage = _session.createTextMessage("update");
+ updateMessage.setStringProperty("CLIENT_ID", ":" + _client);
+ updateMessage.setStringProperty("CONTROL_TYPE", "UPDATE");
+ updateMessage.setLongProperty("RECEIVED", received);
+ updateMessage.setIntProperty("BATCH", batchNumber);
+ updateMessage.setLongProperty("DURATION", time);
+
+ if (log.isInfoEnabled())
+ {
+ log.info("**** SENDING [" + batchNumber + "]**** " + "CLIENT_ID:" + _client + " RECEIVED:" + received
+ + " BATCH:" + batchNumber + " DURATION:" + time);
+ }
+
+ // Output on the main console.info the details of this batch
+ if ((batchNumber % 10) == 0)
+ {
+ console.info("Sending Report [" + batchNumber + "] " + "CLIENT_ID:" + _client + " RECEIVED:" + received
+ + " BATCH:" + batchNumber + " DURATION:" + time);
+ }
+
+ _updater.send(updateMessage);
+ }
+ }
+
+ /**
+ * This class is used here to adjust the _delay value which in turn is used to control the number of messages/second
+ * that are sent through the test system.
+ *
+ * By keeping a record of the messages recevied and the average time taken to process the batch size can be
+ * calculated and so the delay can be adjusted to maintain that rate.
+ *
+ * Given that delays of < 10ms can be rounded up the delay is only used between messages if the _delay > 10ms * no
+ * messages in the batch. Otherwise the delay is used at the end of the batch.
+ */
+ class SustainedRateAdapter implements MessageListener, Runnable
+ {
+ private SustainedClientTestCase _client;
+ private long _batchVariance = Integer.getInteger("batchVariance", 3); // no. batches to allow drifting
+ private long _timeVariance = TEN_MILLI_SEC * 5; // no. nanos between send and report delay (10ms)
+ private volatile long _delay; // in nanos
+ private long _sent;
+ private Map<String, Long> _slowClients = new HashMap<String, Long>();
+ private static final long PAUSE_SLEEP = TEN_MILLI_SEC / 1000; // 10 ms
+ private static final long NO_CLIENT_SLEEP = 1000; // 1s
+ private volatile boolean NO_CLIENTS = true;
+ private int _delayShifting;
+ private final int REPORTS_WITHOUT_CHANGE = Integer.getInteger("stableReportCount", 5);
+ private boolean _warmedup = false;
+ private static final long EXPECTED_TIME_PER_BATCH = 100000L;
+ private int _warmUpBatches = Integer.getInteger("warmUpBatches", 10);
+
+ SustainedRateAdapter(SustainedClientTestCase client)
+ {
+ _client = client;
+ }
+
+ public void onMessage(Message message)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("SustainedRateAdapter onMessage(Message message = " + message + "): called");
+ }
+
+ try
+ {
+ String controlType = message.getStringProperty("CONTROL_TYPE");
+
+ // Check if the message is a test invite.
+ if ("UPDATE".equals(controlType))
+ {
+ NO_CLIENTS = false;
+ long duration = message.getLongProperty("DURATION");
+ long totalReceived = message.getLongProperty("RECEIVED");
+ String client = message.getStringProperty("CLIENT_ID");
+ int batchNumber = message.getIntProperty("BATCH");
+
+ if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0))
+ {
+ log.info("Update Report: CLIENT_ID:" + client + " RECEIVED:" + totalReceived + " Recevied BATCH:"
+ + batchNumber + " DURATION:" + duration);
+ }
+
+ recordSlow(client, totalReceived, batchNumber);
+
+ adjustDelay(client, batchNumber, duration);
+
+ // Warm up completes when:
+ // we haven't warmed up
+ // and the number of batches sent to each client is at least half of the required warmup batches
+ if (!_warmedup && (batchNumber >= _warmUpBatches))
+ {
+ _warmedup = true;
+ _warmup.countDown();
+
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ //
+ }
+ }
+
+ CountDownLatch _warmup = new CountDownLatch(1);
+
+ int _numBatches = 10000;
+
+ // long[] _timings = new long[_numBatches];
+ private boolean _running = true;
+
+ public void run()
+ {
+ console.info("Warming up");
+
+ doBatch(_warmUpBatches);
+
+ try
+ {
+ // wait for warmup to complete.
+ _warmup.await();
+
+ // set delay to the average length of the batches
+ _delay = _totalDuration / _warmUpBatches / delays.size();
+
+ console.info("Warmup complete delay set : " + _delay + " based on _totalDuration: " + _totalDuration
+ + " over no. batches: " + _warmUpBatches + " with client count: " + delays.size());
+
+ _totalDuration = 0L;
+ _totalReceived = 0L;
+ _sent = 0L;
+ }
+ catch (InterruptedException e)
+ {
+ //
+ }
+
+ doBatch(_numBatches);
+
+ }
+
+ private void doBatch(int batchSize) // long[] timings,
+ {
+ TextMessage testMessage = null;
+ try
+ {
+ testMessage = _client.session[0].createTextMessage("start");
+
+ for (int batch = 0; batch <= batchSize; batch++)
+ // while (_running)
+ {
+ long start = System.nanoTime();
+
+ testMessage.setText("start");
+ testMessage.setIntProperty("BATCH", batch);
+
+ _client.producer.send(testMessage);
+ _rateAdapter.sentMessage();
+
+ testMessage.setText("test");
+ // start at 2 so start and end count as part of batch
+ for (int m = 2; m < _batchSize; m++)
+ {
+ _client.producer.send(testMessage);
+ _rateAdapter.sentMessage();
+ }
+
+ testMessage.setText("end");
+ _client.producer.send(testMessage);
+ _rateAdapter.sentMessage();
+
+ long end = System.nanoTime();
+
+ long sendtime = end - start;
+
+ if (log.isDebugEnabled())
+ {
+ log.info("Sent batch[" + batch + "](" + _batchSize + ") in " + sendtime); // timings[batch]);
+ }
+
+ if ((batch % LOG_UPATE_INTERVAL) == 0)
+ {
+ console.info("Sent Batch[" + batch + "](" + _batchSize + ")" + status());
+ }
+
+ _rateAdapter.sleepBatch();
+
+ }
+ }
+ catch (JMSException e)
+ {
+ console.error("Runner ended");
+ }
+ }
+
+ private String status()
+ {
+ return " TotalDuration: " + _totalDuration + " for " + delays.size() + " consumers" + " Delay is " + _delay
+ + " resulting in "
+ + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch"));
+ }
+
+ private void sleepBatch()
+ {
+ if (checkForSlowClients())
+ { // if there werwe slow clients we have already slept so don't sleep anymore again.
+ return;
+ }
+
+ if (!SLEEP_PER_MESSAGE)
+ {
+ // per batch sleep.. if sleep is to small to spread over the batch.
+ if (_delay <= (TEN_MILLI_SEC * _batchSize))
+ {
+ sleepLong(_delay);
+ }
+ else
+ {
+ log.info("Not sleeping _delay > ten*batch is:" + _delay);
+ }
+ }
+ }
+
+ public void stop()
+ {
+ _running = false;
+ }
+
+ Map<String, Long> delays = new HashMap<String, Long>();
+ Long _totalReceived = 0L;
+ Long _totalDuration = 0L;
+ int _skipUpdate = 0;
+
+ /**
+ * Adjust the delay for sending messages based on this update from the client
+ *
+ * @param client The client that send this update
+ * @param duration The time taken for the last batch of messagse
+ * @param batchNumber The reported batchnumber from the client
+ */
+ private void adjustDelay(String client, int batchNumber, long duration)
+ {
+ // Retrieve the current total time taken for this client.
+ Long currentTime = delays.get(client);
+
+ // Add the new duration time to this client
+ if (currentTime == null)
+ {
+ currentTime = duration;
+ }
+ else
+ {
+ currentTime += duration;
+ }
+
+ delays.put(client, currentTime);
+
+ long batchesSent = _sent / _batchSize;
+
+ // ensure we don't divide by zero
+ if (batchesSent == 0)
+ {
+ batchesSent = 1L;
+ }
+
+ _totalReceived += _batchSize;
+ _totalDuration += duration;
+
+ // calculate average duration accross clients per batch
+ long averageDuration = _totalDuration / delays.size() / batchesSent;
+
+ // calculate the difference between current send delay and average report delay
+ long diff = (duration) - averageDuration;
+
+ if (log.isInfoEnabled() && ((batchNumber % DEBUG_LOG_UPATE_INTERVAL) == 0))
+ {
+ log.info("TotalDuration:" + _totalDuration + " for " + delays.size() + " consumers." + " on batch: "
+ + batchesSent + " received batch: " + batchNumber + " Batch Duration: " + duration + " Average: "
+ + averageDuration + " so diff: " + diff + " for : " + client + " Delay is " + _delay + " resulting in "
+ + ((_delay > (TEN_MILLI_SEC * _batchSize)) ? ((_delay / _batchSize) + "/msg") : (_delay + "/batch")));
+ }
+
+ // if the averageDuration differs from the current by more than the specified variane then adjust delay.
+ if (Math.abs(diff) > _timeVariance)
+ {
+
+ // if the the _delay is larger than the required duration to send report
+ // speed up
+ if (diff > TEN_MILLI_SEC)
+ {
+ _delay -= TEN_MILLI_SEC;
+
+ if (_delay < 0)
+ {
+ _delay = 0;
+ log.info("Reset _delay to 0");
+ delayStable();
+ }
+ else
+ {
+ delayChanged();
+ }
+
+ }
+ else if (diff < 0) // diff < 0 diff cannot be 0 as it is > _timeVariance
+ {
+ // the report took longer
+ _delay += TEN_MILLI_SEC;
+ delayChanged();
+ }
+ }
+ else
+ {
+ delayStable();
+ }
+
+ // If we have a consumer that is behind with the batches.
+ if ((batchesSent - batchNumber) > _batchVariance)
+ {
+ log.debug("Increasing _delay as sending more than receiving");
+
+ _delay += 2 * TEN_MILLI_SEC;
+ delayChanged();
+ }
+
+ }
+
+ /** Reset the number of iterations before we say the delay has stabilised. */
+ private void delayChanged()
+ {
+ _delayShifting = REPORTS_WITHOUT_CHANGE;
+ }
+
+ /**
+ * Record the fact that delay has stabilised If delay has stablised for REPORTS_WITHOUT_CHANGE then it will
+ * output Delay stabilised
+ */
+ private void delayStable()
+ {
+ _delayShifting--;
+
+ if (_delayShifting < 0)
+ {
+ _delayShifting = 0;
+ console.debug("Delay stabilised:" + _delay);
+ }
+ }
+
+ /**
+ * Checks that the client has received enough messages. If the client has fallen behind then they are put in the
+ * _slowClients lists which will increase the delay.
+ *
+ * @param client The client identifier to check
+ * @param received the number of messages received by that client
+ * @param batchNumber
+ */
+ private void recordSlow(String client, long received, int batchNumber)
+ {
+ if (Math.abs(batchNumber - (_sent / _batchSize)) > _batchVariance)
+ {
+ _slowClients.put(client, received);
+ }
+ else
+ {
+ _slowClients.remove(client);
+ }
+ }
+
+ /** Incrment the number of sent messages and then sleep, if required. */
+ public void sentMessage()
+ {
+
+ _sent++;
+
+ if (_delay > (TEN_MILLI_SEC * _batchSize))
+ {
+ long batchDelay = _delay / _batchSize;
+ // less than 10ms sleep doesn't always work.
+ // _delay is in nano seconds
+ // if (batchDelay < (TEN_MILLI_SEC))
+ // {
+ // sleep(0, (int) batchDelay);
+ // }
+ // else
+ {
+ // if (batchDelay < 30000000000L)
+ {
+ sleepLong(batchDelay);
+ }
+ }
+ }
+ else
+ {
+ if (SLEEP_PER_MESSAGE && (_delay > 0))
+ {
+ sleepLong(_delay / _batchSize);
+ }
+ }
+ }
+
+ /**
+ * Check at the end of each batch and pause sending messages to allow slow clients to catch up.
+ *
+ * @return true if there were slow clients that caught up.
+ */
+ private boolean checkForSlowClients()
+ {
+ // This will allways be true as we are running this at the end of each batchSize
+ // if (_sent % _batchSize == 0)
+ {
+ // Cause test to pause when we have slow
+ if (!_slowClients.isEmpty() || NO_CLIENTS)
+ {
+
+ while (!_slowClients.isEmpty())
+ {
+ if (log.isInfoEnabled() && ((_sent / _batchSize % DEBUG_LOG_UPATE_INTERVAL) == 0))
+ {
+ String clients = "";
+ Iterator it = _slowClients.keySet().iterator();
+ while (it.hasNext())
+ {
+ clients += it.next();
+ if (it.hasNext())
+ {
+ clients += ", ";
+ }
+ }
+
+ log.info("Pausing for slow clients:" + clients);
+ }
+
+ if (console.isDebugEnabled() && ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0))
+ {
+ console.debug(_slowClients.size() + " slow clients.");
+ }
+
+ sleep(PAUSE_SLEEP);
+ }
+
+ if (NO_CLIENTS)
+ {
+ sleep(NO_CLIENT_SLEEP);
+ }
+
+ log.debug("Continuing");
+
+ return true;
+ }
+ else
+ {
+ if ((_sent / _batchSize % LOG_UPATE_INTERVAL) == 0)
+ {
+ console.info("Total Delay :" + _delay + " "
+ + ((_delayShifting == 0) ? "Stablised" : ("Not Stablised(" + _delayShifting + ")")));
+ }
+ }
+
+ }
+
+ return false;
+ }
+
+ /**
+ * Sleep normally takes micro-seconds this allows the use of a nano-second value.
+ *
+ * @param delay nanoseconds to sleep for.
+ */
+ private void sleepLong(long delay)
+ {
+ sleep(delay / 1000000, (int) (delay % 1000000));
+ }
+
+ /**
+ * Sleep for the specified micro-seconds.
+ * @param sleep microseconds to sleep for.
+ */
+ private void sleep(long sleep)
+ {
+ sleep(sleep, 0);
+ }
+
+ /**
+ * Perform the sleep , swallowing any InteruptException.
+ *
+ * NOTE: If a sleep request is > 10s then reset only sleep for 5s
+ *
+ * @param milli to sleep for
+ * @param nano sub miliseconds to sleep for
+ */
+ private void sleep(long milli, int nano)
+ {
+ try
+ {
+ log.debug("Sleep:" + milli + ":" + nano);
+ if (milli > 10000)
+ {
+
+ if (_delay == milli)
+ {
+ _totalDuration = _totalReceived / _batchSize * EXPECTED_TIME_PER_BATCH;
+ log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000)
+ + "s. Reset _totalDuration:" + _totalDuration);
+ }
+ else
+ {
+ log.error("Sleeping for more than 10 seconds adjusted to 5s!:" + (milli / 1000) + "s");
+ }
+
+ milli = 5000;
+ }
+
+ Thread.sleep(milli, nano);
+ }
+ catch (InterruptedException e)
+ {
+ //
+ }
+ }
+
+ public void setClient(SustainedClientTestCase client)
+ {
+ _client = client;
+ }
+ }
+
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java
new file mode 100644
index 0000000000..5979a459ec
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java
@@ -0,0 +1,126 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.sustained;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.framework.DropInTest;
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import java.util.Properties;
+
+/**
+ * SustainedTestCase is a {@link FrameworkBaseCase} that runs the "Perf_SustainedPubSub" test case. This consists of one
+ * test client sending, and several receiving, and attempts to find the highest rate at which messages can be broadcast
+ * to the receivers. It is also a {@link DropInTest} to which more test clients may be added during a test run.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+public class SustainedTestCase extends FrameworkBaseCase implements DropInTest
+{
+ /** Used for debugging. */
+ Logger log = Logger.getLogger(SustainedTestCase.class);
+
+ /** Holds the root name of the topic on which to send the test messages. */
+ private static final String SUSTAINED_KEY = "Perf_SustainedPubSub";
+
+ /**
+ * Creates a new coordinating test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public SustainedTestCase(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Performs a single test run of the sustained test.
+ *
+ * @throws Exception Any exceptions are allowed to fall through and fail the test.
+ */
+ public void testBasicPubSub() throws Exception
+ {
+ log.debug("public void testSinglePubSubCycle(): called");
+
+ Properties testConfig = new Properties();
+ testConfig.put("TEST_NAME", "Perf_SustainedPubSub");
+ testConfig.put("SUSTAINED_KEY", SUSTAINED_KEY);
+ testConfig.put("SUSTAINED_NUM_RECEIVERS", Integer.getInteger("numReceives", 2));
+ testConfig.put("SUSTAINED_UPDATE_INTERVAL", Integer.getInteger("batchSize", 1000));
+ testConfig.put("SUSTAINED_UPDATE_KEY", SUSTAINED_KEY + ".UPDATE");
+ testConfig.put("ACKNOWLEDGE_MODE", Integer.getInteger("ackMode", AMQSession.AUTO_ACKNOWLEDGE));
+
+ log.info("Created Config: " + testConfig.entrySet().toArray());
+
+ getCircuitFactory().sequenceTest(null, null, testConfig);
+ }
+
+ /**
+ * Accepts a late joining client into this test case. The client will be enlisted with a control message
+ * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields:
+ *
+ * <p/><table>
+ * <tr><td> CLIENT_NAME <td> A unique name for the new client.
+ * <tr><td> CLIENT_PRIVATE_CONTROL_KEY <td> The key for the route on which the client receives its control messages.
+ * </table>
+ *
+ * @param message The late joiners join message.
+ *
+ * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed.
+ */
+ public void lateJoin(Message message) throws JMSException
+ {
+ throw new RuntimeException("Not implemented.");
+ /*
+ // Extract the joining clients details from its join request message.
+ TestClientDetails clientDetails = new TestClientDetails();
+ clientDetails.clientName = message.getStringProperty("CLIENT_NAME");
+ clientDetails.privateControlKey = message.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY");
+
+ // Register the joining client, but do block for confirmation as cannot do a synchronous receivers during this
+ // method call, as it may have been called from an 'onMessage' method.
+ assignReceiverRole(clientDetails, new Properties(), false);
+ */
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as known to the test
+ * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test
+ * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case
+ * name "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ *
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return "Perf_SustainedPubSub";
+ }
+}
diff --git a/Final/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/Final/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java
new file mode 100644
index 0000000000..8f0ef193ef
--- /dev/null
+++ b/Final/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java
@@ -0,0 +1,463 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedtesting;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import org.apache.qpid.interop.clienttestcases.*;
+import org.apache.qpid.sustained.SustainedClientTestCase;
+import org.apache.qpid.test.framework.MessagingTestConfigProperties;
+import org.apache.qpid.test.framework.TestUtils;
+import org.apache.qpid.test.framework.clocksynch.ClockSynchThread;
+import org.apache.qpid.test.framework.clocksynch.ClockSynchronizer;
+import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer;
+import org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd;
+
+import uk.co.thebadgerset.junit.extensions.SleepThrottle;
+import uk.co.thebadgerset.junit.extensions.Throttle;
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+
+import javax.jms.*;
+
+import java.util.*;
+
+/**
+ * Implements a test client as described in the interop testing spec
+ * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that
+ * reacts to control message sequences send by the test {@link Coordinator}.
+ *
+ * <p/><table><caption>Messages Handled by SustainedTestClient</caption>
+ * <tr><th> Message <th> Action
+ * <tr><td> Invite(compulsory) <td> Reply with Enlist.
+ * <tr><td> Invite(test case) <td> Reply with Enlist if test case available.
+ * <tr><td> AssignRole(test case) <td> Reply with Accept Role if matches an enlisted test. Keep test parameters.
+ * <tr><td> Start <td> Send test messages defined by test parameters. Send report on messages sent.
+ * <tr><td> Status Request <td> Send report on messages received.
+ * <tr><td> Terminate <td> Terminate the test client.
+ * <tr><td> ClockSynch <td> Synch clock against the supplied UDP address.
+ * </table>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Handle all incoming control messages. <td> {@link TestClientControlledTest}
+ * <tr><td> Configure and look up test cases by name. <td> {@link TestClientControlledTest}
+ * </table>
+ */
+public class TestClient implements MessageListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TestClient.class);
+
+ /** Used for reporting to the console. */
+ private static final Logger console = Logger.getLogger("CONSOLE");
+
+ /** Holds the default identifying name of the test client. */
+ public static final String CLIENT_NAME = "java";
+
+ /** Holds the URL of the broker to run the tests on. */
+ public static String brokerUrl;
+
+ /** Holds the virtual host to run the tests on. If <tt>null</tt>, then the default virtual host is used. */
+ public static String virtualHost;
+
+ /**
+ * Holds the test context properties that provides the default test parameters, plus command line overrides.
+ * This is initialized with the default test parameters, to which command line overrides may be applied.
+ */
+ public static ParsedProperties testContextProperties =
+ TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
+
+ /** Holds all the test cases loaded from the classpath. */
+ Map<String, TestClientControlledTest> testCases = new HashMap<String, TestClientControlledTest>();
+
+ /** Holds the test case currently being run by this client. */
+ protected TestClientControlledTest currentTestCase;
+
+ /** Holds the connection to the broker that the test is being coordinated on. */
+ protected Connection connection;
+
+ /** Holds the message producer to hold the test coordination over. */
+ protected MessageProducer producer;
+
+ /** Holds the JMS controlSession for the test coordination. */
+ protected Session session;
+
+ /** Holds the name of this client, with a default value. */
+ protected String clientName = CLIENT_NAME;
+
+ /** This flag indicates that the test client should attempt to join the currently running test case on start up. */
+ protected boolean join;
+
+ /** Holds the clock synchronizer for the test node. */
+ ClockSynchThread clockSynchThread;
+
+ /**
+ * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client
+ * identifying name.
+ *
+ * @param brokerUrl The url of the broker to connect to.
+ * @param virtualHost The virtual host to conect to.
+ * @param clientName The client name to use.
+ * @param join Flag to indicate that this client should attempt to join running tests.
+ */
+ public TestClient(String brokerUrl, String virtualHost, String clientName, boolean join)
+ {
+ log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost
+ + ", String clientName = " + clientName + ", boolean join = " + join + "): called");
+
+ // Retain the connection parameters.
+ this.brokerUrl = brokerUrl;
+ this.virtualHost = virtualHost;
+ this.clientName = clientName;
+ this.join = join;
+ }
+
+ /**
+ * The entry point for the interop test coordinator. This client accepts the following command line arguments:
+ *
+ * <p/><table>
+ * <tr><td> -b <td> The broker URL. <td> Optional.
+ * <tr><td> -h <td> The virtual host. <td> Optional.
+ * <tr><td> -n <td> The test client name. <td> Optional.
+ * <tr><td> name=value <td> Trailing argument define name/value pairs. Added to system properties. <td> Optional.
+ * </table>
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called");
+ console.info("Qpid Distributed Test Client.");
+
+ // Override the default broker url to be localhost:5672.
+ testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672");
+
+ // Use the command line parser to evaluate the command line with standard handling behaviour (print errors
+ // and usage then exist if there are errors).
+ // Any options and trailing name=value pairs are also injected into the test context properties object,
+ // to override any defaults that may have been set up.
+ ParsedProperties options =
+ new ParsedProperties(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args,
+ new uk.co.thebadgerset.junit.extensions.util.CommandLineParser(
+ new String[][]
+ {
+ { "b", "The broker URL.", "broker", "false" },
+ { "h", "The virtual host to use.", "virtual host", "false" },
+ { "o", "The name of the directory to output test timings to.", "dir", "false" },
+ { "n", "The name of the test client.", "name", "false" },
+ { "j", "Join this test client to running test.", "false" }
+ }), testContextProperties));
+
+ // Extract the command line options.
+ String brokerUrl = options.getProperty("b");
+ String virtualHost = options.getProperty("h");
+ String clientName = options.getProperty("n");
+ clientName = (clientName == null) ? CLIENT_NAME : clientName;
+ boolean join = options.getPropertyAsBoolean("j");
+
+ // To distinguish logging output set up an NDC on the client name.
+ NDC.push(clientName);
+
+ // Create a test client and start it running.
+ TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join);
+
+ // Use a class path scanner to find all the interop test case implementations.
+ // Hard code the test classes till the classpath scanner is fixed.
+ Collection<Class<? extends TestClientControlledTest>> testCaseClasses =
+ new ArrayList<Class<? extends TestClientControlledTest>>();
+ // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true);
+ Collections.addAll(testCaseClasses, TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class,
+ TestCase4P2PMessageSize.class, TestCase5PubSubMessageSize.class, SustainedClientTestCase.class,
+ TestClientCircuitEnd.class);
+
+ try
+ {
+ client.start(testCaseClasses);
+ }
+ catch (Exception e)
+ {
+ log.error("The test client was unable to start.", e);
+ console.info(e.getMessage());
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Starts the interop test client running. This causes it to start listening for incoming test invites.
+ *
+ * @param testCaseClasses The classes of the available test cases. The test case names from these are used to
+ * matchin incoming test invites against.
+ *
+ * @throws JMSException Any underlying JMSExceptions are allowed to fall through.
+ */
+ protected void start(Collection<Class<? extends TestClientControlledTest>> testCaseClasses) throws JMSException
+ {
+ log.debug("protected void start(Collection<Class<? extends TestClientControlledTest>> testCaseClasses = "
+ + testCaseClasses + "): called");
+
+ // Create all the test case implementations and index them by the test names.
+ for (Class<? extends TestClientControlledTest> nextClass : testCaseClasses)
+ {
+ try
+ {
+ TestClientControlledTest testCase = nextClass.newInstance();
+ testCases.put(testCase.getName(), testCase);
+ }
+ catch (InstantiationException e)
+ {
+ log.warn("Could not instantiate test case class: " + nextClass.getName(), e);
+ // Ignored.
+ }
+ catch (IllegalAccessException e)
+ {
+ log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e);
+ // Ignored.
+ }
+ }
+
+ // Open a connection to communicate with the coordinator on.
+ connection = TestUtils.createConnection(testContextProperties);
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Set this up to listen for control messages.
+ Topic privateControlTopic = session.createTopic("iop.control." + clientName);
+ MessageConsumer consumer = session.createConsumer(privateControlTopic);
+ consumer.setMessageListener(this);
+
+ Topic controlTopic = session.createTopic("iop.control");
+ MessageConsumer consumer2 = session.createConsumer(controlTopic);
+ consumer2.setMessageListener(this);
+
+ // Create a producer to send replies with.
+ producer = session.createProducer(null);
+
+ // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client
+ // is available to join the current test case, if it supports it. This message may be ignored, or it may result
+ // in this test client receiving a test invite.
+ if (join)
+ {
+ Message joinMessage = session.createMessage();
+
+ joinMessage.setStringProperty("CONTROL_TYPE", "JOIN");
+ joinMessage.setStringProperty("CLIENT_NAME", clientName);
+ joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName);
+ producer.send(controlTopic, joinMessage);
+ }
+
+ // Start listening for incoming control messages.
+ connection.start();
+ }
+
+ /**
+ * Handles all incoming control messages.
+ *
+ * @param message The incoming message.
+ */
+ public void onMessage(Message message)
+ {
+ NDC.push(clientName);
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ try
+ {
+ String controlType = message.getStringProperty("CONTROL_TYPE");
+ String testName = message.getStringProperty("TEST_NAME");
+
+ log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'");
+
+ // Check if the message is a test invite.
+ if ("INVITE".equals(controlType))
+ {
+ // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites
+ // for which test cases exist.
+ boolean enlist = false;
+
+ if (testName != null)
+ {
+ log.debug("Got an invite to test: " + testName);
+
+ // Check if the requested test case is available.
+ TestClientControlledTest testCase = testCases.get(testName);
+
+ if (testCase != null)
+ {
+ log.debug("Found implementing class for test '" + testName + "', enlisting for it.");
+
+ // Check if the test case will accept the invitation.
+ enlist = testCase.acceptInvite(message);
+
+ log.debug("The test case "
+ + (enlist ? " accepted the invite, enlisting for it."
+ : " did not accept the invite, not enlisting."));
+
+ // Make the requested test case the current test case.
+ currentTestCase = testCase;
+ }
+ else
+ {
+ log.debug("Received an invite to the test '" + testName + "' but this test is not known.");
+ }
+ }
+ else
+ {
+ log.debug("Got a compulsory invite, enlisting for it.");
+
+ enlist = true;
+ }
+
+ if (enlist)
+ {
+ // Reply with the client name in an Enlist message.
+ Message enlistMessage = session.createMessage();
+ enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST");
+ enlistMessage.setStringProperty("CLIENT_NAME", clientName);
+ enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName);
+ enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID());
+
+ log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo());
+
+ producer.send(message.getJMSReplyTo(), enlistMessage);
+ }
+ else
+ {
+ // Reply with the client name in an Decline message.
+ Message enlistMessage = session.createMessage();
+ enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE");
+ enlistMessage.setStringProperty("CLIENT_NAME", clientName);
+ enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName);
+ enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID());
+
+ log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo());
+
+ producer.send(message.getJMSReplyTo(), enlistMessage);
+ }
+ }
+ else if ("ASSIGN_ROLE".equals(controlType))
+ {
+ // Assign the role to the current test case.
+ String roleName = message.getStringProperty("ROLE");
+
+ log.debug("Got a role assignment to role: " + roleName);
+
+ TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName);
+
+ currentTestCase.assignRole(role, message);
+
+ // Reply by accepting the role in an Accept Role message.
+ Message acceptRoleMessage = session.createMessage();
+ acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName);
+ acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE");
+ acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID());
+
+ log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo());
+
+ producer.send(message.getJMSReplyTo(), acceptRoleMessage);
+ }
+ else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType))
+ {
+ if ("START".equals(controlType))
+ {
+ log.debug("Got a start notification.");
+
+ // Extract the number of test messages to send from the start notification.
+ int numMessages;
+
+ try
+ {
+ numMessages = message.getIntProperty("MESSAGE_COUNT");
+ }
+ catch (NumberFormatException e)
+ {
+ // If the number of messages is not specified, use the default of one.
+ numMessages = 1;
+ }
+
+ // Start the current test case.
+ currentTestCase.start(numMessages);
+ }
+ else
+ {
+ log.debug("Got a status request.");
+ }
+
+ // Generate the report from the test case and reply with it as a Report message.
+ Message reportMessage = currentTestCase.getReport(session);
+ reportMessage.setStringProperty("CLIENT_NAME", clientName);
+ reportMessage.setStringProperty("CONTROL_TYPE", "REPORT");
+ reportMessage.setJMSCorrelationID(message.getJMSCorrelationID());
+
+ log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo());
+
+ producer.send(message.getJMSReplyTo(), reportMessage);
+ }
+ else if ("TERMINATE".equals(controlType))
+ {
+ console.info("Received termination instruction from coordinator.");
+
+ // Is a cleaner shutdown needed?
+ connection.close();
+ System.exit(0);
+ }
+ else if ("CLOCK_SYNCH".equals(controlType))
+ {
+ log.debug("Received clock synch command.");
+ String address = message.getStringProperty("ADDRESS");
+
+ log.debug("address = " + address);
+
+ // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds.
+ if (clockSynchThread != null)
+ {
+ clockSynchThread.terminate();
+ }
+
+ SleepThrottle throttle = new SleepThrottle();
+ throttle.setRate(0.1f);
+
+ clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle);
+ clockSynchThread.start();
+ }
+ else
+ {
+ // Log a warning about this but otherwise ignore it.
+ log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message);
+ }
+ }
+ catch (JMSException e)
+ {
+ // Log a warning about this, but otherwise ignore it.
+ log.warn("Got JMSException whilst handling message: " + message, e);
+ }
+ // Log any runtimes that fall through this message handler. These are fatal errors for the test client.
+ catch (RuntimeException e)
+ {
+ log.error("The test client message handler got an unhandled exception: ", e);
+ console.info("The message handler got an unhandled exception, terminating the test client.");
+ System.exit(1);
+ }
+ finally
+ {
+ NDC.pop();
+ }
+ }
+}
diff --git a/Final/java/integrationtests/src/resources/sustained-log4j.xml b/Final/java/integrationtests/src/resources/sustained-log4j.xml
new file mode 100644
index 0000000000..c5ab3137bf
--- /dev/null
+++ b/Final/java/integrationtests/src/resources/sustained-log4j.xml
@@ -0,0 +1,69 @@
+<?xml version="1.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.
+ -
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+
+ <appender name="FileAppender" class="org.apache.log4j.FileAppender">
+ <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
+ <param name="Append" value="false"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p (%F:%L) - %m%n"/>
+ <!--param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/-->
+ </layout>
+ </appender>
+
+ <category name="SustainedTest">
+ <priority value="${sustained.level}"/>
+ </category>
+
+ <category name="org.apache">
+ <priority value="warn"/>
+ </category>
+
+ <category name="org.apache.qpid.interop">
+ <priority value="${interop.logging.level}"/>
+ </category>
+
+
+ <category name="org.apache.qpid.sustained">
+ <priority value="${amqj.logging.level}"/>
+ </category>
+
+ <!--category name="org.apache.qpid.server.txn">
+ <priority value="debug"/>
+ </category>-->
+
+ <root>
+ <priority value="all"/>
+ <appender-ref ref="STDOUT"/>
+ <!--appender-ref ref="ArchivingFileAppender"/-->
+ </root>
+</log4j:configuration>
diff --git a/Final/java/management/eclipse-plugin/META-INF/MANIFEST.MF b/Final/java/management/eclipse-plugin/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..a03c35c457
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Qpid Management Console Plug-in
+Bundle-SymbolicName: org.apache.qpid.management.ui; singleton:=true
+Bundle-Version: 0.1.0
+Bundle-Activator: org.apache.qpid.management.ui.Activator
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.ui.forms,
+ jmxremote.sasl
+Eclipse-LazyStart: true
+Bundle-Vendor: Apache Software Foundation
diff --git a/Final/java/management/eclipse-plugin/README.txt b/Final/java/management/eclipse-plugin/README.txt
new file mode 100644
index 0000000000..5325bf27ec
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/README.txt
@@ -0,0 +1,21 @@
+
+Running the Qpid Management Console (eclipse-plugin)
+----------------------------------------------------
+
+To run the management console, set the QPIDMC_HOME environment variable to
+qpid management console root directory (e.g. C:/qpidmc)and add $QPIDMC_HOME/bin to your PATH.
+Then run the script to launch the management console-
+For Windows:
+------------
+qpidmc.bat
+qpidmc.sh (using cygwin)
+
+Unix:
+-----
+qpidmc.sh <operating system> <windowing system> <platform achitecture>
+eg. qpidms.sh linux motif x86
+qpidmc_motif.sh
+qpidmc_gtk.sh
+
+Apache confluence page for latest information:
+http://cwiki.apache.org/confluence/display/qpid/Qpid+Management+Console
diff --git a/Final/java/management/eclipse-plugin/bin/qpidmc.bat b/Final/java/management/eclipse-plugin/bin/qpidmc.bat
new file mode 100644
index 0000000000..1f3207f043
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/bin/qpidmc.bat
@@ -0,0 +1,55 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Management Console
+
+rem Guess QPIDMC_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPIDMC_HOME%" == "" goto gotHome
+set QPIDMC_HOME=%CURRENT_DIR%
+echo %QPIDMC_HOME%
+if exist "%QPIDMC_HOME%\bin\qpidmc.bat" goto okHome
+cd ..
+set QPIDMC_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPIDMC_HOME%\bin\qpidmc.bat" goto okHome
+echo The QPIDMC_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of agruments (up to the command line limit, anyway).
+
+"%JAVA_HOME%\bin\java" -Xms40m -Xmx256m -Declipse.consoleLog=false -jar %QPIDMC_HOME%\eclipse\startup.jar org.eclipse.core.launcher.Main -launcher %QPIDMC_HOME%\eclipse\eclipse -name "Qpid Management Console" -showsplash 600 -configuration "file:%QPIDMC_HOME%\configuration" -os win32 -ws win32 -arch x86
diff --git a/Final/java/management/eclipse-plugin/bin/qpidmc.sh b/Final/java/management/eclipse-plugin/bin/qpidmc.sh
new file mode 100755
index 0000000000..2472545a14
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/bin/qpidmc.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if [ "$JAVA_HOME" == "" ]; then
+ echo "The JAVA_HOME environment variable is not defined";
+ exit 0;
+fi
+
+if [ "$QPIDMC_HOME" == "" ]; then
+ echo "The QPIDMC_HOME environment variable is not defined correctly";
+ exit 0;
+fi
+
+# Test if we're running on cygwin.
+cygwin=false
+if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then
+ cygwin=true
+fi
+
+if $cygwin; then
+ QPIDMC_HOME=$(cygpath -w $QPIDMC_HOME)
+fi
+
+os=win32
+ws=win32
+arch=x86
+
+##echo $os
+##echo $ws
+##echo $arch
+
+## If this is to be run on different platform other than windows then following parameters should be passed
+## qpidmc.sh <operating system> <windowing system> <platform achitecture>
+## eg. qpidmc.sh linux motif x86
+if [ $# -eq 3 ]; then
+ os=$1
+ ws=$2
+ arch=$3
+fi
+
+if [ $os = "SunOS" ]; then
+ os="solaris"
+elif [ $os = "Linux" ]; then
+ os="linux"
+fi
+
+"$JAVA_HOME/bin/java" -Xms40m -Xmx256m -Declipse.consoleLog=false -jar $QPIDMC_HOME/eclipse/startup.jar org.eclipse.core.launcher.Main -launcher $QPIDMC_HOME/eclipse/eclipse -name "Qpid Management Console" -showsplash 600 -configuration "file:$QPIDMC_HOME/configuration" -os $os -ws $ws -arch $arch
diff --git a/Final/java/management/eclipse-plugin/bin/qpidmc_gtk.sh b/Final/java/management/eclipse-plugin/bin/qpidmc_gtk.sh
new file mode 100755
index 0000000000..10b463d63b
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/bin/qpidmc_gtk.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+os=`uname | tr A-Z a-z`
+arch=`uname -p`
+
+$QPIDMC_HOME/bin/qpidmc.sh $os gtk $arch
diff --git a/Final/java/management/eclipse-plugin/bin/qpidmc_motif.sh b/Final/java/management/eclipse-plugin/bin/qpidmc_motif.sh
new file mode 100755
index 0000000000..f53be75d87
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/bin/qpidmc_motif.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+os=`uname | tr A-Z a-z`
+arch=`uname -p`
+
+$QPIDMC_HOME/bin/qpidmc.sh $os motif $arch
diff --git a/Final/java/management/eclipse-plugin/icons/Thumbs.db b/Final/java/management/eclipse-plugin/icons/Thumbs.db
new file mode 100644
index 0000000000..306bfb2eda
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/Thumbs.db
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/add.gif b/Final/java/management/eclipse-plugin/icons/add.gif
new file mode 100644
index 0000000000..252d7ebcb8
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/add.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/delete.gif b/Final/java/management/eclipse-plugin/icons/delete.gif
new file mode 100644
index 0000000000..6f647666d3
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/delete.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/icon_ClosedFolder.gif b/Final/java/management/eclipse-plugin/icons/icon_ClosedFolder.gif
new file mode 100644
index 0000000000..beb6ed134c
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/icon_ClosedFolder.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/icon_OpenFolder.gif b/Final/java/management/eclipse-plugin/icons/icon_OpenFolder.gif
new file mode 100644
index 0000000000..a9c777343c
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/icon_OpenFolder.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/mbean_view.png b/Final/java/management/eclipse-plugin/icons/mbean_view.png
new file mode 100644
index 0000000000..9871b72bb8
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/mbean_view.png
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/notifications.gif b/Final/java/management/eclipse-plugin/icons/notifications.gif
new file mode 100644
index 0000000000..f1e585bdf7
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/notifications.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/qpidConnections.gif b/Final/java/management/eclipse-plugin/icons/qpidConnections.gif
new file mode 100644
index 0000000000..89489f11f2
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/qpidConnections.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/qpidmc.gif b/Final/java/management/eclipse-plugin/icons/qpidmc.gif
new file mode 100644
index 0000000000..baf929fbc5
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/qpidmc.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/qpidmc16.gif b/Final/java/management/eclipse-plugin/icons/qpidmc16.gif
new file mode 100644
index 0000000000..4df535bb9a
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/qpidmc16.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/qpidmc32.bmp b/Final/java/management/eclipse-plugin/icons/qpidmc32.bmp
new file mode 100644
index 0000000000..e42ce01dff
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/qpidmc32.bmp
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/qpidmc32.gif b/Final/java/management/eclipse-plugin/icons/qpidmc32.gif
new file mode 100644
index 0000000000..e42ce01dff
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/qpidmc32.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/reconnect.gif b/Final/java/management/eclipse-plugin/icons/reconnect.gif
new file mode 100644
index 0000000000..e2f8c3e1fe
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/reconnect.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/refresh.gif b/Final/java/management/eclipse-plugin/icons/refresh.gif
new file mode 100644
index 0000000000..a063c230ac
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/refresh.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/splash.bmp b/Final/java/management/eclipse-plugin/icons/splash.bmp
new file mode 100644
index 0000000000..b528a508c5
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/splash.bmp
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/icons/stop.gif b/Final/java/management/eclipse-plugin/icons/stop.gif
new file mode 100644
index 0000000000..dc47edf069
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/icons/stop.gif
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/plugin.properties b/Final/java/management/eclipse-plugin/plugin.properties
new file mode 100644
index 0000000000..8507441886
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/plugin.properties
@@ -0,0 +1,20 @@
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+###############################################################################
+pluginName = Qpid Management Console Plug-in
+providerName = Apache Software Foundation \ No newline at end of file
diff --git a/Final/java/management/eclipse-plugin/plugin.xml b/Final/java/management/eclipse-plugin/plugin.xml
new file mode 100644
index 0000000000..5774859b47
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/plugin.xml
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<plugin>
+
+ <extension
+ id="application"
+ point="org.eclipse.core.runtime.applications">
+ <application>
+ <run
+ class="org.apache.qpid.management.ui.Application">
+ </run>
+ </application>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ name="qpid.management.perspective"
+ class="org.apache.qpid.management.ui.Perspective"
+ id="org.apache.qpid.management.ui.perspective">
+ </perspective>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <category
+ id="org.apache.qpid.management.ui.viewcategory"
+ name="Qpid Management Console"/>
+ <view
+ allowMultiple="false"
+ category="org.apache.qpid.management.ui.viewcategory"
+ class="org.apache.qpid.management.ui.views.NavigationView"
+ icon="icons/qpidConnections.gif"
+ id="org.apache.qpid.management.ui.navigationView"
+ name="Qpid Connections">
+ </view>
+ <view
+ allowMultiple="false"
+ category="org.apache.qpid.management.ui.viewcategory"
+ class="org.apache.qpid.management.ui.views.MBeanView"
+ icon="icons/mbean_view.png"
+ id="org.apache.qpid.management.ui.mbeanView"
+ name="Qpid Management">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <category
+ name="qpid.manager.commands"
+ id="org.apache.qpid.management.ui.category">
+ </category>
+ <command
+ name="New Connection"
+ description="Created a new Qpid server connection"
+ categoryId="org.apache.qpid.management.ui.category"
+ id="org.apache.qpid.management.ui.actions.cmd_add">
+ </command>
+ <command
+ categoryId="org.apache.qpid.management.ui.category"
+ description="Reconnect the Qpid server connection"
+ id="org.apache.qpid.management.ui.actions.cmd_reconnect"
+ name="Reconnect"/>
+ <command
+ categoryId="org.apache.qpid.management.ui.category"
+ description="Disconnects the Qpid server connection"
+ id="org.apache.qpid.management.ui.actions.cmd_disconnect"
+ name="Disconnect"/>
+ <command
+ categoryId="org.apache.qpid.management.ui.category"
+ description="Removes the server from management console"
+ id="org.apache.qpid.management.ui.actions.cmd_remove"
+ name="Remove Connection"/>
+ <command
+ categoryId="org.apache.qpid.management.ui.category"
+ description="refreshes the views"
+ id="org.apache.qpid.management.ui.actions.cmd_refresh"
+ name="Refresh"/>
+ <command
+ categoryId="org.apache.qpid.management.ui.category"
+ description="pops up the window for editing selected attribute"
+ id="org.apache.qpid.management.ui.actions.cmd_editAttribute"
+ name="Edit Attribute"/>
+ <command
+ categoryId="org.apache.qpid.management.ui.category"
+ description="About Qpid Management Console"
+ id="qpidmc.about"
+ name="About"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.bindings">
+ <key
+ commandId="org.apache.qpid.management.ui.actions.cmd_add"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="CTRL+Alt+N">
+ </key>
+ <key
+ commandId="org.apache.qpid.management.ui.actions.cmd_reconnect"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="CTRL+Alt+C"/>
+ <key
+ commandId="org.apache.qpid.management.ui.actions.cmd_disconnect"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="CTRL+Alt+D">
+ </key>
+ <key
+ commandId="org.apache.qpid.management.ui.actions.cmd_remove"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="CTRL+Alt+R"/>
+ <key
+ commandId="org.apache.qpid.management.ui.actions.cmd_refresh"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="CTRL+Alt+F5"/>
+ <key
+ commandId="org.apache.qpid.management.ui.actions.cmd_editAttribute"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="CTRL+Alt+E"/>
+ <key
+ commandId="org.eclipse.ui.file.exit"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="CTRL+Alt+X">
+ </key>
+ </extension>
+
+ <extension
+ id="product"
+ point="org.eclipse.core.runtime.products">
+ <product
+ application="org.apache.qpid.management.ui.application"
+ name="Qpid Management Console">
+ <property
+ name="windowImages"
+ value="icons/qpidmc16.gif,icons/qpidmc32.gif">
+ </property>
+ <property
+ name="aboutText"
+ value="Qpid Management Console"/>
+ </product>
+ </extension>
+ <extension
+ point="org.eclipse.ui.actionSets">
+ <actionSet
+ id="org.apache.qpid.management.ui.actionSet"
+ label="Qpid Action Set"
+ visible="true">
+ <menu
+ id="qpidmanager"
+ label="&amp;Qpid Manager">
+ <separator name="qpidActionsGroup"/>
+ </menu>
+ <action
+ class="org.apache.qpid.management.ui.actions.EditAttribute"
+ definitionId="org.apache.qpid.management.ui.actions.cmd_editAttribute"
+ id="org.apache.qpid.management.ui.actions.editAttribute"
+ label="Edit Attribute"
+ menubarPath="qpidmanager/mbeanactions"
+ style="push"
+ tooltip="Edit Attribute"/>
+ <action
+ class="org.apache.qpid.management.ui.actions.Refresh"
+ definitionId="org.apache.qpid.management.ui.actions.cmd_refresh"
+ icon="icons/refresh.gif"
+ id="org.apache.qpid.management.ui.actions.refresh"
+ label="Refresh"
+ menubarPath="qpidmanager/additions"
+ style="push"
+ toolbarPath="qpidActionsGroup"
+ tooltip="Refresh"/>
+ <action
+ class="org.apache.qpid.management.ui.actions.RemoveServer"
+ definitionId="org.apache.qpid.management.ui.actions.cmd_remove"
+ icon="icons/delete.gif"
+ id="org.apache.qpid.management.ui.actions.remove"
+ label="Remove Connection"
+ menubarPath="qpidmanager/additions"
+ style="push"
+ toolbarPath="qpidActionsGroup"/>
+ <action
+ class="org.apache.qpid.management.ui.actions.CloseConnection"
+ definitionId="org.apache.qpid.management.ui.actions.cmd_disconnect"
+ icon="icons/stop.gif"
+ id="org.apache.qpid.management.ui.disconnect"
+ label="Disconnect"
+ menubarPath="qpidmanager/additions"
+ toolbarPath="qpidActionsGroup"
+ tooltip="Disconnect"/>
+ <action
+ class="org.apache.qpid.management.ui.actions.ReconnectServer"
+ definitionId="org.apache.qpid.management.ui.actions.cmd_reconnect"
+ icon="icons/reconnect.gif"
+ id="org.apache.qpid.management.ui.reconnect"
+ label="Reconnect"
+ menubarPath="qpidmanager/additions"
+ toolbarPath="qpidActionsGroup"
+ tooltip="Reconnect"/>
+ <action
+ class="org.apache.qpid.management.ui.actions.AddServer"
+ definitionId="org.apache.qpid.management.ui.actions.cmd_add"
+ icon="icons/add.gif"
+ id="org.apache.qpid.management.ui.add"
+ label="New Connection"
+ menubarPath="qpidmanager/additions"
+ toolbarPath="qpidActionsGroup"
+ tooltip="New Connection"/>
+ </actionSet>
+ </extension>
+
+</plugin>
diff --git a/Final/java/management/eclipse-plugin/pom.xml b/Final/java/management/eclipse-plugin/pom.xml
new file mode 100644
index 0000000000..fd8bdce74f
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/pom.xml
@@ -0,0 +1,252 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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.management</groupId>
+ <artifactId>org.apache.qpid.management.ui</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Management</name>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>repo1.maven.org</id>
+ <name>Maven eclipse Repository</name>
+ <url>http://repo1.maven.org/eclipse</url>
+ </repository>
+ <repository>
+ <id>apache.snapshots</id>
+ <name>Apache SNAPSHOT Repository</name>
+ <url>http://people.apache.org/repo/m2-snapshot-repository</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.ibm.icu</groupId>
+ <artifactId>com.ibm.icu</artifactId>
+ <version>3.4.4</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jface</groupId>
+ <artifactId>org.eclipse.jface</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.core.commands</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.core.contenttype</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.core.expressions</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.core.jobs</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.core.runtime</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.core.runtime.compatibility.auth</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.core.runtime.compatibility.registry</artifactId>
+ <version>3.2.0</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.equinox</groupId>
+ <artifactId>org.eclipse.equinox.common</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.equinox</groupId>
+ <artifactId>org.eclipse.equinox.preferences</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.equinox</groupId>
+ <artifactId>org.eclipse.equinox.registry</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.help</groupId>
+ <artifactId>org.eclipse.help</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.osgi</groupId>
+ <artifactId>org.eclipse.osgi</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.swt</groupId>
+ <artifactId>org.eclipse.swt.win32.win32.x86</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.swt</groupId>
+ <artifactId>org.eclipse.swt</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.ui.forms</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.ui.workbench</artifactId>
+ <version>3.2.1</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.ui</artifactId>
+ <version>3.2.0</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Test Dependencies -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>icons/</directory>
+ <targetPath>icons/</targetPath>
+ <includes>
+ <include>**</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>icons/</directory>
+ <targetPath>/</targetPath>
+ <includes>
+ <include>splash.bmp</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${basedir}</directory>
+ <targetPath>/</targetPath>
+ <includes>
+ <include>plugin.xml</include>
+ <include>plugin.properties</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <!--
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ </plugin>
+ -->
+
+ <!-- This is required to identify the JAR to eclipse as a plugin -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>META-INF/MANIFEST.MF</manifestFile>
+ </archive>
+ <finalName>${artifactId}_${version}</finalName>
+ </configuration>
+ </plugin>
+
+ <!--
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ -->
+ </plugins>
+ </build>
+
+</project>
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java
new file mode 100644
index 0000000000..5eab267c28
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ * @author Bhupendra Bhardwaj
+ */
+public class Activator extends AbstractUIPlugin
+{
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.apache.qpid.management.ui";
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator()
+ {
+ plugin = this;
+ }
+
+ /*
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception
+ {
+ super.start(context);
+ }
+
+ /*
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Returns an image descriptor for the image file at the given plug-in relative path
+ *
+ * @param path the path
+ * @return the image descriptor
+ */
+ public static ImageDescriptor getImageDescriptor(String path)
+ {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java
new file mode 100644
index 0000000000..a1c4b7ddb0
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import org.eclipse.core.runtime.IPlatformRunnable;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * This class controls all aspects of the application's execution
+ * @author Bhupendra Bhardwaj
+ */
+public class Application implements IPlatformRunnable
+{
+ static Shell shell = null;
+
+ /*
+ * The call to createAndRunWorkbench will not return until the workbench is closed.
+ * The SWT event loop and other low-level logistics are handled inside this method.
+ * @see org.eclipse.core.runtime.IPlatformRunnable#run(java.lang.Object)
+ */
+ public Object run(Object args) throws Exception
+ {
+ Display display = PlatformUI.createDisplay();
+ try
+ {
+ int returnCode = PlatformUI.createAndRunWorkbench(display,
+ new ApplicationWorkbenchAdvisor());
+ if (returnCode == PlatformUI.RETURN_RESTART)
+ {
+ return IPlatformRunnable.EXIT_RESTART;
+ }
+ return IPlatformRunnable.EXIT_OK;
+ } finally
+ {
+ display.dispose();
+ }
+ }
+
+ static Shell getActiveShell()
+ {
+ return shell;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationActionBarAdvisor.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationActionBarAdvisor.java
new file mode 100644
index 0000000000..b5c1b5074a
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationActionBarAdvisor.java
@@ -0,0 +1,96 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import org.apache.qpid.management.ui.actions.VersionAction;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.GroupMarker;
+import org.eclipse.jface.action.ICoolBarManager;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
+import org.eclipse.ui.application.ActionBarAdvisor;
+import org.eclipse.ui.application.IActionBarConfigurer;
+
+/**
+ * An action bar advisor is responsible for creating, adding, and disposing of the
+ * actions added to a workbench window. Each window will be populated with
+ * new actions.
+ */
+public class ApplicationActionBarAdvisor extends ActionBarAdvisor
+{
+
+ // Actions - important to allocate these only in makeActions, and then use them
+ // in the fill methods. This ensures that the actions aren't recreated
+ // when fillActionBars is called with FILL_PROXY.
+ private IWorkbenchAction exitAction;
+ private Action _aboutAction;
+
+ public ApplicationActionBarAdvisor(IActionBarConfigurer configurer)
+ {
+ super(configurer);
+ }
+
+ protected void makeActions(final IWorkbenchWindow window)
+ {
+ // Creates the actions and registers them.
+ // Registering is needed to ensure that key bindings work.
+ // The corresponding commands keybindings are defined in the plugin.xml file.
+ // Registering also provides automatic disposal of the actions when
+ // the window is closed.
+
+ exitAction = ActionFactory.QUIT.create(window);
+ register(exitAction);
+
+ _aboutAction = new VersionAction(window);
+ register(_aboutAction);
+ }
+
+
+ protected void fillMenuBar(IMenuManager menuBar)
+ {
+ MenuManager fileMenu = new MenuManager("&Qpid Manager", "qpidmanager");
+ MenuManager helpMenu = new MenuManager("&Help", IWorkbenchActionConstants.M_HELP);
+
+ menuBar.add(fileMenu);
+ // Add a group marker indicating where action set menus will appear.
+ menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ menuBar.add(helpMenu);
+
+ fileMenu.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
+ fileMenu.add(new Separator());
+ fileMenu.add(new GroupMarker("mbeanactions"));
+ fileMenu.add(new Separator());
+ fileMenu.add(exitAction);
+
+ // Help
+ helpMenu.add(_aboutAction);
+ }
+
+ protected void fillCoolBar(ICoolBarManager coolBar)
+ {
+
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java
new file mode 100644
index 0000000000..0ad85dbf33
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.jface.resource.FontRegistry;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Main Application Registry, which contains shared resources and map to all connected servers.
+ * @author Bhupendra Bhardwaj
+ */
+public abstract class ApplicationRegistry
+{
+ private static ImageRegistry imageRegistry = new ImageRegistry();
+ private static FontRegistry fontRegistry = new FontRegistry();
+ public static final boolean debug = Boolean.getBoolean("eclipse.consoleLog");
+ public static final String securityMechanism = System.getProperty("security", null);
+ public static final String connectorClass = System.getProperty("jmxconnector");
+ public static final long timeout = Long.parseLong(System.getProperty("timeout", "5000"));
+
+ static
+ {
+ imageRegistry.put(Constants.CONSOLE_IMAGE,
+ org.apache.qpid.management.ui.Activator.getImageDescriptor("/icons/qpidmc.gif"));
+ imageRegistry.put(Constants.CLOSED_FOLDER_IMAGE,
+ org.apache.qpid.management.ui.Activator.getImageDescriptor("/icons/icon_ClosedFolder.gif"));
+ imageRegistry.put(Constants.OPEN_FOLDER_IMAGE,
+ PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER));
+ imageRegistry.put(Constants.MBEAN_IMAGE,
+ PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT));
+ imageRegistry.put(Constants.NOTIFICATION_IMAGE,
+ org.apache.qpid.management.ui.Activator.getImageDescriptor("/icons/notifications.gif"));
+ }
+
+ static
+ {
+ fontRegistry.put(Constants.FONT_BUTTON, new FontData[]{new FontData("Arial", 8, SWT.BOLD)} );
+ fontRegistry.put(Constants.FONT_BOLD, new FontData[]{new FontData("Bold", 9, SWT.BOLD)} );
+ fontRegistry.put(Constants.FONT_ITALIC, new FontData[]{new FontData("Italic", 9, SWT.ITALIC)} );
+ fontRegistry.put(Constants.FONT_TABLE_CELL, new FontData[]{new FontData("Tablecell", 8, SWT.NORMAL)} );
+ fontRegistry.put(Constants.FONT_NORMAL, new FontData[]{new FontData("Normal", 9, SWT.NORMAL)} );
+ }
+
+ /*
+ * This maps all the managed servers to the respective server registry.
+ * Server can be JMX MBeanServer or a C++ server
+ */
+ private static HashMap<ManagedServer, ServerRegistry> _serverRegistryMap = new HashMap<ManagedServer, ServerRegistry>();
+
+ // This map gets updated when a server connection closes.
+ private static List<ManagedServer> _closedServerList = new CopyOnWriteArrayList<ManagedServer>();
+
+ public static Image getImage(String key)
+ {
+ return imageRegistry.get(key);
+ }
+
+ public static Font getFont(String key)
+ {
+ return fontRegistry.get(key);
+ }
+
+ public static void addServer(ManagedServer server, ServerRegistry registry)
+ {
+ _serverRegistryMap.put(server, registry);
+ }
+
+ public static void removeServer(ManagedServer server)
+ {
+ _serverRegistryMap.remove(server);
+ }
+
+ public static ServerRegistry getServerRegistry(ManagedServer server)
+ {
+ return _serverRegistryMap.get(server);
+ }
+
+ public static ServerRegistry getServerRegistry(ManagedBean mbean)
+ {
+ ManagedServer server = mbean.getServer();
+ return getServerRegistry(server);
+ }
+
+ public static boolean isServerConnected(ManagedServer server)
+ {
+ return _serverRegistryMap.containsKey(server);
+ }
+
+ // remove the server from the registry
+ public static void serverConnectionClosed(ManagedServer server)
+ {
+ _closedServerList.add(server);
+ removeServer(server);
+ }
+
+ /*
+ * Returns the lis of closed servers. The Thread in GUI, which keeps checking for closed connection
+ * will check this and will remove the server links from the GUI.
+ */
+ public static List<ManagedServer> getClosedServers()
+ {
+ if (_closedServerList.isEmpty())
+ return null;
+
+ List<ManagedServer> list = new CopyOnWriteArrayList<ManagedServer>(_closedServerList);
+ _closedServerList.clear();
+ return list;
+ }
+
+ public static String getSecurityMechanism()
+ {
+ return securityMechanism;
+ }
+
+ public static String getJMXConnectorClass()
+ {
+ return connectorClass;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchAdvisor.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchAdvisor.java
new file mode 100644
index 0000000000..a46fa870e4
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchAdvisor.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchAdvisor;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+/**
+ * This workbench advisor creates the window advisor, and specifies
+ * the perspective id for the initial window.
+ */
+public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor
+{
+ public static final String PERSPECTIVE_ID = "org.apache.qpid.management.ui.perspective";
+
+ public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer)
+ {
+ return new ApplicationWorkbenchWindowAdvisor(configurer);
+ }
+
+
+ public String getInitialWindowPerspectiveId()
+ {
+ return PERSPECTIVE_ID;
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java
new file mode 100644
index 0000000000..e3aedef28e
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.application.ActionBarAdvisor;
+import org.eclipse.ui.application.IActionBarConfigurer;
+import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
+import org.eclipse.ui.application.WorkbenchWindowAdvisor;
+
+/**
+ *
+ * @author Bhupendra Bhardwaj
+ */
+public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor
+{
+ public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer)
+ {
+ super(configurer);
+ }
+
+ public ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer)
+ {
+ return new ApplicationActionBarAdvisor(configurer);
+ }
+
+ public void preWindowOpen()
+ {
+ IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
+ int x = Display.getDefault().getBounds().width;
+ int y = Display.getDefault().getBounds().height;
+ configurer.setInitialSize(new Point(9*x/10, 8*y/10));
+ configurer.setShowCoolBar(true);
+ configurer.setShowStatusLine(false);
+
+ configurer.setTitle(Constants.APPLICATION_NAME);
+ }
+
+ public void postWindowCreate()
+ {
+ IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
+ Shell shell = configurer.getWindow().getShell();
+ shell.setImage(ApplicationRegistry.getImage(Constants.CONSOLE_IMAGE));
+ }
+} \ No newline at end of file
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java
new file mode 100644
index 0000000000..d6f895b64a
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java
@@ -0,0 +1,140 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+/**
+ * Contains constants for the application
+ * @author Bhupendra Bhardwaj
+ *
+ */
+public class Constants
+{
+ public final static String APPLICATION_NAME = "Qpid Management Console";
+
+ public final static String ACTION_REMOVE_MBEANNODE = "Remove from list";
+ public final static String VALUE = "value";
+ public final static String TYPE = "type";
+ public final static String NODE_TYPE_SERVER = "server";
+ public final static String NODE_TYPE_DOMAIN = "domain";
+ public final static String NODE_TYPE_MBEANTYPE = "mbeantype";
+ // currently used only for virtual host instances, but will work as general also
+ public final static String NODE_TYPE_TYPEINSTANCE = "mbeantype_instance";
+ public final static String MBEAN = "mbean";
+ public final static String ATTRIBUTE = "Attribute";
+ public final static String ATTRIBUTES = "Attributes";
+ public final static String NOTIFICATIONS = "Notifications";
+ public final static String RESULT = "Result";
+ public final static String VIRTUAL_HOST = "VirtualHost";
+ public final static String DEFAULT_VH = "Default";
+ public final static String DEFAULT_USERNAME = "guest";
+ public final static String DEFAULT_PASSWORD = "guest";
+
+ public final static String USERNAME = "Username";
+ public final static String PASSWORD = "Password";
+
+ // Attributes and operations are used to customize the GUI for Qpid. If these are changes in the
+ // Qpid server, then these should be updated accordingly
+ public final static String ATTRIBUTE_QUEUE_OWNER = "owner";
+ public final static String ATTRIBUTE_QUEUE_DEPTH = "QueueDepth";
+ public final static String ATTRIBUTE_QUEUE_CONSUMERCOUNT = "ActiveConsumerCount";
+ public final static String OPERATION_CREATE_QUEUE = "createNewQueue";
+ public final static String OPERATION_CREATE_BINDING = "createNewBinding";
+ public final static String OPERATION_MOVE_MESSAGES = "moveMessages";
+
+ public final static String OPERATION_CREATEUSER = "createUser";
+ public final static String OPERATION_DELETEUSER = "deleteUser";
+ public final static String OPERATION_VIEWUSERS = "viewUsers";
+ public final static String OPERATION_PARAM_USERNAME = "username";
+
+ public final static String OPERATION_SUCCESSFUL = "Operation successful";
+ public final static String OPERATION_UNSUCCESSFUL = "Operation unsuccessful";
+
+ public final static String ALL = "All";
+
+ public final static String NAVIGATION_ROOT = "Qpid Connections";
+ public final static String DESCRIPTION = " Description";
+
+ public final static String ADMIN_MBEAN_TYPE = "UserManagement";
+ public final static String QUEUE = "Queue";
+ public final static String CONNECTION ="Connection";
+ public final static String EXCHANGE = "Exchange";
+ public final static String EXCHANGE_TYPE = "ExchangeType";
+ public final static String[] EXCHANGE_TYPE_VALUES = {"direct", "fanout", "headers", "topic"};
+ public final static String[] BOOLEAN_TYPE_VALUES = {"false", "true"};
+ public final static String[] ATTRIBUTE_TABLE_TITLES = {"Attribute Name", "Value"};
+ public static final String[] CONNECTION_PROTOCOLS ={"RMI"};
+ public static final String DEFAULT_PROTOCOL = CONNECTION_PROTOCOLS[0];
+
+ public final static String ACTION_ADDSERVER = "New Connection";
+ public final static String ACTION_RECONNECT = "Reconnect";
+ public final static String ACTION_CLOSE = "Close Connection";
+ public final static String ACTION_EDITATTRIBUTE = "Edit Attribute";
+ public final static String ACTION_LOGIN = "Login";
+
+ public final static String QUEUE_SORT_BY_NAME = "Queue Name";
+ public final static String QUEUE_SORT_BY_DEPTH = "Queue Depth";
+ public final static String QUEUE_SORT_BY_CONSUMERCOUNT = "Consumer Count";
+ public final static String QUEUE_SHOW_TEMP_QUEUES= "show temporary queues";
+
+ public final static String SUBSCRIBE_BUTTON = "Subscribe";
+ public final static String UNSUBSCRIBE_BUTTON = "Unsubscribe";
+
+ public final static String CONSOLE_IMAGE = "ConsoelImage";
+ public final static String CLOSED_FOLDER_IMAGE = "ClosedFolderImage";
+ public final static String OPEN_FOLDER_IMAGE = "OpenFolderImage";
+ public final static String MBEAN_IMAGE = "MBeanImage";
+ public final static String NOTIFICATION_IMAGE = "NotificationImage";
+
+ public final static String FONT_BUTTON = "ButtonFont";
+ public final static String FONT_BOLD = "BoldFont";
+ public final static String FONT_ITALIC = "ItalicFont";
+ public final static String FONT_TABLE_CELL = "TableCellFont";
+ public final static String FONT_NORMAL = "Normal";
+
+ public final static String BUTTON_DETAILS = "Details";
+ public final static String BUTTON_EDIT_ATTRIBUTE = "Edit Attribute";
+ public final static String BUTTON_REFRESH = "Refresh";
+ public final static String BUTTON_GRAPH = "Graph";
+ public final static int TIMER_INTERVAL = 5000;
+ public final static String BUTTON_EXECUTE = "Execute";
+ public final static String BUTTON_CLEAR = "Clear";
+ public final static String BUTTON_CONNECT = "Connect";
+ public final static String BUTTON_CANCEL = "Cancel";
+ public final static String BUTTON_UPDATE = "Update";
+
+
+ public final static int OPERATION_IMPACT_INFO = 0;
+ public final static int OPERATION_IMPACT_ACTION = 1;
+ public final static int OPERATION_IMPACT_ACTIONINFO = 2;
+ public final static int OPERATION_IMPACT_UNKNOWN = 3;
+
+ public final static String ERROR_SERVER_CONNECTION = "Server Connection Failed";
+ public final static String INFO_PROTOCOL = "Please select the protocol";
+ public final static String INFO_HOST_ADDRESS = "Please enter the host address";
+ public final static String INFO_HOST_PORT = "Please enter the port number";
+ public final static String INFO_USERNAME = "Please enter the " + USERNAME;
+ public final static String INFO_PASSWORD = "Please enter the " + PASSWORD;
+
+ public final static String MECH_CRAMMD5 = "CRAM-MD5";
+ public final static String MECH_PLAIN = "PLAIN";
+ public final static String SASL_CRAMMD5 = "SASL/CRAM-MD5";
+ public final static String SASL_PLAIN = "SASL/PLAIN";
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java
new file mode 100644
index 0000000000..31825e925d
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.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.management.ui;
+
+import static org.apache.qpid.management.ui.Constants.*;
+import java.util.HashMap;
+
+/**
+ * Class representing a managed bean on the managed server
+ * @author Bhupendra Bhardwaj
+ *
+ */
+public abstract class ManagedBean extends ManagedObject
+{
+ private String _uniqueName = "";
+ private String _domain = "";
+ private String _type = "";
+ private String _virtualHostName = null;
+ private ManagedServer _server = null;
+ private HashMap _properties = null;
+
+ public String getProperty(String key)
+ {
+ return (String)_properties.get(key);
+ }
+
+ public HashMap getProperties()
+ {
+ return _properties;
+ }
+ public void setProperties(HashMap properties)
+ {
+ this._properties = properties;
+ setName(getProperty("name"));
+ setType(getProperty("type"));
+ _virtualHostName = getProperty(VIRTUAL_HOST);
+ }
+ public String getDomain()
+ {
+ return _domain;
+ }
+ public void setDomain(String domain)
+ {
+ this._domain = domain;
+ }
+
+ public ManagedServer getServer()
+ {
+ return _server;
+ }
+ public void setServer(ManagedServer server)
+ {
+ this._server = server;
+ }
+ public String getType()
+ {
+ return _type;
+ }
+ public void setType(String type)
+ {
+ this._type = type;
+ }
+ public String getUniqueName()
+ {
+ return _uniqueName;
+ }
+ public void setUniqueName(String uniqueName)
+ {
+ this._uniqueName = uniqueName;
+ }
+
+ public String getVirtualHostName()
+ {
+ // To make it work with the broker with no virtual host implementation
+ return _virtualHostName == null ? DEFAULT_VH : _virtualHostName;
+ }
+
+ /**
+ * Returns mbean instance name. MBeans which have only one instance, the type attribute will be returned
+ * @return
+ */
+ public String getInstanceName()
+ {
+ if (getName() != null)
+ return getName();
+ else
+ return getType();
+ }
+
+ public boolean isQueue()
+ {
+ return _type.endsWith(QUEUE);
+ }
+
+ public boolean isConnection()
+ {
+ return _type.endsWith(CONNECTION);
+ }
+
+ public boolean isExchange()
+ {
+ return _type.endsWith(EXCHANGE);
+ }
+
+ public boolean isTempQueue()
+ {
+ return (isQueue() && getName().startsWith("tmp_"));
+ }
+
+ public boolean isAdmin()
+ {
+ return _type.endsWith(ADMIN_MBEAN_TYPE);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java
new file mode 100644
index 0000000000..96e0fa46c6
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+/**
+ * Abstract class representing a managed object
+ * @author Bhupendra Bhardwaj
+ */
+public abstract class ManagedObject
+{
+ private String _name;
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public void setName(String name)
+ {
+ this._name = name;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java
new file mode 100644
index 0000000000..480fdb429a
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import static org.apache.qpid.management.ui.Constants.DEFAULT_PROTOCOL;
+/**
+ * Class representing a server being managed eg. MBeanServer
+ * @author Bhupendra Bhardwaj
+ */
+public class ManagedServer extends ManagedObject
+{
+ private String _host;
+ private int _port;
+ private String _url;
+ private String _domain;
+ private String _user;
+ private String _password;
+ private String _protocol = DEFAULT_PROTOCOL;
+
+ public ManagedServer(String host, int port, String domain)
+ {
+ this(host, port, domain, null, null);
+ }
+
+ public ManagedServer(String host, int port, String domain, String user, String password)
+ {
+ setName(host + ":" + port);
+ _host = host;
+ _port = port;
+ _domain = domain;
+ _url = getRMIURL(host, port);
+ _user = user;
+ _password = password;
+ }
+
+ public String getDomain()
+ {
+ return _domain;
+ }
+
+ public String getHost()
+ {
+ return _host;
+ }
+
+ public int getPort()
+ {
+ return _port;
+ }
+
+ public String getUrl()
+ {
+ return _url;
+ }
+
+ public String getProtocol()
+ {
+ return _protocol;
+ }
+
+ public String getPassword()
+ {
+ return _password;
+ }
+
+ public void setPassword(String password)
+ {
+ _password = password;
+ }
+
+ public String getUser()
+ {
+ return _user;
+ }
+
+ public void setUser(String user)
+ {
+ _user = user;
+ }
+
+ private String getRMIURL(String host, int port)
+ {
+ return "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java
new file mode 100644
index 0000000000..f93200cadf
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import org.apache.qpid.management.ui.views.MBeanView;
+import org.apache.qpid.management.ui.views.NavigationView;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+
+/**
+ *
+ * @author Bhupendra Bhardwaj
+ */
+public class Perspective implements IPerspectiveFactory
+{
+ public void createInitialLayout(IPageLayout layout)
+ {
+ String editorArea = layout.getEditorArea();
+ layout.setEditorAreaVisible(false);
+
+ // standalone view meaning it can't be docked or stacked with other views, and it doesn't have a title bar.
+ layout.addStandaloneView(NavigationView.ID, true, IPageLayout.LEFT, 0.30f, editorArea);
+ layout.addStandaloneView(MBeanView.ID, true, IPageLayout.RIGHT, 0.70f, editorArea);
+
+ layout.getViewLayout(NavigationView.ID).setCloseable(false);
+ layout.getViewLayout(MBeanView.ID).setCloseable(false);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ServerRegistry.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ServerRegistry.java
new file mode 100644
index 0000000000..313e143df5
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ServerRegistry.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.management.ui;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.qpid.management.ui.jmx.ClientListener;
+import org.apache.qpid.management.ui.model.ManagedAttributeModel;
+import org.apache.qpid.management.ui.model.NotificationObject;
+import org.apache.qpid.management.ui.model.OperationDataModel;
+
+public abstract class ServerRegistry
+{
+ private ManagedServer _managedServer = null;
+
+ // list of virtual hosts for this server
+ private List<String> _virtualHosts = new ArrayList<String>();
+ // map of all Connection mbeans
+ private ConcurrentMap<String,List<ManagedBean>> _connections = new ConcurrentHashMap<String,List<ManagedBean>>();
+ // map of all exchange mbeans
+ private ConcurrentMap<String,List<ManagedBean>> _exchanges = new ConcurrentHashMap<String,List<ManagedBean>>();
+ // map of all queue mbenas
+ private ConcurrentMap<String,List<ManagedBean>> _queues = new ConcurrentHashMap<String,List<ManagedBean>>();
+
+ public ServerRegistry()
+ {
+
+ }
+
+ public ServerRegistry(ManagedServer server)
+ {
+ _managedServer = server;
+ }
+
+ public ManagedServer getManagedServer()
+ {
+ return _managedServer;
+ }
+
+ public void setManagedServer(ManagedServer server)
+ {
+ _managedServer = server;
+ }
+
+ protected void addConnectionMBean(ManagedBean mbean)
+ {
+ String vHost = mbean.getVirtualHostName();
+ _connections.putIfAbsent(vHost, new ArrayList<ManagedBean>());
+ _connections.get(vHost).add(mbean);
+ }
+
+ protected void addExchangeMBean(ManagedBean mbean)
+ {
+ String vHost = mbean.getVirtualHostName();
+ _exchanges.putIfAbsent(vHost, new ArrayList<ManagedBean>());
+ _exchanges.get(vHost).add(mbean);
+ }
+
+ protected void addQueueMBean(ManagedBean mbean)
+ {
+ String vHost = mbean.getVirtualHostName();
+ _queues.putIfAbsent(vHost, new ArrayList<ManagedBean>());
+ _queues.get(vHost).add(mbean);
+ }
+
+ protected void removeConnectionMBean(ManagedBean mbean)
+ {
+ _connections.get(mbean.getVirtualHostName()).remove(mbean);
+ }
+
+ protected void removeExchangeMBean(ManagedBean mbean)
+ {
+ _exchanges.get(mbean.getVirtualHostName()).remove(mbean);
+ }
+
+ protected void removeQueueMBean(ManagedBean mbean)
+ {
+ _queues.get(mbean.getVirtualHostName()).remove(mbean);
+ }
+
+ public List<ManagedBean> getConnections(String virtualHost)
+ {
+ return _connections.get(virtualHost);
+ }
+
+ public List<ManagedBean> getExchanges(String virtualHost)
+ {
+ return _exchanges.get(virtualHost);
+ }
+
+ public List<ManagedBean> getQueues(String virtualHost)
+ {
+ return _queues.get(virtualHost);
+ }
+
+ public void addVirtualHost(String name)
+ {
+ if (!_virtualHosts.contains(name))
+ {
+ _virtualHosts.add(name);
+ }
+ }
+
+ public List<String> getVirtualHosts()
+ {
+ return _virtualHosts;
+ }
+
+ public abstract void setUserList(List<String> list);
+
+ public abstract List<String> getUsernames();
+
+ public abstract void addManagedObject(ManagedBean key);
+
+ public abstract List<ManagedBean> getMBeans();
+
+ public abstract void removeManagedObject(ManagedBean mbean);
+
+ public abstract List<ManagedBean> getObjectsToBeRemoved();
+
+ public abstract ManagedAttributeModel getAttributeModel(ManagedBean mbean);
+
+ public abstract Object getServerConnection();
+
+ public abstract void closeServerConnection() throws Exception;
+
+ public abstract OperationDataModel getOperationModel(ManagedBean mbean);
+
+ public abstract List<String> getQueueNames(String vistualHostName);
+
+ public abstract String[] getExchangeNames(String vistualHostName);
+
+ public abstract String[] getConnectionNames(String vistualHostName);
+
+ public abstract List<NotificationObject> getNotifications(ManagedBean mbean);
+
+ public abstract boolean hasSubscribedForNotifications(ManagedBean mbean, String name, String type);
+
+ public abstract void clearNotifications(ManagedBean mbean, List<NotificationObject> list);
+
+ public ClientListener getNotificationListener()
+ {
+ return null;
+ }
+
+ public ClientListener getClientListener()
+ {
+ return null;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java
new file mode 100644
index 0000000000..53aa927299
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java
@@ -0,0 +1,136 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.actions;
+
+import static org.apache.qpid.management.ui.Constants.ERROR_SERVER_CONNECTION;
+
+import java.io.IOException;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ApplicationWorkbenchAdvisor;
+import org.apache.qpid.management.ui.Constants;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.views.NavigationView;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class AbstractAction
+{
+ private NavigationView _navigationView;
+
+ protected IWorkbenchWindow _window;
+
+ public static final String RMI_SASL_ERROR = "non-JRMP server";
+ public static final String SECURITY_FAILURE = "User authentication has failed";
+ public static final String SERVER_UNAVAILABLE = "Qpid server is not running";
+
+ /**
+ * We will cache window object in order to
+ * be able to provide parent shell for the message dialog.
+ * @see IWorkbenchWindowActionDelegate#init
+ */
+ public void init(IWorkbenchWindow window)
+ {
+ this._window = window;
+ if (_window.getShell() != null)
+ {
+ _window.getShell().setImage(ApplicationRegistry.getImage(Constants.CONSOLE_IMAGE));
+ }
+ }
+
+ protected NavigationView getNavigationView()
+ {
+ if (_navigationView == null)
+ {
+ _navigationView = (NavigationView)_window.getActivePage().findView(NavigationView.ID);
+ }
+
+ return _navigationView;
+ }
+
+
+ protected void handleException(Throwable ex, String title, String msg)
+ {
+ MBeanUtility.printStackTrace(ex);
+ if (msg == null)
+ {
+ if (ex instanceof IOException)
+ {
+ if ((ex.getMessage() != null) && (ex.getMessage().indexOf(RMI_SASL_ERROR) != -1))
+ {
+ msg = SECURITY_FAILURE;
+ }
+ else
+ {
+ msg = SERVER_UNAVAILABLE;
+ }
+ }
+ else if (ex instanceof SecurityException)
+ {
+ msg = SECURITY_FAILURE;
+ }
+ else
+ {
+ msg = ex.getMessage();
+ }
+ }
+
+ if ((msg == null) && (ex.getCause() != null))
+ {
+ msg = ex.getCause().getMessage();
+ }
+
+ if (msg == null)
+ {
+ msg = ERROR_SERVER_CONNECTION;
+ }
+
+ if (title == null)
+ {
+ title = ERROR_SERVER_CONNECTION;
+ }
+ IStatus status = new Status(IStatus.ERROR, ApplicationWorkbenchAdvisor.PERSPECTIVE_ID,
+ IStatus.OK, msg, null);
+ ErrorDialog.openError(_window.getShell(), "Error", title, status);
+ }
+
+
+ /**
+ * Selection in the workbench has been changed. We can change the state of the 'real' action here
+ * if we want, but this can only happen after the delegate has been created.
+ * @see IWorkbenchWindowActionDelegate#selectionChanged
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ }
+
+ /**
+ * We can use this method to dispose of any system resources we previously allocated.
+ * @see IWorkbenchWindowActionDelegate#dispose
+ */
+ public void dispose() {
+
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AddServer.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AddServer.java
new file mode 100644
index 0000000000..7a36ca6160
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AddServer.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.management.ui.actions;
+
+import static org.apache.qpid.management.ui.Constants.*;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.exceptions.InfoRequiredException;
+import org.apache.qpid.management.ui.views.NumberVerifyListener;
+import org.apache.qpid.management.ui.views.ViewUtility;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class AddServer extends AbstractAction implements IWorkbenchWindowActionDelegate
+{
+ private static final String[] _domains ={"org.apache.qpid"};
+
+ private String _transport = DEFAULT_PROTOCOL;
+ private String _host;
+ private String _port;
+ private String _domain;
+ private String _user;
+ private String _password;
+
+ private boolean _addServer;
+
+ public AddServer()
+ {
+
+ }
+
+ public void run(IAction action)
+ {
+ if(_window == null)
+ return;
+
+ reset();
+ createAddServerPopup();
+ try
+ {
+ if (_addServer)
+ {
+ getNavigationView().addNewServer(_transport, _host, Integer.parseInt(_port), _domain, _user, _password);
+ }
+ }
+ catch(InfoRequiredException ex)
+ {
+ ViewUtility.popupInfoMessage(ACTION_ADDSERVER, ex.getMessage());
+ }
+ catch (Exception ex)
+ {
+ handleException(ex, null, null);
+ }
+ }
+
+ private void reset()
+ {
+ _addServer = false;
+ _host = null;
+ _port = null;
+ _domain = null;
+ _user = null;
+ _password = null;
+ }
+
+ /**
+ * Creates the shell and then opens the popup where user can enter new connection details.
+ * Connects to the new server and adds the server in the navigation page.
+ * Pops up any error occured in connecting to the new server
+ */
+ private void createAddServerPopup()
+ {
+ Display display = Display.getCurrent();
+ final Shell shell = new Shell(display, SWT.BORDER | SWT.CLOSE);
+ shell.setText(ACTION_ADDSERVER);
+ shell.setImage(ApplicationRegistry.getImage(CONSOLE_IMAGE));
+ shell.setLayout(new GridLayout());
+
+ int x = display.getBounds().width;
+ int y = display.getBounds().height;
+ shell.setBounds(x/3, y/3, 425, 275);
+
+ createWidgets(shell);
+
+ shell.open();
+ _window.getShell().setEnabled(false);
+
+ while (!shell.isDisposed())
+ {
+ if (!display.readAndDispatch())
+ {
+ display.sleep();
+ }
+ }
+
+ // enable the main shell
+ _window.getShell().setEnabled(true);
+ _window.getShell().open();
+ }
+
+ // Creates SWT widgets for the user to add server connection details.
+ // Adds listeners to the widgets to take appropriate action
+ private void createWidgets(final Shell shell)
+ {
+ Composite composite = new Composite(shell, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ GridLayout layout = new GridLayout(2, false);
+ layout.horizontalSpacing = 10;
+ layout.verticalSpacing = 10;
+ layout.marginHeight = 20;
+ layout.marginWidth = 20;
+ composite.setLayout(layout);
+
+ /* Commenting this, as there is only one protocol at the moment.
+ * This can be uncommented and enhanced, if more protocols are added in future
+ Label name = new Label(composite, SWT.NONE);
+ name.setText("Connection Type");
+ GridData layoutData = new GridData(SWT.TRAIL, SWT.TOP, false, false);
+ name.setLayoutData(layoutData);
+
+ final Combo comboTransport = new Combo(composite, SWT.READ_ONLY);
+ comboTransport.setItems(CONNECTION_PROTOCOLS);
+ comboTransport.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ comboTransport.select(0);
+ */
+
+ Label host = new Label(composite, SWT.NONE);
+ host.setText("Host");
+ host.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ final Text textHost = new Text(composite, SWT.BORDER);
+ textHost.setText("");
+ textHost.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ textHost.setFocus();
+
+ Label port = new Label(composite, SWT.NONE);
+ port.setText("Port");
+ port.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ final Text textPort = new Text(composite, SWT.BORDER);
+ textPort.setText("");
+ textPort.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ // Verify if the value entered is numeric
+ textPort.addVerifyListener(new NumberVerifyListener());
+
+
+ Label domain = new Label(composite, SWT.NONE);
+ domain.setText("Domain");
+ domain.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ final Combo comboDomain = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
+ comboDomain.setItems(_domains);
+ comboDomain.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ comboDomain.select(0);
+
+
+ Label user = new Label(composite, SWT.NONE);
+ user.setText(USERNAME);
+ user.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ final Text textUser = new Text(composite, SWT.BORDER);
+ textUser.setText("");
+ textUser.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ Label password = new Label(composite, SWT.NONE);
+ password.setText(PASSWORD);
+ password.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ final Text textPwd = new Text(composite, SWT.BORDER | SWT.SINGLE | SWT.PASSWORD);
+ textPwd.setText("");
+ //textPwd.setEchoChar('*');
+ textPwd.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ Composite buttonsComposite = new Composite(composite, SWT.NONE);
+ buttonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+ buttonsComposite.setLayout(new GridLayout(2, true));
+
+
+ final Button connectButton = new Button(buttonsComposite, SWT.PUSH | SWT.CENTER);
+ connectButton.setText(BUTTON_CONNECT);
+ GridData gridData = new GridData (SWT.TRAIL, SWT.BOTTOM, true, true);
+ gridData.widthHint = 100;
+ connectButton.setLayoutData(gridData);
+ connectButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ connectButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent event)
+ {
+ _host = textHost.getText();
+ if ((_host == null) || (_host.trim().length() == 0))
+ {
+ ViewUtility.popupInfoMessage(ACTION_ADDSERVER, INFO_HOST_ADDRESS);
+ textHost.setText("");
+ textHost.setFocus();
+ return;
+ }
+
+ _port = textPort.getText();
+ if ((_port == null) || (_port.trim().length() == 0))
+ {
+ ViewUtility.popupInfoMessage(ACTION_ADDSERVER, INFO_HOST_PORT);
+ textPort.setText("");
+ textPort.setFocus();
+ return;
+ }
+
+ _user = textUser.getText();
+ if ((_user == null) || (_user.trim().length() == 0))
+ {
+ ViewUtility.popupInfoMessage(ACTION_ADDSERVER, INFO_USERNAME);
+ textUser.setText("");
+ textUser.setFocus();
+ return;
+ }
+
+ _password = textPwd.getText();
+ if (_password == null)
+ {
+ ViewUtility.popupInfoMessage(ACTION_ADDSERVER, INFO_PASSWORD);
+ textPwd.setText("");
+ textPwd.setFocus();
+ return;
+ }
+
+ _domain = comboDomain.getText();
+ _addServer = true;
+ shell.dispose();
+ }
+ });
+
+ final Button cancelButton = new Button(buttonsComposite, SWT.PUSH);
+ cancelButton.setText(BUTTON_CANCEL);
+ gridData = new GridData (SWT.LEAD, SWT.BOTTOM, true, true);
+ gridData.widthHint = 100;
+ cancelButton.setLayoutData(gridData);
+ cancelButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ cancelButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent event)
+ {
+ shell.dispose();
+ }
+ });
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/CloseConnection.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/CloseConnection.java
new file mode 100644
index 0000000000..a3e52149df
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/CloseConnection.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.actions;
+
+import static org.apache.qpid.management.ui.Constants.ACTION_CLOSE;
+import org.apache.qpid.management.ui.exceptions.InfoRequiredException;
+import org.apache.qpid.management.ui.views.NavigationView;
+import org.apache.qpid.management.ui.views.ViewUtility;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class CloseConnection extends AbstractAction implements IWorkbenchWindowActionDelegate
+{
+ public CloseConnection()
+ {
+
+ }
+
+ public void run(IAction action)
+ {
+ if(_window != null)
+ {
+ NavigationView view = (NavigationView)_window.getActivePage().findView(NavigationView.ID);
+ try
+ {
+ view.disconnect();
+ }
+ catch(InfoRequiredException ex)
+ {
+ ViewUtility.popupInfoMessage(ACTION_CLOSE, ex.getMessage());
+ }
+ catch(Exception ex)
+ {
+ handleException(ex, null, null);
+ }
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/EditAttribute.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/EditAttribute.java
new file mode 100644
index 0000000000..d3af3661b0
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/EditAttribute.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.management.ui.actions;
+
+import static org.apache.qpid.management.ui.Constants.ACTION_EDITATTRIBUTE;
+import org.apache.qpid.management.ui.exceptions.InfoRequiredException;
+import org.apache.qpid.management.ui.views.MBeanView;
+import org.apache.qpid.management.ui.views.ViewUtility;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class EditAttribute extends AbstractAction implements IWorkbenchWindowActionDelegate
+{
+ public void run(IAction action)
+ {
+ if(_window != null)
+ {
+ MBeanView view = (MBeanView)_window.getActivePage().findView(MBeanView.ID);
+ try
+ {
+ view.editAttribute();
+ }
+ catch(InfoRequiredException ex)
+ {
+ ViewUtility.popupInfoMessage(ACTION_EDITATTRIBUTE, ex.getMessage());
+ }
+ catch(Exception ex)
+ {
+ handleException(ex, "Attribute could not be edited", null);
+ }
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/ReconnectServer.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/ReconnectServer.java
new file mode 100644
index 0000000000..dd9e792912
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/ReconnectServer.java
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.actions;
+
+import static org.apache.qpid.management.ui.Constants.ACTION_LOGIN;
+import static org.apache.qpid.management.ui.Constants.CONSOLE_IMAGE;
+import static org.apache.qpid.management.ui.Constants.INFO_PASSWORD;
+import static org.apache.qpid.management.ui.Constants.INFO_USERNAME;
+import static org.apache.qpid.management.ui.Constants.PASSWORD;
+import static org.apache.qpid.management.ui.Constants.USERNAME;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.Constants;
+import org.apache.qpid.management.ui.exceptions.InfoRequiredException;
+import org.apache.qpid.management.ui.views.TreeObject;
+import org.apache.qpid.management.ui.views.ViewUtility;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class ReconnectServer extends AbstractAction implements IWorkbenchWindowActionDelegate
+{
+ private String _title;
+ private String _serverName;
+ private String _user;
+ private String _password;
+ private boolean _connect;
+
+ public void run(IAction action)
+ {
+ if(_window == null)
+ return;
+
+ try
+ {
+ reset();
+ // Check if a server node is selected to be reconnected.
+ TreeObject serverNode = getNavigationView().getSelectedServerNode();
+ _serverName = serverNode.getName();
+ _title = ACTION_LOGIN + " (" + _serverName + ")";
+
+ // Get the login details(username/password)
+ createLoginPopup();
+
+ if (_connect)
+ {
+ // Connect the server
+ getNavigationView().reconnect(_user, _password);
+ }
+ }
+ catch(InfoRequiredException ex)
+ {
+ ViewUtility.popupInfoMessage("Reconnect Qpid server", ex.getMessage());
+ }
+ catch (Exception ex)
+ {
+ handleException(ex, null, null);
+ }
+ }
+
+ private void reset()
+ {
+ _connect = false;
+ _user = null;
+ _password = null;
+ }
+
+ // Create the login popup fot th user to enter usernaem and password
+ private void createLoginPopup()
+ {
+ Display display = Display.getCurrent();
+ final Shell shell = new Shell(display, SWT.BORDER | SWT.CLOSE);
+ shell.setText(_title);
+ shell.setImage(ApplicationRegistry.getImage(CONSOLE_IMAGE));
+ shell.setLayout(new GridLayout());
+
+ int x = display.getBounds().width;
+ int y = display.getBounds().height;
+ shell.setBounds(x/3, y/3, 350, 200);
+
+ createWidgets(shell);
+
+ shell.open();
+ _window.getShell().setEnabled(false);
+
+ while (!shell.isDisposed())
+ {
+ if (!display.readAndDispatch())
+ {
+ display.sleep();
+ }
+ }
+
+ // enable the main shell
+ _window.getShell().setEnabled(true);
+ _window.getShell().open();
+ }
+
+ // Creates the SWT widgets in the popup shell, to enter username and password.
+ // Adds listeners to the widgets to take appropriate action
+ private void createWidgets(final Shell shell)
+ {
+ Composite composite = new Composite(shell, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ GridLayout layout = new GridLayout(2, false);
+ layout.horizontalSpacing = 10;
+ layout.verticalSpacing = 10;
+ layout.marginHeight = 20;
+ layout.marginWidth = 20;
+ composite.setLayout(layout);
+
+ Label user = new Label(composite, SWT.NONE);
+ user.setText(USERNAME);
+ user.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ final Text textUser = new Text(composite, SWT.BORDER);
+ textUser.setText("");
+ textUser.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ // Put cursor on this field
+ textUser.setFocus();
+
+ Label password = new Label(composite, SWT.NONE);
+ password.setText(PASSWORD);
+ password.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ final Text textPwd = new Text(composite, SWT.BORDER | SWT.SINGLE | SWT.PASSWORD);
+ textPwd.setText("");
+ textPwd.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ Composite buttonsComposite = new Composite(composite, SWT.NONE);
+ buttonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+ buttonsComposite.setLayout(new GridLayout(2, true));
+
+
+ final Button connectButton = new Button(buttonsComposite, SWT.PUSH | SWT.CENTER);
+ connectButton.setText(Constants.BUTTON_CONNECT);
+ GridData gridData = new GridData (SWT.TRAIL, SWT.BOTTOM, true, true);
+ gridData.widthHint = 100;
+ connectButton.setLayoutData(gridData);
+ connectButton.setFont(ApplicationRegistry.getFont(Constants.FONT_BUTTON));
+ connectButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent event)
+ {
+ _user = textUser.getText();
+ if ((_user == null) || (_user.trim().length() == 0))
+ {
+ ViewUtility.popupInfoMessage(_title, INFO_USERNAME);
+ textUser.setText("");
+ textUser.setFocus();
+ return;
+ }
+
+ _password = textPwd.getText();
+ if (_password == null)
+ {
+ ViewUtility.popupInfoMessage(_title, INFO_PASSWORD);
+ textPwd.setText("");
+ textPwd.setFocus();
+ return;
+ }
+
+ _connect = true;
+ shell.dispose();
+ }
+ });
+
+ final Button cancelButton = new Button(buttonsComposite, SWT.PUSH);
+ cancelButton.setText(Constants.BUTTON_CANCEL);
+ gridData = new GridData (SWT.LEAD, SWT.BOTTOM, true, true);
+ gridData.widthHint = 100;
+ cancelButton.setLayoutData(gridData);
+ cancelButton.setFont(ApplicationRegistry.getFont(Constants.FONT_BUTTON));
+ cancelButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent event)
+ {
+ shell.dispose();
+ }
+ });
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/Refresh.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/Refresh.java
new file mode 100644
index 0000000000..34251c12d7
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/Refresh.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.actions;
+
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.views.MBeanView;
+import org.apache.qpid.management.ui.views.NavigationView;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+/**
+ * This action refreshes both the views -Navigation and MBeanView
+ * @author Bhupendra Bhardwaj
+ */
+public class Refresh extends AbstractAction implements IWorkbenchWindowActionDelegate
+{
+ public void run(IAction action)
+ {
+ if(_window != null)
+ {
+ NavigationView view = (NavigationView)_window.getActivePage().findView(NavigationView.ID);
+ view.refresh();
+
+ MBeanView mbeanview = (MBeanView)_window.getActivePage().findView(MBeanView.ID);
+ try
+ {
+ mbeanview.refreshMBeanView();
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/RemoveServer.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/RemoveServer.java
new file mode 100644
index 0000000000..e329255414
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/RemoveServer.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.management.ui.actions;
+
+import org.apache.qpid.management.ui.exceptions.InfoRequiredException;
+import org.apache.qpid.management.ui.views.NavigationView;
+import org.apache.qpid.management.ui.views.ViewUtility;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class RemoveServer extends AbstractAction implements IWorkbenchWindowActionDelegate
+{
+ public void run(IAction action)
+ {
+ if(_window != null)
+ {
+ NavigationView view = (NavigationView)_window.getActivePage().findView(NavigationView.ID);
+ try
+ {
+ view.removeServer();
+ }
+ catch(InfoRequiredException ex)
+ {
+ ViewUtility.popupInfoMessage("Remove Qpid server", ex.getMessage());
+ }
+ catch(Exception ex)
+ {
+ handleException(ex, "Server could not be removed", null);
+ }
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/VersionAction.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/VersionAction.java
new file mode 100644
index 0000000000..11db02f5a2
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/VersionAction.java
@@ -0,0 +1,89 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.actions;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.apache.qpid.management.ui.Constants;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.ui.IWorkbenchWindow;
+
+public class VersionAction extends Action
+{
+ private IWorkbenchWindow _window;
+ public static final String VERSION_RESOURCE = "qpidversion.properties";
+
+ public static final String PRODUCT_NAME_PROPERTY = "qpid.name";
+ public static final String RELEASE_VERSION_PROPERTY = "qpid.version";
+ public static final String BUILD_VERSION_PROPERTY = "qpid.svnversion";
+
+ private static final String DEFAULT = "unknown";
+ private static String _releaseVersion;
+ private static String _buildVersion;
+ private static String _text;
+
+ static
+ {
+ Properties props = new Properties();
+ try
+ {
+ InputStream propertyStream = VersionAction.class.getClassLoader().getResourceAsStream(VERSION_RESOURCE);
+ if (propertyStream != null)
+ {
+ props.load(propertyStream);
+ _releaseVersion = readPropertyValue(props, RELEASE_VERSION_PROPERTY);
+ _buildVersion = readPropertyValue(props, BUILD_VERSION_PROPERTY);
+ _text = "Build Version : " + _buildVersion + "\n" +
+ "Release Version : " + _releaseVersion;
+ }
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.printStackTrace(ex);
+ }
+ }
+
+ public VersionAction(IWorkbenchWindow window)
+ {
+ _window = window;
+ setText("About " + Constants.APPLICATION_NAME);
+ setId("qpidmc.about");
+ setActionDefinitionId("qpidmc.about");
+ }
+
+ private static String readPropertyValue(Properties props, String propertyName)
+ {
+ String retVal = (String) props.get(propertyName);
+ if (retVal == null)
+ {
+ retVal = DEFAULT;
+ }
+ return retVal;
+ }
+
+ public void run()
+ {
+ MessageDialog.openInformation(_window.getShell(), Constants.APPLICATION_NAME, _text);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/InfoRequiredException.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/InfoRequiredException.java
new file mode 100644
index 0000000000..672426a59d
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/InfoRequiredException.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.exceptions;
+
+public class InfoRequiredException extends Exception
+{
+ private static final long serialVersionUID = 1L;
+
+ public InfoRequiredException(String message)
+ {
+ super(message);
+ }
+
+ public InfoRequiredException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java
new file mode 100644
index 0000000000..17c127c01a
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.management.ui.exceptions;
+
+@SuppressWarnings("serial")
+public class ManagementConsoleException extends Exception
+{
+ public ManagementConsoleException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java
new file mode 100644
index 0000000000..2be0ddbebf
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java
@@ -0,0 +1,77 @@
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */package org.apache.qpid.management.ui.jmx;
+
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectionNotification;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedServer;
+
+
+public class ClientListener implements NotificationListener
+{
+ protected ManagedServer server = null;
+ protected JMXServerRegistry serverRegistry = null;
+
+ public ClientListener(ManagedServer server)
+ {
+ this.server = server;
+ serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(server);
+ }
+
+ public void handleNotification(Notification notification, Object handback)
+ {
+ ObjectName objName = null;
+ String type = notification.getType();
+ MBeanUtility.printOutput(type + ":" + objName);
+
+ if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(type))
+ {
+ objName = ((MBeanServerNotification)notification).getMBeanName();
+ getServerRegistry().registerManagedObject(objName);
+ }
+ else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(type))
+ {
+ objName = ((MBeanServerNotification)notification).getMBeanName();
+ getServerRegistry().unregisterManagedObject(objName);
+ }
+ else if (JMXConnectionNotification.FAILED.equals(type))
+ {
+ ApplicationRegistry.serverConnectionClosed(server);
+ }
+ }
+
+ protected JMXServerRegistry getServerRegistry()
+ {
+ if (serverRegistry == null)
+ serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(server);
+
+ return serverRegistry;
+ }
+ public ManagedServer getServer()
+ {
+ return server;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientNotificationListener.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientNotificationListener.java
new file mode 100644
index 0000000000..c6ecda4b4c
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientNotificationListener.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.jmx;
+
+import javax.management.Notification;
+import javax.management.ObjectName;
+
+import org.apache.qpid.management.ui.ManagedServer;
+
+public class ClientNotificationListener extends ClientListener
+{
+ public ClientNotificationListener(ManagedServer server)
+ {
+ super(server);
+ }
+
+ public void handleNotification(Notification notification, Object handback)
+ {
+ ObjectName objName = (ObjectName)notification.getSource();
+ //String type = notification.getType();
+ getServerRegistry().addNotification(objName, notification);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java
new file mode 100644
index 0000000000..3561e16098
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.jmx;
+
+import java.util.HashMap;
+
+import javax.management.ObjectName;
+
+import org.apache.qpid.management.ui.ManagedBean;
+
+
+public class JMXManagedObject extends ManagedBean
+{
+ private ObjectName _objName;
+
+ @SuppressWarnings("unchecked")
+ public JMXManagedObject(ObjectName objName)
+ {
+ super();
+ this._objName = objName;
+ setUniqueName(_objName.toString());
+ setDomain(_objName.getDomain());
+ super.setProperties(new HashMap(_objName.getKeyPropertyList()));
+ }
+
+ public ObjectName getObjectName()
+ {
+ return _objName;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java
new file mode 100644
index 0000000000..f671a1dc9a
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java
@@ -0,0 +1,628 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.jmx;
+
+import static org.apache.qpid.management.ui.Constants.*;
+
+import java.lang.reflect.Constructor;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.SaslClientFactory;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ManagedServer;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.apache.qpid.management.ui.model.ManagedAttributeModel;
+import org.apache.qpid.management.ui.model.NotificationInfoModel;
+import org.apache.qpid.management.ui.model.NotificationObject;
+import org.apache.qpid.management.ui.model.OperationDataModel;
+import org.apache.qpid.management.ui.sasl.JCAProvider;
+import org.apache.qpid.management.ui.sasl.SaslProvider;
+import org.apache.qpid.management.ui.sasl.UserPasswordCallbackHandler;
+import org.apache.qpid.management.ui.sasl.UsernameHashedPasswordCallbackHandler;
+
+
+public class JMXServerRegistry extends ServerRegistry
+{
+ private boolean _connected = false;
+ private ObjectName _serverObjectName = null;
+ private Map<String, Object> _env = null;
+ private JMXServiceURL _jmxUrl = null;
+ private JMXConnector _jmxc = null;
+ private MBeanServerConnection _mbsc = null;
+ private Exception _connectionException = null;
+
+ private List<String> _usersList;
+ // When an mbean gets removed from mbean server, then the notification listener
+ // will add that mbean in this list.
+ private List<ManagedBean> _mbeansToBeRemoved = new ArrayList<ManagedBean>();
+
+ // Map containing all managed beans and mapped with unique mbean name
+ private HashMap<String, ManagedBean> _mbeansMap = new HashMap<String, ManagedBean>();
+ // Map containing MBeanInfo for all mbeans and mapped with unique mbean name
+ private HashMap<String, MBeanInfo> _mbeanInfoMap = new HashMap<String, MBeanInfo>();
+ // Map containing attribute model for all mbeans and mapped with unique mbean name
+ private HashMap<String, ManagedAttributeModel> _attributeModelMap = new HashMap<String, ManagedAttributeModel>();
+ // Map containing operation model for all mbeans and mapped with unique mbean name
+ private HashMap<String, OperationDataModel> _operationModelMap = new HashMap<String, OperationDataModel>();
+ // Map containing NotificationInfo for all mbeans and mapped with unique mbean name
+ private HashMap<String, List<NotificationInfoModel>> _notificationInfoMap = new HashMap<String, List<NotificationInfoModel>>();
+ // Map containing all notifications sent for all mbeans, which are registered for notification
+ private HashMap<String, List<NotificationObject>> _notificationsMap = new HashMap<String, List<NotificationObject>>();
+ // For mbeans which have subscribed for a notification type
+ // mbean unique name mapped with notification map. Notification map contains list of notification type
+ // mapped with notification name. Notification type list contains those notification types,
+ // which are subscribed for notification.
+ private HashMap<String, HashMap<String, List<String>>> _subscribedNotificationMap = new HashMap<String, HashMap<String, List<String>>>();
+
+ // listener for registration or unregistratioj of mbeans on mbean server
+ private ClientNotificationListener _notificationListener = null;
+ // listener for server connection. Receives notification if server connection goes down
+ private ClientListener _clientListener = null;
+
+ public JMXServerRegistry(ManagedServer server) throws Exception
+ {
+ super(server);
+ String securityMechanism = ApplicationRegistry.getSecurityMechanism();
+ String connectorClassName = ApplicationRegistry.getJMXConnectorClass();
+
+ if ((securityMechanism != null) && (connectorClassName != null))
+ {
+ createSASLConnector(securityMechanism, connectorClassName);
+ }
+ else
+ {
+ _jmxUrl = new JMXServiceURL(server.getUrl());
+ _jmxc = JMXConnectorFactory.connect(_jmxUrl, null);
+ }
+
+ _mbsc = _jmxc.getMBeanServerConnection();
+
+ _clientListener = new ClientListener(server);
+ _notificationListener = new ClientNotificationListener(server);
+
+ _jmxc.addConnectionNotificationListener(_clientListener, null, null);
+ _serverObjectName = new ObjectName("JMImplementation:type=MBeanServerDelegate");
+ _mbsc.addNotificationListener(_serverObjectName, _clientListener, null, null);
+ }
+
+ public MBeanServerConnection getServerConnection()
+ {
+ return _mbsc;
+ }
+
+ private void createSASLConnector(String mech, String className) throws Exception
+ {
+ String text = "Security mechanism " + mech + " is not supported.";
+ // Check if the given connector, which supports SASL is available
+ Class connectorClass = Class.forName(className);
+
+ _jmxUrl = new JMXServiceURL("jmxmp", getManagedServer().getHost(), getManagedServer().getPort());
+ _env = new HashMap<String, Object>();
+ CallbackHandler handler;
+ if (MECH_CRAMMD5.equals(mech))
+ {
+ // For SASL/CRAM-MD5
+ Map<String, Class<? extends SaslClientFactory>> map = new HashMap<String, Class<? extends SaslClientFactory>>();
+ Class<?> clazz = Class.forName("org.apache.qpid.management.ui.sasl.CRAMMD5HashedSaslClientFactory");
+ map.put("CRAM-MD5-HASHED", (Class<? extends SaslClientFactory>) clazz);
+
+ Security.addProvider(new JCAProvider(map));
+ handler = new UsernameHashedPasswordCallbackHandler(getManagedServer().getUser(),
+ getManagedServer().getPassword());
+ _env.put("jmx.remote.profiles", SASL_CRAMMD5);
+ _env.put("jmx.remote.sasl.callback.handler", handler);
+
+ }
+ else if (MECH_PLAIN.equals(mech))
+ {
+ // For SASL/PLAIN
+ Security.addProvider(new SaslProvider());
+ handler = new UserPasswordCallbackHandler(getManagedServer().getUser(), getManagedServer().getPassword());
+ _env.put("jmx.remote.profiles", SASL_PLAIN);
+ _env.put("jmx.remote.sasl.callback.handler", handler);
+ }
+ else
+ {
+ MBeanUtility.printOutput(text);
+ throw new Exception(text);
+ }
+ // Now create the instance of JMXMPConnector
+ Class[] paramTypes = {JMXServiceURL.class, Map.class};
+ Constructor cons = connectorClass.getConstructor(paramTypes);
+
+ Object[] args = {_jmxUrl, _env};
+ Object theObject = cons.newInstance(args);
+
+ _jmxc = (JMXConnector)theObject;
+
+ Thread connectorThread = new Thread(new ConnectorThread());
+ connectorThread.start();
+ long timeNow = System.currentTimeMillis();
+ connectorThread.join(ApplicationRegistry.timeout);
+
+ if (_connectionException != null)
+ {
+ throw _connectionException;
+ }
+ if (!_connected)
+ {
+ if (System.currentTimeMillis() - timeNow >= ApplicationRegistry.timeout)
+ throw new Exception("Qpid server connection timed out");
+ else
+ throw new Exception("Qpid server connection failed");
+ }
+ }
+
+ private class ConnectorThread implements Runnable
+ {
+ public void run()
+ {
+ try
+ {
+ _connected = false;
+ _connectionException = null;
+
+ _jmxc.connect();
+ _connected = true;
+ }
+ catch (Exception ex)
+ {
+ _connectionException = ex;
+ MBeanUtility.printStackTrace(ex);
+ }
+ }
+ }
+ /**
+ * removes all listeners from the mbean server. This is required when user
+ * disconnects the Qpid server connection
+ */
+ public void closeServerConnection() throws Exception
+ {
+ try
+ {
+ if (_jmxc != null && _clientListener != null)
+ _jmxc.removeConnectionNotificationListener(_clientListener);
+
+ if (_mbsc != null && _clientListener != null)
+ _mbsc.removeNotificationListener(_serverObjectName, _clientListener);
+
+ // remove mbean notification listeners
+ for (String mbeanName : _subscribedNotificationMap.keySet())
+ {
+ _mbsc.removeNotificationListener(new ObjectName(mbeanName), _notificationListener);
+ }
+ }
+ catch (ListenerNotFoundException ex)
+ {
+ MBeanUtility.printOutput(ex.toString());
+ }
+ }
+
+ public ManagedBean getManagedObject(String uniqueName)
+ {
+ return _mbeansMap.get(uniqueName);
+ }
+
+ public void addManagedObject(ManagedBean mbean)
+ {
+ if (mbean.isQueue())
+ {
+ addQueueMBean(mbean);
+ }
+ else if (mbean.isExchange())
+ {
+ addExchangeMBean(mbean);
+ }
+ else if (mbean.isConnection())
+ {
+ addConnectionMBean(mbean);
+ }
+
+ addVirtualHost(mbean.getVirtualHostName());
+ _mbeansMap.put(mbean.getUniqueName(), mbean);
+ }
+
+ public void removeManagedObject(ManagedBean mbean)
+ {
+ MBeanUtility.printOutput("Removing MBean:" + mbean.getUniqueName());
+
+ if (mbean.isQueue())
+ {
+ removeQueueMBean(mbean);
+ }
+ else if (mbean.isExchange())
+ {
+ removeExchangeMBean(mbean);
+ }
+ else if (mbean.isConnection())
+ {
+ removeConnectionMBean(mbean);
+ }
+
+ _mbeansMap.remove(mbean.getUniqueName());
+ }
+
+ public void putMBeanInfo(ManagedBean mbean, MBeanInfo mbeanInfo)
+ {
+ _mbeanInfoMap.put(mbean.getUniqueName(), mbeanInfo);
+ }
+ public MBeanInfo getMBeanInfo(ManagedBean mbean)
+ {
+ return _mbeanInfoMap.get(mbean.getUniqueName());
+ }
+
+ public List<ManagedBean> getMBeans()
+ {
+ return new ArrayList<ManagedBean>(_mbeansMap.values());
+ }
+
+ public void setNotificationInfo(ManagedBean mbean, List<NotificationInfoModel>value)
+ {
+ _notificationInfoMap.put(mbean.getUniqueName(), value);
+ }
+
+ public List<NotificationInfoModel> getNotificationInfo(ManagedBean mbean)
+ {
+ return _notificationInfoMap.get(mbean.getUniqueName());
+ }
+
+ public void addNotification(ObjectName objName, Notification notification)
+ {
+ List<NotificationObject> list = _notificationsMap.get(objName.toString());
+ NotificationObject obj = new NotificationObject(notification.getSequenceNumber(),
+ new Date(notification.getTimeStamp()),
+ notification.getMessage(),
+ notification.getSource(),
+ notification.getType());
+
+ if (list == null)
+ {
+ list = new ArrayList<NotificationObject>();
+ _notificationsMap.put(objName.toString(), list);
+ }
+
+ list.add(obj);
+ }
+
+ /**
+ * Returns all the notification objects for a given mbean. If mbean is null, it returns
+ * notification objects for all the mbeans.
+ */
+ public List<NotificationObject> getNotifications(ManagedBean mbean)
+ {
+ if (mbean == null)
+ {
+ List<NotificationObject> totalList = new ArrayList<NotificationObject>();
+ for (List<NotificationObject> list : _notificationsMap.values())
+ {
+ totalList.addAll(list);
+ }
+ return totalList;
+ }
+ else
+ {
+ return _notificationsMap.get(mbean.getUniqueName());
+ }
+ }
+
+ public void clearNotifications(ManagedBean mbean, List<NotificationObject> list)
+ {
+ if (mbean == null)
+ {
+ if (list == null || list.isEmpty())
+ {
+ // All notifications of all mbeans to be cleared
+ _notificationsMap.clear();
+ }
+ else
+ {
+ // Clear the selected notifications
+ for (NotificationObject obj : list)
+ {
+ mbean = _mbeansMap.get(obj.getSource().toString());
+ List<NotificationObject> nList = _notificationsMap.get(mbean.getUniqueName());
+ if (nList != null && !nList.isEmpty())
+ {
+ nList.remove(obj);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (list == null || list.isEmpty())
+ {
+ // All notifications of this mbean to be cleared
+ List<NotificationObject> nList = _notificationsMap.get(mbean.getUniqueName());
+ if (nList != null && !nList.isEmpty())
+ {
+ nList.clear();
+ }
+ }
+ else
+ {
+ // Clear the selected notifications
+ for (NotificationObject obj : list)
+ {
+ List<NotificationObject> nList = _notificationsMap.get(mbean.getUniqueName());
+ if (nList != null && !nList.isEmpty())
+ {
+ nList.remove(obj);
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Adds notification name and type to the map. The map contains all the notification names,
+ * subscribed for an mbean.
+ * @param mbean
+ * @param name
+ * @param type
+ */
+ public void addNotificationListener(ManagedBean mbean, String name, String type)
+ {
+ // Get the subscribed notifications map for given mbean. If map is null then create a new one.
+ HashMap<String, List<String>> map = _subscribedNotificationMap.get(mbean.getUniqueName());
+ if (map == null)
+ {
+ map = new HashMap<String, List<String>>();
+ _subscribedNotificationMap.put(mbean.getUniqueName(),map);
+ }
+
+ // Get the list of notification types for given notification name. If null, then create a new list.
+ List<String> list = map.get(name);
+ if (list == null)
+ {
+ list = new ArrayList<String>();
+ map.put(name, list);
+ }
+ // Now add the notification type to the list
+ if (ALL.equals(type))
+ {
+ List<NotificationInfoModel> infoList = _notificationInfoMap.get(mbean.getUniqueName());
+ for (NotificationInfoModel model : infoList)
+ {
+ if (model.getName().equals(name))
+ {
+ String[] types = model.getTypes();
+ for (int i = 0; i < types.length; i++)
+ {
+ list.add(types[i]);
+ }
+ }
+ }
+ }
+ else
+ {
+ list.add(type);
+ }
+
+ //System.out.println("Subscribed for notification :" + mbean.getUniqueName());
+ }
+
+ /**
+ * Checks if the given notification name and type are subscribed for the mbean.
+ */
+ public boolean hasSubscribedForNotifications(ManagedBean mbean, String name, String type)
+ {
+ if (_subscribedNotificationMap.containsKey(mbean.getUniqueName()))
+ {
+ HashMap<String, List<String>> map = _subscribedNotificationMap.get(mbean.getUniqueName());
+ if (map.containsKey(name))
+ {
+ if (map.get(name).contains(type))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Clears the notification name and type information from the subscribed notifications map
+ * and removes the listener from mbeanserver connection
+ * @param mbean
+ * @param name
+ * @param type
+ * @throws Exception
+ */
+ public void removeNotificationListener(ManagedBean mbean, String name, String type) throws Exception
+ {
+ //System.out.println("Removed notification listener :" + mbean.getUniqueName() + name +type);
+ if (_subscribedNotificationMap.containsKey(mbean.getUniqueName()))
+ {
+ // get the notifications map. This map contains the notification name mapped with the notification types
+ HashMap<String, List<String>> map = _subscribedNotificationMap.get(mbean.getUniqueName());
+ if (map.containsKey(name))
+ {
+ if (ALL.equals(type))
+ {
+ map.remove(name);
+ }
+ else if (type != null)
+ {
+ map.get(name).remove(type);
+ if (map.get(name).isEmpty())
+ {
+ map.remove(name);
+ }
+ }
+ }
+ if (map.size() == 0)
+ {
+ _subscribedNotificationMap.remove(mbean.getUniqueName());
+ }
+
+ JMXManagedObject jmxbean = (JMXManagedObject)mbean;
+ _mbsc.removeNotificationListener(jmxbean.getObjectName(), _notificationListener);
+ }
+ }
+
+ /**
+ * When the mbean registration request is received from the mbean server, then the client listener
+ * can use this method. It will add the mbean to a list, which will be used to add the mbean to
+ * the registry and gui
+ * @param objName
+ */
+ public void registerManagedObject(ObjectName objName)
+ {
+ JMXManagedObject managedObject = new JMXManagedObject(objName);
+ managedObject.setServer(getManagedServer());
+ addManagedObject(managedObject);
+ }
+
+ /**
+ * When mbean unregistration notification is received from the mbean server, then client listener
+ * can invoke this method. It will add the mbean to the list of mbeans to be removed from registry
+ * @param objName
+ */
+ public void unregisterManagedObject(ObjectName objName)
+ {
+ ManagedBean mbean = _mbeansMap.get(objName.toString());
+ removeManagedObject(mbean);
+ // Check if mbean was not available in the map. It can happen if mbean unregistration
+ // notification is received and the mbean is not added in the map.
+ if (mbean != null)
+ {
+ _mbeansToBeRemoved.add(mbean);
+ }
+ }
+
+ public List<ManagedBean> getObjectsToBeRemoved()
+ {
+ if (_mbeansToBeRemoved.isEmpty())
+ return null;
+ else
+ {
+ List<ManagedBean> list = new CopyOnWriteArrayList<ManagedBean>(_mbeansToBeRemoved);
+ _mbeansToBeRemoved.clear();
+ return list;
+ }
+ }
+
+ public void setAttributeModel(ManagedBean mbean, ManagedAttributeModel value)
+ {
+ _attributeModelMap.put(mbean.getUniqueName(), value);
+ }
+
+ public ManagedAttributeModel getAttributeModel(ManagedBean mbean)
+ {
+ return _attributeModelMap.get(mbean.getUniqueName());
+ }
+
+ public void setOperationModel(ManagedBean mbean, OperationDataModel value)
+ {
+ _operationModelMap.put(mbean.getUniqueName(), value);
+ }
+
+ public OperationDataModel getOperationModel(ManagedBean mbean)
+ {
+ return _operationModelMap.get(mbean.getUniqueName());
+ }
+
+ public List<String> getQueueNames(String virtualHostName)
+ {
+ List<ManagedBean> list = getQueues(virtualHostName);
+ if (list == null)
+ return null;
+
+ List<String> queueNames = new ArrayList<String>();
+ for (ManagedBean mbean : list)
+ {
+ queueNames.add(mbean.getName());
+ }
+ return queueNames;
+ }
+
+ public String[] getExchangeNames(String virtualHostName)
+ {
+ List<ManagedBean> list = getExchanges(virtualHostName);
+ if (list == null)
+ return null;
+
+ String[] exchanges = new String[list.size()];
+ int i = 0;
+ for (ManagedBean mbean : list)
+ {
+ exchanges[i++] = mbean.getName();
+ }
+ return exchanges;
+ }
+
+ public String[] getConnectionNames(String virtualHostName)
+ {
+ List<ManagedBean> list = getExchanges(virtualHostName);
+ if (list == null)
+ return null;
+
+ String[] connections = new String[list.size()];
+ int i = 0;
+ for (ManagedBean mbean : list)
+ {
+ connections[i++] = mbean.getName();
+ }
+ return connections;
+ }
+
+ public void setUserList(List<String> list)
+ {
+ _usersList = list;
+ Collections.sort(_usersList);
+ }
+
+ public List<String> getUsernames()
+ {
+ return _usersList;
+ }
+
+ public ClientNotificationListener getNotificationListener()
+ {
+ return _notificationListener;
+ }
+
+ public ClientListener getClientListener()
+ {
+ return _clientListener;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java
new file mode 100644
index 0000000000..2d1883533b
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java
@@ -0,0 +1,466 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.jmx;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ManagedServer;
+import org.apache.qpid.management.ui.exceptions.ManagementConsoleException;
+import org.apache.qpid.management.ui.model.AttributeData;
+import org.apache.qpid.management.ui.model.ManagedAttributeModel;
+import org.apache.qpid.management.ui.model.NotificationInfoModel;
+import org.apache.qpid.management.ui.model.OperationData;
+import org.apache.qpid.management.ui.model.OperationDataModel;
+import org.apache.qpid.management.ui.model.ParameterData;
+import org.apache.qpid.management.ui.views.ViewUtility;
+
+/**
+ * Utility class for all mbeanserver related operations. Keeps all JMX code out from view and model classes
+ * @author Bhupendra Bhardwaj
+ */
+public class MBeanUtility
+{
+ public static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
+ public static final BigInteger MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE);
+ /**
+ * Retrieves the MBeanInfo from MBeanServer and stores in the application registry
+ * @param mbean managed bean
+ * @return MBeanInfo
+ * @throws Exception, if server connection is null or if server throws Exception
+ */
+ public static MBeanInfo getMBeanInfo(ManagedBean mbean) throws Exception
+ {
+ ManagedServer server = mbean.getServer();
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(server);
+
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+ if (mbsc == null)
+ {
+ throw new ManagementConsoleException("Server connection is broken");
+ }
+
+ JMXManagedObject jmxbean = (JMXManagedObject)mbean;
+ MBeanInfo mbeanInfo = mbsc.getMBeanInfo(jmxbean.getObjectName());
+ serverRegistry.putMBeanInfo(mbean, mbeanInfo);
+
+ // populate the server registry with attribute and operation info
+ getAttributes(mbean);
+ getOperations(mbean);
+
+ return mbeanInfo;
+ }
+
+ /**
+ * executes the MBean operation
+ * @param mbean
+ * @param opData
+ * @return MBean operation return value
+ * @throws Exception if server connection is broken or if operation execution fails on the mbean server
+ */
+ public static Object execute(ManagedBean mbean, OperationData opData) throws Exception
+ {
+ String opName = opData.getName();
+ Object[] values = null;
+ String[] signature = null;
+
+ List<ParameterData> params = opData.getParameters();
+ if (params != null && !params.isEmpty())
+ {
+ signature = new String[params.size()];;
+ values = new Object[params.size()];
+ for (int i = 0; i < params.size(); i++)
+ {
+ signature[i] = params.get(i).getType();
+ values[i] = params.get(i).getValue();
+ }
+ }
+
+ ManagedServer server = mbean.getServer();
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(server);
+
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+ if (mbsc == null)
+ {
+ throw new ManagementConsoleException("Server connection is broken");
+ // TODO
+ // try and get the connection again if it was disconnected
+ }
+ JMXManagedObject jmxbean = (JMXManagedObject)mbean;
+ return mbsc.invoke(jmxbean.getObjectName(), opName, values, signature);
+ }
+
+ /**
+ * @see MBeanUtility#handleException(ManagedBean, Exception)
+ */
+ public static void handleException(Exception ex)
+ {
+ handleException(null, ex);
+ }
+
+ /**
+ * handels the exception received. Shows the exception to the user in best suitable way
+ * @param mbean managed bean
+ * @param ex Exception
+ */
+ public static void handleException(ManagedBean mbean, Throwable ex)
+ {
+ if (mbean == null)
+ {
+ ViewUtility.popupErrorMessage("Error", "Managed Object is null \n" + ex.toString());
+ printStackTrace(ex);
+ }
+ else if (ex instanceof ReflectionException)
+ {
+ ViewUtility.popupErrorMessage(mbean.getInstanceName(), "Server has thrown error \n" + ex.toString());
+ printStackTrace(ex);
+ }
+ else if (ex instanceof InstanceNotFoundException)
+ {
+ ViewUtility.popupErrorMessage(mbean.getInstanceName(), "Managed Object Not Found \n" + ex.toString());
+ printStackTrace(ex);
+ }
+ else if (ex instanceof MBeanException)
+ {
+ String cause = ((MBeanException)ex).getTargetException().getMessage();
+ if (cause == null)
+ cause = ex.toString();
+ ViewUtility.popupInfoMessage(mbean.getInstanceName(), cause);
+ }
+ else if (ex instanceof JMException)
+ {
+ ViewUtility.popupErrorMessage(mbean.getInstanceName(), "Management Exception occured \n" + ex.toString());
+ }
+ else if (ex instanceof ManagementConsoleException)
+ {
+ ViewUtility.popupErrorMessage(mbean.getInstanceName(), ex.getMessage());
+ }
+ else
+ {
+ if (ex.getCause() != null)
+ {
+ handleException(mbean, ex.getCause());
+ }
+ else
+ {
+ String msg = ex.getMessage();
+ if (msg == null)
+ {
+ msg = ex.toString();
+ }
+ ViewUtility.popupErrorMessage(mbean.getInstanceName(), msg);
+ printStackTrace(ex);
+ }
+ }
+
+ }
+
+ /**
+ * Registers the notification listener with the MBeanServer
+ * @param mbean managed bean
+ * @param name notification name
+ * @param type notification type
+ * @throws Exception if server connection is broken or if listener could not be created
+ */
+ public static void createNotificationlistener(ManagedBean mbean, String name, String type)
+ throws Exception
+ {
+ JMXManagedObject jmxbean = (JMXManagedObject)mbean;
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ serverRegistry.addNotificationListener(mbean, name, type);
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+
+ if (mbsc == null)
+ {
+ throw new ManagementConsoleException("Server connection is broken");
+ }
+ mbsc.addNotificationListener(jmxbean.getObjectName(), serverRegistry.getNotificationListener(), null, null);
+ }
+
+ public static void removeNotificationListener(ManagedBean mbean, String name, String type) throws Exception
+ {
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ serverRegistry.removeNotificationListener(mbean, name, type);
+ }
+
+ /**
+ * Checks if the server registry contains attribute information for this mbean. If not then it queries the
+ * mbean server for complete mbean information, else it gets the latest value of the given attribute
+ * from mbean server.
+ * @return attribute data for the given mbean attribute
+ */
+ public static AttributeData getAttributeData(ManagedBean mbean, String attribute) throws Exception
+ {
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ ManagedAttributeModel attributeModel = serverRegistry.getAttributeModel(mbean);
+ if (attributeModel == null)
+ {
+ // If process is here, it means the mbeanInfo is not retrieved from mbean server even once for this mbean
+ getMBeanInfo(mbean);
+ }
+ else
+ {
+ // refresh attribute value from mbean server
+ refreshAttribute(mbean, attribute);
+ }
+ attributeModel = serverRegistry.getAttributeModel(mbean);
+ return attributeModel.getAttribute(attribute);
+ }
+
+ /**
+ * Retrieves the latest attribute value from mbean server for the given mbean attribute
+ * and also sets that value in the attribute model in the server registry
+ * @return latest attribute value for the given mbean attribute
+ */
+ public static Object refreshAttribute(ManagedBean mbean, String attribute) throws Exception
+ {
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+
+ if (mbsc == null)
+ {
+ throw new ManagementConsoleException("Server connection is broken");
+ }
+
+ Object value = mbsc.getAttribute(((JMXManagedObject)mbean).getObjectName(), attribute);
+ // update the attribute data in server registry for this attribute
+ ManagedAttributeModel attributeModel = serverRegistry.getAttributeModel(mbean);
+ attributeModel.setAttributeValue(attribute, value);
+ return value;
+ }
+
+ /**
+ * Retrieves the attribute values from MBeanSever and stores in the server registry.
+ * @param mbean
+ * @return the attribute model
+ * @throws Exception if attributes can not be retrieved from MBeanServer
+ */
+ public static ManagedAttributeModel getAttributes(ManagedBean mbean) throws Exception
+ {
+ ObjectName objName = ((JMXManagedObject)mbean).getObjectName();
+ String[] attributes = null;
+ AttributeList list = null;
+
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+ MBeanAttributeInfo[] attributesInfo = null;
+ ManagedAttributeModel attributeModel = serverRegistry.getAttributeModel(mbean);
+
+ if (attributeModel == null)
+ {
+ // If the process is here, then it means the attribute values are not retrieved from mbean server
+ // even once for this mbean. Create attribute model, retrieve values from mbean server and
+ // set the attribute model in server registry for this mbean
+ attributeModel = new ManagedAttributeModel();
+ attributesInfo = serverRegistry.getMBeanInfo(mbean).getAttributes();
+ attributes = new String[attributesInfo.length];
+ for (int i = 0; i< attributesInfo.length ; i++)
+ {
+ attributes[i] = attributesInfo[i].getName();
+ attributeModel.setAttributeDescription(attributes[i], attributesInfo[i].getDescription());
+ attributeModel.setAttributeWritable(attributes[i], attributesInfo[i].isWritable());
+ attributeModel.setAttributeReadable(attributes[i], attributesInfo[i].isReadable());
+ }
+ }
+ else
+ {
+ attributes = attributeModel.getAttributeNames().toArray(new String[0]);
+ }
+
+ if (attributes.length != 0)
+ {
+ list = mbsc.getAttributes(objName, attributes);
+ for (Iterator itr = list.iterator(); itr.hasNext();)
+ {
+ Attribute attrib = (Attribute)itr.next();
+ attributeModel.setAttributeValue(attrib.getName(), attrib.getValue());
+ }
+ }
+
+ serverRegistry.setAttributeModel(mbean, attributeModel);
+ return attributeModel;
+ }
+
+ /**
+ * Updates the attribute value of an MBean
+ * @param mbean
+ * @param attribute
+ * @param value
+ * @throws Exception if MBeanServer throws exception in updating the attribute value
+ */
+ public static void updateAttribute(ManagedBean mbean, AttributeData attribute, String value) throws Exception
+ {
+ JMXManagedObject jmxbean = (JMXManagedObject)mbean;
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+
+ Object newValue = value;
+ if (attribute.getDataType().equals(Long.class.getName()))
+ {
+ if (MAX_LONG.compareTo(new BigInteger(value)) == -1)
+ {
+ throw new ManagementConsoleException("Entered value is too big for \"" +
+ ViewUtility.getDisplayText(attribute.getName()) + "\"");
+ }
+ newValue = new Long(Long.parseLong(value));
+ }
+ else if (attribute.getDataType().equals(Integer.class.getName()))
+ {
+ if (MAX_INT.compareTo(new BigInteger(value)) == -1)
+ {
+ throw new ManagementConsoleException("Entered value is too big for " + attribute.getName());
+ }
+ newValue = new Integer(Integer.parseInt(value));
+ }
+
+ mbsc.setAttribute(jmxbean.getObjectName(), new Attribute(attribute.getName(), newValue));
+ // Update the value in the registry, to avoid refreshing from mbsc
+ ManagedAttributeModel attributeModel = serverRegistry.getAttributeModel(mbean);
+ attributeModel.setAttributeValue(attribute.getName(), newValue);
+ }
+
+ /**
+ * populates the operation data model in server registry for given mbean
+ * @param mbean
+ * @return operation data model
+ */
+ public static OperationDataModel getOperations(ManagedBean mbean)
+ {
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ OperationDataModel dataModel = serverRegistry.getOperationModel(mbean);
+ if (dataModel == null)
+ {
+ // Create operation model and set it in server registry for this mbean
+ MBeanOperationInfo[] operationsInfo = serverRegistry.getMBeanInfo(mbean).getOperations();
+ dataModel = new OperationDataModel();
+
+ for (int i = 0; i < operationsInfo.length; i++)
+ {
+ dataModel.addOperation(operationsInfo[i]);
+ }
+
+ serverRegistry.setOperationModel(mbean, dataModel);
+ }
+ return dataModel;
+ }
+
+ /**
+ * populates the notification in the server registry for given mbean
+ * @param mbean
+ * @return notification info model
+ */
+ public static NotificationInfoModel[] getNotificationInfo(ManagedBean mbean)
+ {
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ MBeanNotificationInfo[] info = serverRegistry.getMBeanInfo(mbean).getNotifications();
+
+ // Check if this mbean sends any notification
+ if (info == null || info.length == 0)
+ return null;
+
+ // Create notification model if not already set in the server registry for this mbean
+ List<NotificationInfoModel> list = serverRegistry.getNotificationInfo(mbean);
+ if (list != null)
+ return list.toArray(new NotificationInfoModel[0]);
+ else
+ list = new ArrayList<NotificationInfoModel>();
+
+ for (int i = 0; i < info.length; i++)
+ {
+ list.add(new NotificationInfoModel(info[i].getName(), info[i].getDescription(), info[i].getNotifTypes()));
+ }
+
+ // Set the notification model in the server registry for this mbean
+ serverRegistry.setNotificationInfo(mbean, list);
+ return list.toArray(new NotificationInfoModel[0]);
+ }
+
+ /**
+ * Retrieves all the MBeans from mbean server for a given domain
+ * @return list of ManagedBeans
+ */
+ public static List<ManagedBean> getManagedObjectsForDomain(ManagedServer server, String domain) throws Exception
+ {
+ List<ManagedBean> mbeans = new ArrayList<ManagedBean>();
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(server);
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+ ObjectName objName = new ObjectName(domain + ":*");
+ Set objectInstances = mbsc.queryMBeans(objName, null);
+
+ for (Iterator itr = objectInstances.iterator(); itr.hasNext();)
+ {
+ ObjectInstance instance = (ObjectInstance)itr.next();
+ ManagedBean obj = new JMXManagedObject(instance.getObjectName());
+ mbeans.add(obj);
+ }
+
+ return mbeans;
+ }
+
+ /**
+ * Returns all the domains for the given server. This method can be removed as now this RCP is specific to
+ * Qpid and domain is also fixed
+ */
+ public static List<String> getAllDomains(ManagedServer server) throws Exception
+ {
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(server);
+ MBeanServerConnection mbsc = serverRegistry.getServerConnection();
+ String[] domains = mbsc.getDomains();
+ return Arrays.asList(domains);
+ }
+
+ public static void printOutput(String statement)
+ {
+ if (ApplicationRegistry.debug)
+ {
+ System.out.println(statement);
+ }
+ }
+
+ public static void printStackTrace(Throwable ex)
+ {
+ if (ApplicationRegistry.debug)
+ {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/AttributeData.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/AttributeData.java
new file mode 100644
index 0000000000..ccd4cf8df8
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/AttributeData.java
@@ -0,0 +1,96 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.model;
+
+public class AttributeData
+{
+ String name = "";
+ String description = "";
+ String dataType = "";
+ Object value = "";
+ boolean readable = true;
+ boolean writable = false;
+
+
+ public String getDataType()
+ {
+ return dataType;
+ }
+ public void setDataType(String dataType)
+ {
+ this.dataType = dataType;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public Object getValue()
+ {
+ return value;
+ }
+ public void setValue(Object value)
+ {
+ if (value != null)
+ this.value = value;
+ }
+ public boolean isReadable()
+ {
+ return readable;
+ }
+ public void setReadable(boolean readable)
+ {
+ this.readable = readable;
+ }
+ public boolean isWritable()
+ {
+ return writable;
+ }
+ public void setWritable(boolean writable)
+ {
+ this.writable = writable;
+ }
+
+ public boolean isNumber()
+ {
+ if ("int".equals(dataType) || "java.lang.Integer".equals(dataType) ||
+ "long".equals(dataType) || "java.lang.Long".equals(dataType) )
+ {
+ return true;
+ }
+ else
+ return false;
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ManagedAttributeModel.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ManagedAttributeModel.java
new file mode 100644
index 0000000000..b3219f15ea
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ManagedAttributeModel.java
@@ -0,0 +1,118 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ManagedAttributeModel
+{
+ HashMap<String, AttributeData> _attributeMap = new HashMap<String, AttributeData>();
+
+ public void setAttributeValue(String name, Object value)
+ {
+ if (value == null)
+ return;
+
+ AttributeData data = null;
+ String dataType = value.getClass().getName();
+ if (_attributeMap.containsKey(name))
+ {
+ data = _attributeMap.get(name);
+ data.setValue(value);
+ }
+ else
+ {
+ data = new AttributeData();
+ data.setName(name);
+ data.setValue(value);
+ _attributeMap.put(name, data);
+ }
+ data.setDataType(dataType);
+ }
+
+
+ public void setAttributeDescription(String name, String value)
+ {
+ if (_attributeMap.containsKey(name))
+ {
+ _attributeMap.get(name).setDescription(value);
+ }
+ else
+ {
+ AttributeData data = new AttributeData();
+ data.setName(name);
+ data.setDescription(value);
+ _attributeMap.put(name, data);
+ }
+ }
+
+ public void setAttributeReadable(String name, boolean readable)
+ {
+ if (_attributeMap.containsKey(name))
+ {
+ _attributeMap.get(name).setReadable(readable);
+ }
+ else
+ {
+ AttributeData data = new AttributeData();
+ data.setName(name);
+ data.setReadable(readable);
+ _attributeMap.put(name, data);
+ }
+ }
+
+ public void setAttributeWritable(String name, boolean writable)
+ {
+ if (_attributeMap.containsKey(name))
+ {
+ _attributeMap.get(name).setWritable(writable);
+ }
+ else
+ {
+ AttributeData data = new AttributeData();
+ data.setName(name);
+ data.setWritable(writable);
+ _attributeMap.put(name, data);
+ }
+ }
+
+ public List<String> getAttributeNames()
+ {
+ return new ArrayList<String>(_attributeMap.keySet());
+ }
+
+ public AttributeData[] getAttributes()
+ {
+ return _attributeMap.values().toArray(new AttributeData[0]);
+ }
+
+ public AttributeData getAttribute(String name)
+ {
+ return _attributeMap.get(name);
+ }
+
+ public int getCount()
+ {
+ return _attributeMap.size();
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationInfoModel.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationInfoModel.java
new file mode 100644
index 0000000000..6d4160889e
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationInfoModel.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.management.ui.model;
+
+public class NotificationInfoModel
+{
+ String name;
+ String description;
+ String[] types;
+
+ public NotificationInfoModel(String name, String desc, String[] types)
+ {
+ this.name = name;
+ this.description = desc;
+ this.types = types;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String[] getTypes()
+ {
+ return types;
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java
new file mode 100644
index 0000000000..926e5f0a24
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.model;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import javax.management.ObjectName;
+
+public class NotificationObject
+{
+
+ private long _sequenceNo;
+ private Date _timeStamp;
+ private String _message;
+ private Object _source;
+ private String _type; // INFO, WARN, etc
+ private static final SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss dd/MM/yy z");
+
+ public NotificationObject(long seqNo, Date timeStamp, String message, Object source, String type)
+ {
+ this._sequenceNo = seqNo;
+ this._message = message;
+ this._source = source;
+ this._type = type;
+ this._timeStamp = timeStamp;
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ public Object getSource()
+ {
+ return _source;
+ }
+ public void setSource(Object _source)
+ {
+ this._source = _source;
+ }
+
+ public String getSourceName()
+ {
+ if (_source instanceof ObjectName)
+ {
+ return ((ObjectName)_source).getKeyProperty("name");
+ }
+
+ return null;
+ }
+
+ public String getMessage()
+ {
+ return _message;
+ }
+ public void setMessage(String _message)
+ {
+ this._message = _message;
+ }
+ public long getSequenceNo()
+ {
+ return _sequenceNo;
+ }
+ public void setSequenceNo(long no)
+ {
+ _sequenceNo = no;
+ }
+ public String getTimeStamp()
+ {
+ return dateFormat.format(_timeStamp);
+ }
+ public void setTimeStamp(Date stamp)
+ {
+ _timeStamp = stamp;
+ }
+ public String getType()
+ {
+ return _type;
+ }
+ public void setType(String _type)
+ {
+ this._type = _type;
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationData.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationData.java
new file mode 100644
index 0000000000..bf3b730b3e
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationData.java
@@ -0,0 +1,110 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.model;
+
+import java.util.List;
+
+public class OperationData
+{
+ private String _name;
+ private String _description;
+ private String _returnType;
+ private int _impact;
+ private List<ParameterData> _parameters;
+
+ public OperationData(String value)
+ {
+ this._name = value;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public String getDescription()
+ {
+ return _description;
+ }
+
+ public void setDescription(String description)
+ {
+ this._description = description;
+ }
+
+ public List<ParameterData> getParameters()
+ {
+ return _parameters;
+ }
+
+ public void setParameters(List<ParameterData> parameters)
+ {
+ this._parameters = parameters;
+ }
+
+ public int getImpact()
+ {
+ return _impact;
+ }
+
+ public void setImpact(int impact)
+ {
+ this._impact = impact;
+ }
+
+ public String getReturnType()
+ {
+ return _returnType;
+ }
+
+ public void setReturnType(String returnType)
+ {
+ this._returnType = returnType;
+ }
+
+ public boolean isReturnTypeBoolean()
+ {
+ return (_returnType.equals("boolean") || _returnType.equals("java.lang.Boolean"));
+ }
+
+ public boolean isReturnTypeVoid()
+ {
+ return (_returnType.equals("void") || _returnType.equals("java.lang.Void"));
+ }
+
+ public Object getParameterValue(String paramName)
+ {
+ if (_parameters == null)
+ {
+ return null;
+ }
+
+ for (int i = 0; i < _parameters.size(); i++)
+ {
+ if (paramName.equalsIgnoreCase(_parameters.get(i).getName()))
+ {
+ return _parameters.get(i).getValue();
+ }
+ }
+
+ return null;
+ }
+} \ No newline at end of file
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationDataModel.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationDataModel.java
new file mode 100644
index 0000000000..96964a81ef
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationDataModel.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+
+public class OperationDataModel
+{
+ HashMap<String, OperationData> _operationMap = new HashMap<String, OperationData>();
+
+ public void addOperation(MBeanOperationInfo opInfo)
+ {
+ OperationData opData = new OperationData(opInfo.getName());
+ opData.setDescription(opInfo.getDescription());
+ opData.setImpact(opInfo.getImpact());
+ opData.setReturnType(opInfo.getReturnType());
+
+ int parametersCount = opInfo.getSignature().length;
+ if (parametersCount != 0)
+ {
+ List<ParameterData> paramList = new ArrayList<ParameterData>();
+ for (int i = 0; i < parametersCount; i++)
+ {
+ MBeanParameterInfo paramInfo = opInfo.getSignature()[i];
+ ParameterData param = new ParameterData(paramInfo.getName(), paramInfo.getDescription(),
+ paramInfo.getType());
+ paramList.add(param);
+ }
+ opData.setParameters(paramList);
+ }
+
+ _operationMap.put(opInfo.getName(), opData);
+ }
+
+ public OperationData getOperation(String name)
+ {
+ return _operationMap.get(name);
+ }
+
+ public List<OperationData> getOperations()
+ {
+ return new ArrayList<OperationData>(_operationMap.values());
+ }
+
+ public int getCount()
+ {
+ return _operationMap.size();
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ParameterData.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ParameterData.java
new file mode 100644
index 0000000000..d12217c6eb
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ParameterData.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.model;
+
+/**
+ * Class representing an mbean operation parameter
+ * @author Bhupendra Bhardwaj
+ */
+public class ParameterData
+{
+ private String _name;
+ private String _description;
+ private String _type;
+ private Object _value;
+
+ ParameterData(String name, String desc, String type)
+ {
+ this._name = name;
+ this._description = desc;
+ this._type = type;
+ setDefaultValue();
+ }
+
+ public String getDescription()
+ {
+ return _description;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public String getType()
+ {
+ return _type;
+ }
+
+ public Object getValue()
+ {
+ return _value;
+ }
+
+ public void setValueFromString(String strValue)
+ {
+ if ("int".equals(_type))
+ _value = Integer.parseInt(strValue);
+ else if (isBoolean())
+ _value = Boolean.valueOf(strValue);
+ else if ("long".equals(_type))
+ _value = Long.parseLong(strValue);
+ else
+ _value = strValue;
+ }
+
+ public void setValue(Object value)
+ {
+ this._value = value;
+ }
+
+ public boolean isBoolean()
+ {
+ return (_type.equals("boolean") || _type.equals("java.lang.Boolean"));
+ }
+
+ public void setDefaultValue()
+ {
+ if (isBoolean())
+ {
+ _value = Boolean.valueOf("false");
+ }
+ else
+ {
+ _value = null;
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/CRAMMD5HashedSaslClientFactory.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/CRAMMD5HashedSaslClientFactory.java
new file mode 100644
index 0000000000..32a0c12344
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/CRAMMD5HashedSaslClientFactory.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.sasl;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslException;
+
+public class CRAMMD5HashedSaslClientFactory implements SaslClientFactory
+{
+ /** The name of this mechanism */
+ public static final String MECHANISM = "CRAM-MD5-HASHED";
+
+ public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol,
+ String serverName, Map<String, ?> props, CallbackHandler cbh)
+ throws SaslException
+ {
+ for (int i = 0; i < mechanisms.length; i++)
+ {
+ if (mechanisms[i].equals(MECHANISM))
+ {
+ if (cbh == null)
+ {
+ throw new SaslException("CallbackHandler must not be null");
+ }
+
+ String[] mechs = {"CRAM-MD5"};
+ return Sasl.createSaslClient(mechs, authorizationId, protocol, serverName, props, cbh);
+ }
+ }
+ return null;
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ return new String[]{MECHANISM};
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/ClientSaslFactory.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/ClientSaslFactory.java
new file mode 100644
index 0000000000..ce9a273eaa
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/ClientSaslFactory.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.management.ui.sasl;
+
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslClientFactory;
+import javax.security.sasl.SaslException;
+
+public class ClientSaslFactory implements SaslClientFactory
+{
+ public SaslClient createSaslClient(String[] mechs, String authorizationId, String protocol,
+ String serverName, Map props, CallbackHandler cbh)
+ throws SaslException
+ {
+ for (int i = 0; i < mechs.length; i++)
+ {
+ if (mechs[i].equals("PLAIN"))
+ {
+ return new PlainSaslClient(authorizationId, cbh);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Simple-minded implementation that ignores props
+ */
+ public String[] getMechanismNames(Map props)
+ {
+ return new String[]{"PLAIN"};
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/JCAProvider.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/JCAProvider.java
new file mode 100644
index 0000000000..d8189f3ac3
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/JCAProvider.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.sasl;
+
+import java.security.Provider;
+import java.util.Map;
+
+import javax.security.sasl.SaslClientFactory;
+
+public class JCAProvider extends Provider
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates the security provider with a map from SASL mechanisms to implementing factories.
+ *
+ * @param providerMap The map from SASL mechanims to implementing factory classes.
+ */
+ public JCAProvider(Map<String, Class<? extends SaslClientFactory>> providerMap)
+ {
+ super("AMQSASLProvider", 1.0, "A JCA provider that registers all "
+ + "AMQ SASL providers that want to be registered");
+ register(providerMap);
+ }
+
+ /**
+ * Registers client factory classes for a map of mechanism names to client factory classes.
+ *
+ * @param providerMap The map from SASL mechanims to implementing factory classes.
+ */
+ private void register(Map<String, Class<? extends SaslClientFactory>> providerMap)
+ {
+ for (Map.Entry<String, Class<? extends SaslClientFactory>> me : providerMap.entrySet())
+ {
+ put("SaslClientFactory." + me.getKey(), me.getValue().getName());
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/PlainSaslClient.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/PlainSaslClient.java
new file mode 100644
index 0000000000..22190f29eb
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/PlainSaslClient.java
@@ -0,0 +1,203 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.sasl;
+
+import java.io.*;
+import javax.security.auth.callback.*;
+import javax.security.sasl.*;
+
+public class PlainSaslClient implements SaslClient
+{
+
+ private boolean completed;
+ private CallbackHandler cbh;
+ private String authorizationID;
+ private String authenticationID;
+ private byte password[];
+ private static byte SEPARATOR = 0;
+
+ public PlainSaslClient(String authorizationID, CallbackHandler cbh) throws SaslException
+ {
+ completed = false;
+ this.cbh = cbh;
+ Object[] userInfo = getUserInfo();
+ this.authorizationID = authorizationID;
+ this.authenticationID = (String) userInfo[0];
+ this.password = (byte[]) userInfo[1];
+ if (authenticationID == null || password == null)
+ {
+ throw new SaslException("PLAIN: authenticationID and password must be specified");
+ }
+ }
+
+ public byte[] evaluateChallenge(byte[] challenge) throws SaslException
+ {
+ if (completed)
+ {
+ throw new IllegalStateException("PLAIN: authentication already " +
+ "completed");
+ }
+ completed = true;
+ try
+ {
+ byte authzid[] =
+ authorizationID == null ? null : authorizationID.getBytes("UTF8");
+ byte authnid[] = authenticationID.getBytes("UTF8");
+ byte response[] =
+ new byte[
+ password.length +
+ authnid.length +
+ 2 + // SEPARATOR
+ (authzid != null ? authzid.length : 0)
+ ];
+ int size = 0;
+ if (authzid != null) {
+ System.arraycopy(authzid, 0, response, 0, authzid.length);
+ size = authzid.length;
+ }
+ response[size++] = SEPARATOR;
+ System.arraycopy(authnid, 0, response, size, authnid.length);
+ size += authnid.length;
+ response[size++] = SEPARATOR;
+ System.arraycopy(password, 0, response, size, password.length);
+ clearPassword();
+ return response;
+ } catch (UnsupportedEncodingException e) {
+ throw new SaslException("PLAIN: Cannot get UTF-8 encoding of ids",
+ e);
+ }
+ }
+
+ public String getMechanismName()
+ {
+ return "PLAIN";
+ }
+
+ public boolean hasInitialResponse()
+ {
+ return true;
+ }
+
+ public boolean isComplete()
+ {
+ return completed;
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ if (completed) {
+ throw new IllegalStateException("PLAIN: this mechanism supports " +
+ "neither integrity nor privacy");
+ } else {
+ throw new IllegalStateException("PLAIN: authentication not " +
+ "completed");
+ }
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ if (completed)
+ {
+ throw new IllegalStateException("PLAIN: this mechanism supports " +
+ "neither integrity nor privacy");
+ }
+ else
+ {
+ throw new IllegalStateException("PLAIN: authentication not " +
+ "completed");
+ }
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ if (completed)
+ {
+ if (propName.equals(Sasl.QOP))
+ {
+ return "auth";
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("PLAIN: authentication not " +
+ "completed");
+ }
+ }
+
+ private void clearPassword()
+ {
+ if (password != null)
+ {
+ for (int i = 0 ; i < password.length ; i++)
+ {
+ password[i] = 0;
+ }
+ password = null;
+ }
+ }
+
+ public void dispose() throws SaslException
+ {
+ clearPassword();
+ }
+
+ protected void finalize()
+ {
+ clearPassword();
+ }
+
+ private Object[] getUserInfo() throws SaslException
+ {
+ try
+ {
+ final String userPrompt = "PLAIN authentication id: ";
+ final String pwPrompt = "PLAIN password: ";
+ NameCallback nameCb = new NameCallback(userPrompt);
+ PasswordCallback passwordCb = new PasswordCallback(pwPrompt, false);
+ cbh.handle(new Callback[] { nameCb, passwordCb });
+ String userid = nameCb.getName();
+ char pwchars[] = passwordCb.getPassword();
+ byte pwbytes[];
+ if (pwchars != null)
+ {
+ pwbytes = (new String(pwchars)).getBytes("UTF8");
+ passwordCb.clearPassword();
+ }
+ else
+ {
+ pwbytes = null;
+ }
+ return (new Object[] { userid, pwbytes });
+ }
+ catch (IOException e)
+ {
+ throw new SaslException("Cannot get password", e);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException("Cannot get userid/password", e);
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/SaslProvider.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/SaslProvider.java
new file mode 100644
index 0000000000..2917de8740
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/SaslProvider.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.sasl;
+
+import java.security.Provider;
+
+public class SaslProvider extends Provider
+{
+ private static final long serialVersionUID = -6978096016899676466L;
+
+ public SaslProvider()
+ {
+ super("SaslClientFactory", 1.0, "SASL PLAIN CLIENT MECHANISM");
+ put("SaslClientFactory.PLAIN", "ClientSaslFactory");
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UserPasswordCallbackHandler.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UserPasswordCallbackHandler.java
new file mode 100644
index 0000000000..1602229c85
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UserPasswordCallbackHandler.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.management.ui.sasl;
+
+import java.io.*;
+import javax.security.auth.callback.*;
+
+public class UserPasswordCallbackHandler implements CallbackHandler
+{
+ private String user;
+ private char[] pwchars;
+
+ public UserPasswordCallbackHandler(String user, String password)
+ {
+ this.user = user;
+ this.pwchars = password.toCharArray();
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ if (callbacks[i] instanceof NameCallback)
+ {
+ NameCallback ncb = (NameCallback) callbacks[i];
+ ncb.setName(user);
+ }
+ else if (callbacks[i] instanceof PasswordCallback)
+ {
+ PasswordCallback pcb = (PasswordCallback) callbacks[i];
+ pcb.setPassword(pwchars);
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(callbacks[i]);
+ }
+ }
+ }
+
+ private void clearPassword()
+ {
+ if (pwchars != null)
+ {
+ for (int i = 0 ; i < pwchars.length ; i++)
+ {
+ pwchars[i] = 0;
+ }
+ pwchars = null;
+ }
+ }
+
+ protected void finalize()
+ {
+ clearPassword();
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UsernameHashedPasswordCallbackHandler.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UsernameHashedPasswordCallbackHandler.java
new file mode 100644
index 0000000000..f4e3d2661e
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UsernameHashedPasswordCallbackHandler.java
@@ -0,0 +1,82 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.sasl;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.qpid.management.ui.views.ViewUtility;
+
+public class UsernameHashedPasswordCallbackHandler implements CallbackHandler
+{
+ private String user;
+ private char[] pwchars;
+
+ public UsernameHashedPasswordCallbackHandler(String user, String password) throws Exception
+ {
+ this.user = user;
+ this.pwchars = ViewUtility.getHash(password);
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ for (int i = 0; i < callbacks.length; i++)
+ {
+ if (callbacks[i] instanceof NameCallback)
+ {
+ NameCallback ncb = (NameCallback) callbacks[i];
+ ncb.setName(user);
+ }
+ else if (callbacks[i] instanceof PasswordCallback)
+ {
+ PasswordCallback pcb = (PasswordCallback) callbacks[i];
+ pcb.setPassword(pwchars);
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(callbacks[i]);
+ }
+ }
+ }
+
+
+ private void clearPassword()
+ {
+ if (pwchars != null)
+ {
+ for (int i = 0 ; i < pwchars.length ; i++)
+ {
+ pwchars[i] = 0;
+ }
+ pwchars = null;
+ }
+ }
+
+ protected void finalize()
+ {
+ clearPassword();
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java
new file mode 100644
index 0000000000..3234503fb5
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java
@@ -0,0 +1,936 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import static org.apache.qpid.management.ui.Constants.*;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.jmx.JMXServerRegistry;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.model.AttributeData;
+import org.apache.qpid.management.ui.model.ManagedAttributeModel;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+
+/**
+ * Creates controller composite for the attribute's tab.
+ * @author Bhupendra Bhardwaj
+ */
+public class AttributesTabControl extends TabControl
+{
+ private FormToolkit _toolkit;
+ private Form _form;
+ private Table _table = null;
+ private TableViewer _tableViewer = null;
+ private static final int[] tableWidths = new int[] {300, 300};
+
+ private Composite _tableComposite = null;
+ private Composite _buttonsComposite = null;
+
+ private DisposeListener tableDisposeListener = new DisposeListenerImpl();
+ final Image image;
+ private Button _detailsButton = null;
+ private Button _editButton = null;
+ private Button _graphButton = null;
+ private Button _refreshButton = null;
+ private boolean disableEditing = false;
+
+ private static final String MAX_VALUE = "MaxValue";
+ private static final String GRAPH_VALUES = "GraphValues";
+ private int GRAPH_WIDTH = 700;
+ private int GRAPH_HEIGHT = 450;
+ private int GRAPH_ITEM_GAP = 100;
+ private int startX = 80;
+ private int startY = 60;
+
+ public AttributesTabControl(TabFolder tabFolder)
+ {
+ super(tabFolder);
+ _toolkit = new FormToolkit(_tabFolder.getDisplay());
+ _form = _toolkit.createForm(_tabFolder);
+ GridLayout gridLayout = new GridLayout(2, false);
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ _form.getBody().setLayout(gridLayout);
+ _tableComposite = _toolkit.createComposite(_form.getBody());
+ _tableComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ _tableComposite.setLayout(new GridLayout());
+ _buttonsComposite = _toolkit.createComposite(_form.getBody());
+ _buttonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
+ _buttonsComposite.setLayout(new GridLayout());
+
+ image = Display.getCurrent().getSystemImage(SWT.ICON_INFORMATION);
+ createWidgets();
+ }
+
+ /**
+ * @see TabControl#getControl()
+ */
+ public Control getControl()
+ {
+ return _form;
+ }
+
+ /**
+ * Creates required widgets for Attribute's tab
+ */
+ protected void createWidgets()
+ {
+ createTable();
+ createTableViewer();
+ createButtons();
+ addTableListeners();
+ }
+
+ /**
+ * Creates table for listing the MBean attributes
+ */
+ private void createTable()
+ {
+ _table = _toolkit.createTable(_tableComposite, SWT.FULL_SELECTION);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ _table.setLayoutData(gridData);
+
+ for (int i = 0; i < ATTRIBUTE_TABLE_TITLES.length; ++i)
+ {
+ final TableColumn column = new TableColumn(_table, SWT.NONE);
+ column.setText(ATTRIBUTE_TABLE_TITLES[i]);
+ column.setWidth(tableWidths[i]);
+ column.setResizable(false);
+ }
+
+ _table.setLinesVisible (true);
+ _table.setHeaderVisible (true);
+ }
+
+ /**
+ * Creates tableviewer for the attribute's table
+ */
+ private void createTableViewer()
+ {
+ _tableViewer = new TableViewer(_table);
+ _tableViewer.setUseHashlookup(true);
+ _tableViewer.setColumnProperties(ATTRIBUTE_TABLE_TITLES);
+ _tableViewer.setContentProvider(new ContentProviderImpl());
+ _tableViewer.setLabelProvider(new LabelProviderImpl());
+ _tableViewer.setSorter(new ViewerSorterImpl());
+ }
+
+ private void createButtons()
+ {
+ addDetailsButton();
+ addEditButton();
+ addGraphButton();
+ addRefreshButton();
+ }
+
+ private void addDetailsButton()
+ {
+ // Create and configure the button for attribute details
+ _detailsButton = _toolkit.createButton(_buttonsComposite, BUTTON_DETAILS, SWT.PUSH | SWT.CENTER);
+ _detailsButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ GridData gridData = new GridData(SWT.CENTER, SWT.TOP, false, false);
+ gridData.widthHint = 80;
+ _detailsButton.setLayoutData(gridData);
+ _detailsButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ disableEditing = true;
+ int index = _table.getSelectionIndex();
+ TableItem item = _table.getItem(index);
+ createDetailsPopup((AttributeData)item.getData());
+ disableEditing = false;
+ setFocus();
+ }
+ });
+ }
+
+ /**
+ * Creates the button for editing attributes.
+ */
+ private void addEditButton()
+ {
+ // Create and configure the button for editing attribute
+ _editButton = _toolkit.createButton(_buttonsComposite, BUTTON_EDIT_ATTRIBUTE, SWT.PUSH | SWT.CENTER);
+ _editButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ GridData gridData = new GridData(SWT.CENTER, SWT.TOP, false, false);
+ gridData.widthHint = 80;
+ _editButton.setLayoutData(gridData);
+ _editButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ int index = _table.getSelectionIndex();
+ TableItem item = _table.getItem(index);
+ createDetailsPopup((AttributeData)item.getData());
+ setFocus();
+ }
+ });
+ }
+
+ /**
+ * Creates the button for viewing Graphs
+ */
+ private void addGraphButton()
+ {
+ _graphButton = _toolkit.createButton(_buttonsComposite, BUTTON_GRAPH, SWT.PUSH | SWT.CENTER);
+ _graphButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ GridData gridData = new GridData(SWT.CENTER, SWT.TOP, false, false);
+ gridData.widthHint = 80;
+ _graphButton.setLayoutData(gridData);
+ _graphButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent event)
+ {
+ int selectionIndex = _table.getSelectionIndex();
+ AttributeData data = (AttributeData)_table.getItem(selectionIndex).getData();
+ createGraph(data);
+ setFocus();
+ }
+ });
+ }
+
+ /**
+ * Creates the "Refresh" button
+ */
+ private void addRefreshButton()
+ {
+ _refreshButton = _toolkit.createButton(_buttonsComposite, BUTTON_REFRESH, SWT.PUSH | SWT.CENTER);
+
+ _refreshButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ GridData gridData = new GridData(SWT.CENTER, SWT.TOP, false, false);
+ gridData.widthHint = 80;
+ _refreshButton.setLayoutData(gridData);
+ _refreshButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ try
+ {
+ // refresh the attributes list
+ refresh(_mbean);
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(_mbean, ex);
+ }
+
+ }
+ });
+ }
+
+ private void addTableListeners()
+ {
+ _tableViewer.addSelectionChangedListener(new ISelectionChangedListener(){
+ public void selectionChanged(SelectionChangedEvent evt)
+ {
+ IStructuredSelection ss = (IStructuredSelection)evt.getSelection();
+ checkForEnablingButtons((AttributeData)ss.getFirstElement());
+ }
+ });
+
+ MouseListenerImpl listener = new MouseListenerImpl();
+ _tableViewer.getTable().addMouseTrackListener(listener);
+ _tableViewer.getTable().addMouseMoveListener(listener);
+ _tableViewer.getTable().addMouseListener(listener);
+
+ _table.addDisposeListener(tableDisposeListener);
+
+ // _table is equal to _tableViewer.getControl()
+ _table.addListener(SWT.MeasureItem, new Listener() {
+ public void handleEvent(Event event)
+ {
+ event.height = event.gc.getFontMetrics().getHeight() * 3/2;
+ }
+ });
+ }
+
+ /**
+ * Listeners implementation class for showing table tooltip
+ * @author Bhupendra Bhardwaj
+ */
+ private class MouseListenerImpl implements MouseTrackListener, MouseMoveListener, KeyListener, MouseListener
+ {
+ Shell tooltipShell = null;
+ Label tooltipLabel = null;
+ public void mouseHover(MouseEvent event)
+ {
+ TableItem item = _table.getItem (new Point (event.x, event.y));
+
+ if (item != null)
+ {
+ AttributeData data = (AttributeData)item.getData();
+ if (tooltipShell != null && !tooltipShell.isDisposed ()) tooltipShell.dispose ();
+ tooltipShell = new Shell(_table.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
+ tooltipShell.setBackground(event.display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ FillLayout layout = new FillLayout();
+ layout.marginWidth = 2;
+ tooltipShell.setLayout(layout);
+ tooltipLabel = new Label(tooltipShell, SWT.NONE);
+ tooltipLabel.setForeground(event.display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ tooltipLabel.setBackground(event.display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ tooltipLabel.setText(data.getDescription());
+ tooltipLabel.setData("_TABLEITEM", item);
+ tooltipLabel.addListener(SWT.MouseExit, tooltipLabelListener);
+ tooltipLabel.addListener(SWT.MouseDown, tooltipLabelListener);
+ Point size = tooltipShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ Rectangle rect = item.getBounds(0);
+ Point pt = _table.toDisplay(rect.x, rect.y);
+ tooltipShell.setBounds(pt.x, pt.y, size.x, size.y);
+ tooltipShell.setVisible(true);
+ }
+ }
+ public void mouseEnter(MouseEvent e)
+ {
+ }
+ public void mouseExit(MouseEvent e)
+ {
+ }
+
+ // MouseMoveListener implementation
+ public void mouseMove(MouseEvent event)
+ {
+ if (tooltipShell == null)
+ return;
+
+ tooltipShell.dispose();
+ tooltipShell = null;
+ tooltipLabel = null;
+ }
+
+ // KeyListener implementation
+ public void keyPressed(KeyEvent e)
+ {
+ if (tooltipShell == null)
+ return;
+
+ tooltipShell.dispose();
+ tooltipShell = null;
+ tooltipLabel = null;
+ }
+ public void keyReleased(KeyEvent e)
+ {
+
+ }
+
+ // MouseListener implementation
+ public void mouseDoubleClick(MouseEvent event)
+ {
+ if (tooltipShell != null)
+ {
+ tooltipShell.dispose();
+ tooltipShell = null;
+ tooltipLabel = null;
+ }
+ Table table = (Table)event.getSource();
+ int selectionIndex = table.getSelectionIndex();
+ AttributeData data = (AttributeData)table.getItem(selectionIndex).getData();
+ createDetailsPopup(data);
+ }
+ public void mouseDown(MouseEvent e)
+ {
+ if (tooltipShell != null)
+ {
+ tooltipShell.dispose();
+ tooltipShell = null;
+ tooltipLabel = null;
+ }
+ }
+ public void mouseUp(MouseEvent e)
+ {
+
+ }
+ } // end of MouseListenerImpl
+
+ /**
+ * Creates pop-up window for showing attribute details
+ * @param data - Selectes attribute
+ */
+ public void createDetailsPopup(AttributeData data)
+ {
+ int width = 500;
+ int height = 250;
+ if (!isSimpleType(data.getValue()))
+ {
+ width = 650;
+ height = 450;
+ }
+
+ Display display = Display.getCurrent();
+ Shell shell = ViewUtility.createPopupShell(ATTRIBUTE, width, height);
+ createDetailsPopupContents(shell, data);
+
+ shell.open();
+ while (!shell.isDisposed())
+ {
+ if (!display.readAndDispatch())
+ {
+ display.sleep();
+ }
+ }
+ shell.dispose();
+ }
+
+ /**
+ * Listener class for table tooltip label
+ */
+ final Listener tooltipLabelListener = new Listener ()
+ {
+ public void handleEvent (Event event)
+ {
+ Label label = (Label)event.widget;
+ Shell shell = label.getShell();
+ switch (event.type)
+ {
+ case SWT.MouseDown:
+ Event e = new Event();
+ e.item = (TableItem)label.getData ("_TABLEITEM");
+ _table.setSelection(new TableItem[] {(TableItem)e.item});
+ shell.dispose();
+ _table.setFocus();
+ break;
+ case SWT.MouseExit:
+ shell.dispose();
+ break;
+ }
+ }
+ };
+
+
+ /**
+ * Create the contents for the attribute details window pop-up
+ * @param shell - The shell that will be filled with details.
+ * @param attribute - Selected attribute
+ */
+ private void createDetailsPopupContents(Composite shell, AttributeData attribute)
+ {
+ GridLayout layout = new GridLayout(2, false);
+ layout.horizontalSpacing = 10;
+ layout.verticalSpacing = 10;
+ layout.marginHeight = 20;
+ layout.marginWidth = 20;
+
+ Composite parent = _toolkit.createComposite(shell, SWT.NONE);
+ parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ parent.setLayout(layout);
+
+ // Name
+ Label label = _toolkit.createLabel(parent, ATTRIBUTE_TABLE_TITLES[0], SWT.NONE);
+ GridData layoutData = new GridData(SWT.TRAIL, SWT.TOP, false, false);
+ label.setLayoutData(layoutData);
+ int textStyle = SWT.BEGINNING | SWT.BORDER |SWT.READ_ONLY;
+ Text value = _toolkit.createText(parent, ViewUtility.getDisplayText(attribute.getName()), textStyle);
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+
+ // Description
+ label = _toolkit.createLabel(parent, DESCRIPTION, SWT.NONE);
+ label.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+ value = _toolkit.createText(parent, attribute.getDescription(), textStyle);
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ // value
+ label = _toolkit.createLabel(parent, ATTRIBUTE_TABLE_TITLES[1], SWT.NONE);
+ label.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ if (!attribute.isReadable())
+ {
+ value = _toolkit.createText(parent, "", textStyle);
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ }
+ else
+ {
+ if (!isSimpleType(attribute.getValue()))
+ {
+ Composite composite = new Composite(parent, SWT.BORDER);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ ViewUtility.populateCompositeWithData(_toolkit, composite, attribute.getValue());
+ }
+ else
+ {
+ if (attribute.isWritable())
+ {
+ value = _toolkit.createText(parent, "", SWT.BEGINNING | SWT.BORDER);
+ value.addVerifyListener(new NumberVerifyListener());
+
+ // set data to access in the listener
+ parent.setData(attribute);
+ }
+ else
+ {
+ value = _toolkit.createText(parent, "", textStyle);
+ }
+
+ value.setText(attribute.getValue().toString());
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ }
+ }
+
+
+ // Update button
+ Button updateButton = addUpdateButton(parent);
+ updateButton.setData(value);
+ if (!attribute.isWritable())
+ {
+ updateButton.setVisible(false);
+ }
+
+ if (disableEditing)
+ {
+ value.setEditable(false);
+ updateButton.setVisible(false);
+ }
+ }
+
+ /**
+ * Create the button for updating attributes. This should be enabled for writable attribute
+ */
+ private Button addUpdateButton(Composite parent)
+ {
+ final Button updateButton = new Button(parent, SWT.PUSH | SWT.CENTER);
+ // set the data to access in the listener
+ parent.setData(BUTTON_UPDATE, updateButton);
+
+ updateButton.setText(BUTTON_UPDATE);
+ GridData gridData = new GridData (SWT.CENTER, SWT.BOTTOM, true, true, 2, 1);
+ gridData.widthHint = 100;
+ updateButton.setLayoutData(gridData);
+ updateButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent event)
+ {
+ try
+ {
+ Button button = (Button)event.widget;
+ Text text = (Text)button.getData();
+ AttributeData data = (AttributeData)button.getParent().getData();
+ MBeanUtility.updateAttribute(_mbean, data, text.getText());
+ button.getShell().close();
+ refresh();
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(_mbean, ex);
+ }
+ }
+ });
+
+ return updateButton;
+ }
+
+ // Refresh from the server registry
+ public void refresh()
+ {
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(_mbean);
+ ManagedAttributeModel attributesList = serverRegistry.getAttributeModel(_mbean);
+ _tableViewer.setInput(attributesList);
+ }
+
+ /**
+ * Refreshes the attribute tab by querying the mbean server for latest values
+ */
+ @Override
+ public void refresh(ManagedBean mbean)
+ {
+ _mbean = mbean;
+ if (_mbean == null)
+ {
+ _tableViewer.setInput(null);
+ return;
+ }
+ ManagedAttributeModel attributesList = null;
+ try
+ {
+ attributesList = MBeanUtility.getAttributes(mbean);
+ }
+ catch(Exception ex)
+ {
+ MBeanUtility.handleException(_mbean, ex);
+ }
+ _tableViewer.setInput(attributesList);
+ checkForEnablingButtons(getSelectionAttribute());
+
+ _form.layout(true);
+ _form.getBody().layout(true, true);
+ }
+
+ /**
+ * @see TabControl#setFocus()
+ */
+ public void setFocus()
+ {
+ _table.setFocus();
+ }
+
+ /**
+ * Checks which buttons are to be enabled or disabled. The graph button will be enabled only
+ * for readable number attributes. Editing is enabled for writeable attribtues.
+ * @param attribute
+ */
+ private void checkForEnablingButtons(AttributeData attribute)
+ {
+ if (attribute == null)
+ {
+ _detailsButton.setEnabled(false);
+ _editButton.setEnabled(false);
+ _graphButton.setEnabled(false);
+ _refreshButton.setEnabled(false);
+ return;
+ }
+
+ _detailsButton.setEnabled(true);
+ _refreshButton.setEnabled(true);
+ if (attribute.isWritable())
+ {
+ _editButton.setEnabled(true);
+ _graphButton.setEnabled(false);
+ }
+ else
+ {
+ _editButton.setEnabled(false);
+ // Currently only Queues are having attributes, which are suitable for a graph
+ if (attribute.isNumber() && _mbean.isQueue())
+ {
+ _graphButton.setEnabled(true);
+ }
+ else
+ {
+ _graphButton.setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * Creates graph in a pop-up window for given attribute.
+ * @param data
+ */
+ private void createGraph(final AttributeData data)
+ {
+ Display display = Display.getCurrent();
+ Shell shell = new Shell(display, SWT.BORDER | SWT.CLOSE | SWT.MIN | SWT.MAX);
+ shell.setText(_mbean.getName());
+ int x = display.getBounds().width;
+ int y = display.getBounds().height;
+ shell.setBounds(x/4, y/4, GRAPH_WIDTH, GRAPH_HEIGHT);
+ shell.setLayout(new FillLayout());
+
+ final Canvas canvas = new Canvas(shell, SWT.NONE);
+ long currentValue = Long.parseLong(data.getValue().toString());
+ long mValue = getGraphMaxValue(currentValue);
+ canvas.setData(MAX_VALUE, mValue);
+ canvas.setData(GRAPH_VALUES, new long[] {0,0,0,0,0,currentValue});
+
+ canvas.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
+ canvas.addPaintListener(new PaintListener()
+ {
+ public void paintControl(PaintEvent event)
+ {
+ Canvas canvas = (Canvas)event.widget;
+ int maxX = canvas.getSize().x;
+ int maxY = canvas.getSize().y;
+ event.gc.fillRectangle(canvas.getBounds());
+ event.gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
+ event.gc.setLineWidth(4);
+
+ Object canvasData = canvas.getData(MAX_VALUE);
+ String str = canvasData.toString();
+ long maxValue = Long.parseLong(str);
+ // Set the graph dimensions
+ event.gc.drawText("0", startX - 40, maxY - startY - 10);
+ event.gc.drawText("" + maxValue/2, startX - 40, maxY/2);
+ event.gc.drawText("" + maxValue, startX - 40, startY);
+
+ // horizontal line
+ event.gc.drawLine(startX, maxY - startY, maxX - 60, maxY - startY);
+ // vertical line
+ event.gc.drawLine(startX, maxY - startY, startX, startY);
+ // set graph text
+ event.gc.drawText(data.getName(), startX - 40, startY - 40);
+ event.gc.drawText("25 sec", startX, maxY - startY + 10);
+ event.gc.drawText("20 sec", startX + GRAPH_ITEM_GAP, maxY - startY + 10);
+ event.gc.drawText("15 sec", startX + GRAPH_ITEM_GAP * 2, maxY - startY + 10);
+ event.gc.drawText("10 sec", startX + GRAPH_ITEM_GAP * 3, maxY - startY + 10);
+ event.gc.drawText(" 5 sec", startX + GRAPH_ITEM_GAP * 4, maxY - startY + 10);
+ event.gc.drawText(" 0 sec", startX + GRAPH_ITEM_GAP * 5, maxY - startY + 10);
+
+ // plot the graph now for values
+ event.gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE));
+ canvasData = canvas.getData(GRAPH_VALUES);
+ long[] graphValues = (long[]) canvasData;
+ for (int i = 0; i < graphValues.length; i++)
+ {
+ int x = startX + i * GRAPH_ITEM_GAP;
+ int yTotalLength = (maxY - 2 * startY);
+ float ratio = ((float)graphValues[i]/(float)maxValue);
+ int itemlength = (int)(yTotalLength * ratio);
+ int y = maxY - startY - itemlength;
+ event.gc.drawLine(x, maxY- startY, x, y);
+ event.gc.drawText(String.valueOf(graphValues[i]), x, y - 20);
+ }
+ }
+ });
+
+ shell.open();
+
+ // Set up the timer for the animation
+ Runnable runnable = new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ animate(canvas, data);
+ Display.getCurrent().timerExec(TIMER_INTERVAL, this);
+ }
+ catch(Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ };
+
+ // Launch the timer
+ display.timerExec(TIMER_INTERVAL, runnable);
+
+ while (!shell.isDisposed())
+ {
+ if (!display.readAndDispatch())
+ {
+ display.sleep();
+ }
+ }
+
+ // Kill the timer
+ display.timerExec(-1, runnable);
+ shell.dispose();
+ }
+
+ /**
+ * @return selected attribute in the table
+ */
+ public AttributeData getSelectionAttribute()
+ {
+ int index = _table.getSelectionIndex();
+ if (index == -1)
+ return null;
+
+ return (AttributeData)_table.getItem(index).getData();
+ }
+
+ /**
+ * checks for newer values of selected attribute to update the graph
+ * @param canvas
+ * @param data
+ * @throws Exception
+ */
+ private void animate(Canvas canvas, AttributeData data) throws Exception
+ {
+ String attribute = data.getName();
+ Object valueObj = MBeanUtility.refreshAttribute(_mbean, attribute);
+ int value = Integer.parseInt(String.valueOf(valueObj));
+ Object canvasData = canvas.getData(GRAPH_VALUES);
+ long[] graphValues = (long[]) canvasData;
+
+ for (int i = 0; i < graphValues.length -1; i++)
+ {
+ graphValues[i] = graphValues[i + 1];
+ }
+ graphValues[graphValues.length - 1] = value;
+
+ canvasData = canvas.getData(MAX_VALUE);
+ long maxValue = Long.parseLong(String.valueOf(canvasData));
+ if (maxValue < value)
+ {
+ maxValue = getGraphMaxValue(value);
+ canvas.setData(MAX_VALUE, maxValue);
+ }
+
+ canvas.redraw();
+ }
+
+ /**
+ * @param maxAttributeValue
+ * @return dynamically calculated value for y-axis on the graph
+ */
+ private long getGraphMaxValue(long maxAttributeValue)
+ {
+ long maxGraphValue = 100;
+ long temp = maxAttributeValue * 3/2;
+ if (temp > maxGraphValue)
+ {
+ long modulus = temp % 100;
+ maxGraphValue = temp + ( 100 - modulus);
+ }
+
+ return maxGraphValue;
+ }
+
+ /**
+ * Content Provider class for the table viewer
+ * @author Bhupendra Bhardwaj
+ */
+ private class ContentProviderImpl implements IStructuredContentProvider
+ {
+
+ public void inputChanged(Viewer v, Object oldInput, Object newInput)
+ {
+
+ }
+
+ public void dispose()
+ {
+
+ }
+
+ public Object[] getElements(Object parent)
+ {
+ return ((ManagedAttributeModel)parent).getAttributes();
+ }
+ }
+
+ /**
+ * Label Provider class for the table viewer
+ * @author Bhupendra Bhardwaj
+ */
+ private class LabelProviderImpl extends LabelProvider implements ITableLabelProvider,
+ IFontProvider,
+ IColorProvider
+ {
+ AttributeData attribute = null;
+ public String getColumnText(Object element, int columnIndex)
+ {
+ String result = "";
+ attribute = (AttributeData) element;
+
+ switch (columnIndex)
+ {
+ case 0 : // attribute name column
+ result = ViewUtility.getDisplayText(attribute.getName());
+ break;
+ case 1 : // attribute value column
+ if (attribute.getValue() != null)
+ result = String.valueOf(attribute.getValue());
+ break;
+ default :
+ result = "";
+ }
+
+ return result;
+ }
+
+ public Image getColumnImage(Object element, int columnIndex)
+ {
+ return null;
+ }
+
+ public Font getFont(Object element)
+ {
+ return ApplicationRegistry.getFont(FONT_TABLE_CELL);
+ }
+
+ public Color getForeground(Object element)
+ {
+ attribute = (AttributeData) element;
+ if (attribute.isWritable())
+ return Display.getCurrent().getSystemColor(SWT.COLOR_DARK_BLUE);
+ else
+ return Display.getCurrent().getSystemColor(SWT.COLOR_BLACK);
+ }
+ public Color getBackground(Object element)
+ {
+ return _form.getBackground();
+ }
+ }
+
+ private class DisposeListenerImpl implements DisposeListener
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+
+ }
+ }
+
+ /**
+ * Sorter class for the table viewer. It sorts the table for according to attribute name.
+ * @author Bhupendra Bhardwaj
+ *
+ */
+ private class ViewerSorterImpl extends ViewerSorter
+ {
+ public int compare(Viewer viewer, Object o1, Object o2)
+ {
+ AttributeData attribtue1 = (AttributeData)o1;
+ AttributeData attribtue2 = (AttributeData)o2;
+
+ return collator.compare(attribtue1.getName(), attribtue2.getName());
+ }
+ }
+} \ No newline at end of file
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ConnectionTypeTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ConnectionTypeTabControl.java
new file mode 100644
index 0000000000..d891a45210
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ConnectionTypeTabControl.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.management.ui.views;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.Constants;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.eclipse.swt.widgets.TabFolder;
+
+/**
+ * Controller class, which takes care of displaying appropriate information and widgets for Connections.
+ * This allows user to select Connections and add those to the navigation view
+ */
+public class ConnectionTypeTabControl extends MBeanTypeTabControl
+{
+
+ public ConnectionTypeTabControl(TabFolder tabFolder)
+ {
+ super(tabFolder, Constants.CONNECTION);
+ createWidgets();
+ }
+
+ protected void createWidgets()
+ {
+ createHeaderComposite(getFormComposite());
+ createButtonsComposite(getFormComposite());
+ createListComposite(getFormComposite());
+ }
+
+ protected void populateList() throws Exception
+ {
+ // map should be cleared before populating it with new values
+ getMBeansMap().clear();
+
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(MBeanView.getServer());
+ java.util.List<ManagedBean> list = serverRegistry.getConnections(MBeanView.getVirtualHost());
+ getListWidget().setItems(getItems(list));
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ExchangeTypeTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ExchangeTypeTabControl.java
new file mode 100644
index 0000000000..ee55b251ee
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ExchangeTypeTabControl.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.management.ui.views;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.Constants;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.eclipse.swt.widgets.TabFolder;
+
+/**
+ * Controller class, which takes care of displaying appropriate information and widgets for Exchanges.
+ * This allows user to select Exchanges and add those to the navigation view
+ * @author Bhupendra Bhardwaj
+ */
+public class ExchangeTypeTabControl extends MBeanTypeTabControl
+{
+
+ public ExchangeTypeTabControl(TabFolder tabFolder)
+ {
+ super(tabFolder, Constants.EXCHANGE);
+ createWidgets();
+ }
+
+ protected void createWidgets()
+ {
+ createHeaderComposite(getFormComposite());
+ createButtonsComposite(getFormComposite());
+ createListComposite(getFormComposite());
+ }
+
+ protected void populateList() throws Exception
+ {
+ // map should be cleared before populating it with new values
+ getMBeansMap().clear();
+
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(MBeanView.getServer());
+ java.util.List<ManagedBean> list = serverRegistry.getExchanges(MBeanView.getVirtualHost());
+ getListWidget().setItems(getItems(list));
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/INotificationViewer.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/INotificationViewer.java
new file mode 100644
index 0000000000..bc560b6064
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/INotificationViewer.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import java.util.List;
+
+import org.apache.qpid.management.ui.model.NotificationObject;
+
+public interface INotificationViewer
+{
+ public void addNotification(NotificationObject notification);
+
+ public void addNotification(List<NotificationObject> notificationList);
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTypeTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTypeTabControl.java
new file mode 100644
index 0000000000..24dfb519fd
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTypeTabControl.java
@@ -0,0 +1,336 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.management.ui.views;
+
+import static org.apache.qpid.management.ui.Constants.BUTTON_REFRESH;
+import static org.apache.qpid.management.ui.Constants.FONT_BOLD;
+import static org.apache.qpid.management.ui.Constants.FONT_ITALIC;
+import static org.apache.qpid.management.ui.Constants.FONT_NORMAL;
+
+import java.util.Collections;
+import java.util.HashMap;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.model.AttributeData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * Abstract class to be extended by the Controller classes for different MBean types (Connection, Queue, Exchange)
+ */
+public abstract class MBeanTypeTabControl
+{
+ private FormToolkit _toolkit = null;
+ private Form _form = null;
+ private TabFolder _tabFolder = null;
+ private Composite _composite = null;
+ private Composite _headerComposite = null;
+ private Composite _listComposite = null;
+ private Label _labelName = null;
+ private Label _labelDesc = null;
+ private Label _labelList = null;
+
+ private org.eclipse.swt.widgets.List _list = null;
+ private Button _refreshButton = null;
+ private Button _addButton = null;
+
+ private String _type = null;
+
+ // maps an mbean name with the mbean object. Required to get mbean object when an mbean
+ // is to be added to the navigation view.
+ private HashMap<String, ManagedBean> _objectsMap = new HashMap<String, ManagedBean>();
+ private Sorter _sorterByName = new Sorter();
+
+ public MBeanTypeTabControl(TabFolder tabFolder, String type)
+ {
+ _type = type;
+ _tabFolder = tabFolder;
+ _toolkit = new FormToolkit(_tabFolder.getDisplay());
+ _form = _toolkit.createForm(_tabFolder);
+ createFormComposite();
+ }
+
+ public FormToolkit getToolkit()
+ {
+ return _toolkit;
+ }
+
+ public Control getControl()
+ {
+ return _form;
+ }
+
+ public String getType()
+ {
+ return _type;
+ }
+
+ protected List getListWidget()
+ {
+ return _list;
+ }
+
+ protected HashMap<String, ManagedBean> getMBeansMap()
+ {
+ return _objectsMap;
+ }
+
+ public Sorter getMBeanNameSorter()
+ {
+ return _sorterByName;
+ }
+
+ public Button getAddButton()
+ {
+ return _addButton;
+ }
+
+ public Button getRefreshButton()
+ {
+ return _refreshButton;
+ }
+
+ /**
+ * Creates the main Composite, which will contain all other Composites and Widgets
+ */
+ protected void createFormComposite()
+ {
+ _form.getBody().setLayout(new GridLayout());
+ _composite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
+ _composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ GridLayout layout = new GridLayout();
+ layout.verticalSpacing = 10;
+ layout.horizontalSpacing = 0;
+ _composite.setLayout(layout);
+ }
+
+ protected Composite getFormComposite()
+ {
+ return _composite;
+ }
+
+ /**
+ * Creates the header composite, which has MBean type name and description
+ * @param parentComposite
+ */
+ protected void createHeaderComposite(Composite parentComposite)
+ {
+ _headerComposite = _toolkit.createComposite(parentComposite);
+ _headerComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ GridLayout layout = new GridLayout();
+ layout.verticalSpacing = 10;
+ layout.horizontalSpacing = 0;
+ _headerComposite.setLayout(layout);
+
+ _labelName = _toolkit.createLabel(_headerComposite, "Type:", SWT.NONE);
+ GridData gridData = new GridData(SWT.CENTER, SWT.TOP, true, false);
+ _labelName.setLayoutData(gridData);
+ _labelName.setFont(ApplicationRegistry.getFont(FONT_BOLD));
+
+ _labelDesc = _toolkit.createLabel(_headerComposite, " ", SWT.NONE);
+ _labelDesc.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+ _labelDesc.setFont(ApplicationRegistry.getFont(FONT_ITALIC));
+
+ _headerComposite.layout();
+ }
+
+ /**
+ * Creates Composite, which contains the common buttons - Add and Refresh.
+ * @param parentComposite
+ */
+ protected void createButtonsComposite(Composite parentComposite)
+ {
+ Composite composite = _toolkit.createComposite(parentComposite);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ GridLayout layout = new GridLayout(2, true);
+ layout.verticalSpacing = 10;
+ layout.horizontalSpacing = 20;
+ composite.setLayout(layout);
+
+ createAddButton(composite);
+ createRefreshButton(composite);
+ }
+
+ /**
+ * Creates the Add button, which adds the selected item to the navigation view
+ * @param parentComposite
+ */
+ protected void createAddButton(Composite parentComposite)
+ {
+ Button _addButton = _toolkit.createButton(parentComposite, "<- Add to Navigation", SWT.PUSH);
+ GridData gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+ _addButton.setLayoutData(gridData);
+ _addButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (_list.getSelectionCount() == 0)
+ return;
+
+ String[] selectedItems = _list.getSelection();
+ for (int i = 0; i < selectedItems.length; i++)
+ {
+ String name = selectedItems[i];
+ // pass the ManagedBean to the navigation view to be added
+ ManagedBean mbean = _objectsMap.get(name);
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ NavigationView view = (NavigationView)window.getActivePage().findView(NavigationView.ID);
+ try
+ {
+ view.addManagedBean(mbean);
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(mbean, ex);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Creates the Refresh button, which gets syncs the items with the broker server
+ * @param parentComposite
+ */
+ protected void createRefreshButton(Composite parentComposite)
+ {
+ Button _refreshButton = _toolkit.createButton(parentComposite, BUTTON_REFRESH, SWT.PUSH);
+ GridData gridData = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+ gridData.widthHint = 120;
+ _refreshButton.setLayoutData(gridData);
+ _refreshButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent e)
+ {
+ try
+ {
+ // refresh the list from the broker server
+ populateList();
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ });
+ }
+
+ /**
+ * Creates the Composite, which contains the items ( Connections, Exchanges or Queues)
+ * @param parentComposite
+ */
+ protected void createListComposite(Composite parentComposite)
+ {
+ // Composite to contain the item list
+ _listComposite = _toolkit.createComposite(parentComposite);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ _listComposite.setLayoutData(gridData);
+ GridLayout layout = new GridLayout();
+ layout.verticalSpacing = 0;
+ _listComposite.setLayout(layout);
+
+ // Label for item name
+ _labelList = _toolkit.createLabel(_listComposite, " ", SWT.CENTER);
+ gridData = new GridData(SWT.CENTER, SWT.TOP, true, false, 1, 1);
+ _labelList.setLayoutData(gridData);
+ _labelList.setFont(ApplicationRegistry.getFont(FONT_NORMAL));
+
+ _list = new List(_listComposite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+ gridData = new GridData(SWT.FILL, SWT.FILL, true, true,1, 1);
+ _list.setLayoutData(gridData);
+
+ }
+
+ /**
+ * This is called from MBean View to refresh the tab contents
+ * @throws Exception
+ */
+ public void refresh() throws Exception
+ {
+ setLabelValues();
+ populateList();
+ layout();
+ }
+
+ protected void setLabelValues()
+ {
+ _labelName.setText("Type : " + _type);
+ _labelDesc.setText("Select the " + _type + "(s) to add in the Navigation View");
+ _labelList.setText("-- List of " + _type + "s --");
+ }
+
+ protected abstract void populateList() throws Exception;
+
+ public void layout()
+ {
+ _form.layout(true);
+ _form.getBody().layout(true, true);
+ }
+
+ // sets the map with appropriate mbean and name
+ protected String[] getItems(java.util.List<ManagedBean> list)
+ {
+ if (list == null)
+ return new String[0];
+
+ Collections.sort(list, _sorterByName);
+ String[] items = new String[list.size()];
+ int i = 0;
+ for (ManagedBean mbean : list)
+ {
+ items[i++] = mbean.getName();
+ _objectsMap.put(mbean.getName(), mbean);
+ }
+ return items;
+ }
+
+ protected class ComparatorImpl implements java.util.Comparator<AttributeData>
+ {
+ public int compare(AttributeData data1, AttributeData data2)
+ {
+ Integer int1 = Integer.parseInt(data1.getValue().toString());
+ Integer int2 = Integer.parseInt(data2.getValue().toString());
+ return int1.compareTo(int2) * -1;
+ }
+ }
+
+ protected class Sorter implements java.util.Comparator<ManagedBean>
+ {
+ public int compare(ManagedBean mbean1, ManagedBean mbean2)
+ {
+ return mbean1.getName().compareTo(mbean2.getName());
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanView.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanView.java
new file mode 100644
index 0000000000..344c3c4e7f
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanView.java
@@ -0,0 +1,545 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import java.util.HashMap;
+
+import static org.apache.qpid.management.ui.Constants.*;
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ManagedServer;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.apache.qpid.management.ui.exceptions.InfoRequiredException;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.model.AttributeData;
+import org.apache.qpid.management.ui.model.OperationData;
+import org.apache.qpid.management.ui.model.OperationDataModel;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * MBean View create appropriate view based on the user selection on the Navigation View.
+ * Create TabFolder for all MBeans and displays the attribtues and method tabs.
+ * @author Bhupendra Bhardwaj
+ *
+ */
+public class MBeanView extends ViewPart
+{
+ public static final String ID = "org.apache.qpid.management.ui.mbeanView";
+ private static final String CONTROLLER = "CONTROLLER";
+
+ private FormToolkit _toolkit = null;
+ private Form _form = null;
+ private String _formText = APPLICATION_NAME;
+ private static ManagedServer _server = null;
+ private TreeObject _selectedNode = null;
+ private ManagedBean _mbean = null;
+ private static String _virtualHostName = null;
+ // This map contains a TabFolder for each kind of MBean.
+ // TabFolder is mapped with mbeantype(Connection, Queue and Exchange)
+ private HashMap<String, TabFolder> tabFolderMap = new HashMap<String, TabFolder>();
+ private ISelectionListener selectionListener = new SelectionListenerImpl();
+
+ // TabFolder to list all the mbeans for a given mbeantype(eg Connection, Queue, Exchange)
+ private TabFolder typeTabFolder = null;
+
+ private TabFolder notificationTabFolder = null;
+ /*
+ * Listener for the selection events in the navigation view
+ */
+ private class SelectionListenerImpl implements ISelectionListener
+ {
+ public void selectionChanged(IWorkbenchPart part, ISelection sel)
+ {
+ if (!(sel instanceof IStructuredSelection))
+ return;
+
+ IStructuredSelection ss = (IStructuredSelection) sel;
+ _selectedNode = (TreeObject)ss.getFirstElement();
+
+
+ // mbean should be set to null. A selection done on the navigation view can be either an mbean or
+ // an mbeantype. For mbeantype selection(eg Connection, Queue, Exchange) _mbean will remain null.
+ _mbean = null;
+ setInvisible();
+
+ // If a selected node(mbean) gets unregistered from mbean server, mbeanview should
+ // make the tabfolber for that mbean invisible
+ if (_selectedNode == null)
+ return;
+
+ setServer();
+ refreshMBeanView();
+ setFormTitle();
+ }
+ }
+
+ private void setFormTitle()
+ {
+ if (_mbean != null)
+ {
+ _formText = _mbean.getType();
+ if ((_mbean.getVirtualHostName() != null) && (!DEFAULT_VH.equals(_mbean.getVirtualHostName())) )
+ {
+ _formText = _formText.replaceFirst(VIRTUAL_HOST, _mbean.getVirtualHostName());
+ if (_mbean.getName() != null && _mbean.getName().length() != 0)
+ {
+ _formText = _formText + ": " + _mbean.getName();
+ }
+ }
+ }
+ else if ((_selectedNode.getVirtualHost() != null) && (!DEFAULT_VH.equals(_selectedNode.getVirtualHost())))
+ {
+ _formText = _selectedNode.getVirtualHost();
+ }
+ else
+ {
+ _formText = APPLICATION_NAME;
+ }
+ _form.setText(_formText);
+ }
+
+ public void refreshMBeanView()
+ {
+ try
+ {
+ if (NODE_TYPE_SERVER.equals(_selectedNode.getType()) ||
+ NODE_TYPE_DOMAIN.equals(_selectedNode.getType()) )
+ {
+ return;
+ }
+ else if (NODE_TYPE_TYPEINSTANCE.equals(_selectedNode.getType()))
+ {
+ // An virtual host instance is selected
+ refreshTypeTabFolder(typeTabFolder.getItem(0));
+ }
+ else if (NODE_TYPE_MBEANTYPE.equals(_selectedNode.getType()))
+ {
+ refreshTypeTabFolder(_selectedNode.getName());
+ }
+ else if (NOTIFICATIONS.equals(_selectedNode.getType()))
+ {
+ refreshNotificationPage();
+ }
+ else if (MBEAN.equals(_selectedNode.getType()))
+ {
+ _mbean = (ManagedBean)_selectedNode.getManagedObject();
+ showSelectedMBean();
+ }
+
+ _form.layout(true);
+ _form.getBody().layout(true, true);
+ }
+ catch(Exception ex)
+ {
+ MBeanUtility.handleException(_mbean, ex);
+ }
+ }
+
+ /**
+ * Sets the managedServer based on the selection in the navigation view
+ * At any given time MBeanView will be displaying information for an mbean of mbeantype
+ * for a specifiv managed server. This server information will be used by the tab controllers
+ * to get server registry.
+ */
+ private void setServer()
+ {
+ if (NODE_TYPE_SERVER.equals(_selectedNode.getType()) ||
+ NODE_TYPE_DOMAIN.equals(_selectedNode.getType()) )
+ {
+ _server = (ManagedServer)_selectedNode.getManagedObject();
+ _virtualHostName = null;
+ }
+ else
+ {
+ TreeObject parent = _selectedNode.getParent();
+ while (parent != null && !parent.getType().equals(NODE_TYPE_SERVER))
+ {
+ parent = parent.getParent();
+ }
+
+ if (parent != null && parent.getType().equals(NODE_TYPE_SERVER))
+ _server = (ManagedServer)parent.getManagedObject();
+
+ _virtualHostName = _selectedNode.getVirtualHost();
+ }
+ }
+
+ public static ManagedServer getServer()
+ {
+ return _server;
+ }
+
+ public static String getVirtualHost()
+ {
+ return _virtualHostName;
+ }
+
+ private void showSelectedMBean() throws Exception
+ {
+ try
+ {
+ MBeanUtility.getMBeanInfo(_mbean);
+ }
+ catch(Exception ex)
+ {
+ MBeanUtility.handleException(_mbean, ex);
+ return;
+ }
+
+ TabFolder tabFolder = tabFolderMap.get(_mbean.getType());
+ /*
+ * This solution can be used if there are many versions of Qpid running. Otherwise
+ * there is no need to create a tabFolder everytime a bean is selected.
+ if (tabFolder != null && !tabFolder.isDisposed())
+ {
+ tabFolder.dispose();
+ }
+ tabFolder = createTabFolder();
+ */
+ if (tabFolder == null)
+ {
+ tabFolder = createMBeanTabFolder();
+ }
+
+ int tabIndex = 0;
+ if (NOTIFICATIONS.equals(_selectedNode.getType()))
+ {
+ tabIndex = tabFolder.getItemCount() -1;
+ }
+
+ TabItem tab = tabFolder.getItem(tabIndex);
+ // If folder is being set as visible after tab refresh, then the tab
+ // doesn't have the focus.
+ tabFolder.setSelection(tabIndex);
+ refreshTab(tab);
+ setVisible(tabFolder);
+ }
+
+ public void createPartControl(Composite parent)
+ {
+ // Create the Form
+ _toolkit = new FormToolkit(parent.getDisplay());
+ _form = _toolkit.createForm(parent);
+ _form.getBody().setLayout(new FormLayout());
+ _form.setText(APPLICATION_NAME);
+
+ // Add selection listener for selection events in the Navigation view
+ getSite().getPage().addSelectionListener(NavigationView.ID, selectionListener);
+
+ // Add mbeantype TabFolder. This will list all the mbeans under a mbeantype (eg Queue, Exchange).
+ // Using this list mbeans will be added in the navigation view
+ createMBeanTypeTabFolder();
+
+ createNotificationsTabFolder();
+ }
+
+ private TabFolder createMBeanTabFolder()
+ {
+ TabFolder tabFolder = new TabFolder(_form.getBody(), SWT.NONE);
+ FormData layoutData = new FormData();
+ layoutData.left = new FormAttachment(0);
+ layoutData.top = new FormAttachment(0);
+ layoutData.right = new FormAttachment(100);
+ layoutData.bottom = new FormAttachment(100);
+ tabFolder.setLayoutData(layoutData);
+ tabFolder.setVisible(false);
+
+ createAttributesTab(tabFolder);
+ createOperationTabs(tabFolder);
+ createNotificationsTab(tabFolder);
+
+ tabFolder.addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event evt)
+ {
+ TabItem tab = (TabItem)evt.item;
+ refreshTab(tab);
+ }
+ });
+
+ tabFolderMap.put(_mbean.getType(), tabFolder);
+ return tabFolder;
+ }
+
+ private void refreshTab(TabItem tab)
+ {
+ // We can avoid refreshing the attributes tab because it's control
+ // already contains the required values. But it is added for now and
+ // will remove if there is any performance issue or any other issue.
+ // The operations control should be refreshed because there is only one
+ // controller for all operations tab.
+ // The Notifications control needs to refresh with latest set of notifications
+
+ if (tab == null)
+ return;
+
+ TabControl controller = (TabControl)tab.getData(CONTROLLER);
+ controller.refresh(_mbean);
+ }
+
+ public void setFocus()
+ {
+ //_form.setFocus();
+ }
+
+ public void dispose()
+ {
+ _toolkit.dispose();
+ super.dispose();
+ }
+
+ private void createAttributesTab(TabFolder tabFolder)
+ {
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean);
+ if (serverRegistry.getAttributeModel(_mbean).getCount() == 0)
+ {
+ return;
+ }
+
+ TabItem tab = new TabItem(tabFolder, SWT.NONE);
+ tab.setText(ATTRIBUTES);
+ AttributesTabControl controller = new AttributesTabControl(tabFolder);
+ tab.setControl(controller.getControl());
+ tab.setData(CONTROLLER, controller);
+ }
+
+ private void createOperationTabs(TabFolder tabFolder)
+ {
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean);
+ int operationsCount = serverRegistry.getOperationModel(_mbean).getCount();
+ if (operationsCount == 0)
+ {
+ return;
+ }
+
+ OperationDataModel operationModel = serverRegistry.getOperationModel(_mbean);
+ for (OperationData operationData : operationModel.getOperations())
+ {
+ TabItem operationTab = new TabItem(tabFolder, SWT.NONE);
+ operationTab.setText(ViewUtility.getDisplayText(operationData.getName()));
+ operationTab.setData(operationData);
+ OperationTabControl control = new OperationTabControl(tabFolder, operationData);
+ operationTab.setData(CONTROLLER, control);
+ operationTab.setControl(control.getControl());
+ }
+ }
+
+ private void createNotificationsTab(TabFolder tabFolder)
+ {
+ NotificationsTabControl controller = new NotificationsTabControl(tabFolder);
+
+ TabItem tab = new TabItem(tabFolder, SWT.NONE);
+ tab.setText(NOTIFICATIONS);
+ tab.setData(CONTROLLER, controller);
+ tab.setControl(controller.getControl());
+ }
+
+ /**
+ * For the EditAttribtue Action. Invoking this from action is same as clicking
+ * "EditAttribute" button from Attribute tab.
+ */
+ public void editAttribute() throws Exception
+ {
+ if (_mbean == null)
+ throw new InfoRequiredException("Please select the managed object and then attribute to be edited");
+
+ String name = (_mbean.getName() != null) ? _mbean.getName() : _mbean.getType();
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean);
+ if (serverRegistry.getAttributeModel(_mbean).getCount() == 0)
+ {
+ throw new InfoRequiredException("There are no attributes to be edited for " + name);
+ }
+
+ TabFolder tabFolder = tabFolderMap.get(_mbean.getType());
+ int index = tabFolder.getSelectionIndex();
+ if (index != 0)
+ {
+ tabFolder.setSelection(0);
+ throw new InfoRequiredException("Please select the attribute to be edited");
+ }
+
+ TabItem tab = tabFolder.getItem(0);
+ AttributesTabControl tabControl = (AttributesTabControl)tab.getData(CONTROLLER);
+ AttributeData attribute = tabControl.getSelectionAttribute();
+ if (attribute == null)
+ throw new InfoRequiredException("Please select the attribute to be edited");
+
+ tabControl.createDetailsPopup(attribute);
+ }
+
+ /**
+ * Creates TabFolder and tabs for each mbeantype (eg Connection, Queue, Exchange)
+ */
+ private void createMBeanTypeTabFolder()
+ {
+ typeTabFolder = new TabFolder(_form.getBody(), SWT.NONE);
+ FormData layoutData = new FormData();
+ layoutData.left = new FormAttachment(0);
+ layoutData.top = new FormAttachment(0);
+ layoutData.right = new FormAttachment(100);
+ layoutData.bottom = new FormAttachment(100);
+ typeTabFolder.setLayoutData(layoutData);
+ typeTabFolder.setVisible(false);
+
+ TabItem tab = new TabItem(typeTabFolder, SWT.NONE);
+ tab.setText(CONNECTION);
+ MBeanTypeTabControl controller = new ConnectionTypeTabControl(typeTabFolder);
+ tab.setData(CONTROLLER, controller);
+ tab.setControl(controller.getControl());
+
+ tab = new TabItem(typeTabFolder, SWT.NONE);
+ tab.setText(EXCHANGE);
+ controller = new ExchangeTypeTabControl(typeTabFolder);
+ tab.setData(CONTROLLER, controller);
+ tab.setControl(controller.getControl());
+
+ tab = new TabItem(typeTabFolder, SWT.NONE);
+ tab.setText(QUEUE);
+ controller = new QueueTypeTabControl(typeTabFolder);
+ tab.setData(CONTROLLER, controller);
+ tab.setControl(controller.getControl());
+
+ typeTabFolder.addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event evt)
+ {
+ TabItem tab = (TabItem)evt.item;
+ try
+ {
+ refreshTypeTabFolder(tab);
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ });
+ }
+
+ private void createNotificationsTabFolder()
+ {
+ notificationTabFolder = new TabFolder(_form.getBody(), SWT.NONE);
+ FormData layoutData = new FormData();
+ layoutData.left = new FormAttachment(0);
+ layoutData.top = new FormAttachment(0);
+ layoutData.right = new FormAttachment(100);
+ layoutData.bottom = new FormAttachment(100);
+ notificationTabFolder.setLayoutData(layoutData);
+ notificationTabFolder.setVisible(false);
+
+ VHNotificationsTabControl controller = new VHNotificationsTabControl(notificationTabFolder);
+ TabItem tab = new TabItem(notificationTabFolder, SWT.NONE);
+ tab.setText(NOTIFICATIONS);
+ tab.setData(CONTROLLER, controller);
+ tab.setControl(controller.getControl());
+ }
+
+ private void refreshNotificationPage()
+ {
+ TabItem tab = notificationTabFolder.getItem(0);
+ VHNotificationsTabControl controller = (VHNotificationsTabControl)tab.getData(CONTROLLER);
+ controller.refresh();
+ notificationTabFolder.setVisible(true);
+ }
+
+ /**
+ * Refreshes the Selected mbeantype tab. The control lists all the available mbeans
+ * for an mbeantype(eg Queue, Exchange etc)
+ * @param tab
+ * @throws Exception
+ */
+ private void refreshTypeTabFolder(TabItem tab) throws Exception
+ {
+ if (tab == null)
+ {
+ return;
+ }
+ typeTabFolder.setSelection(tab);
+ MBeanTypeTabControl controller = (MBeanTypeTabControl)tab.getData(CONTROLLER);
+ controller.refresh();
+ typeTabFolder.setVisible(true);
+ }
+
+ private void refreshTypeTabFolder(String type) throws Exception
+ {
+ if (CONNECTION.equals(type))
+ {
+ refreshTypeTabFolder(typeTabFolder.getItem(0));
+ }
+ else if (EXCHANGE.equals(type))
+ {
+ refreshTypeTabFolder(typeTabFolder.getItem(1));
+ }
+ else if (QUEUE.equals(type))
+ {
+ refreshTypeTabFolder(typeTabFolder.getItem(2));
+ }
+ }
+
+ /**
+ * hides other folders and makes the given one visible.
+ * @param tabFolder
+ */
+ private void setVisible(TabFolder tabFolder)
+ {
+ for (TabFolder folder : tabFolderMap.values())
+ {
+ if (folder == tabFolder)
+ folder.setVisible(true);
+ else
+ folder.setVisible(false);
+ }
+ }
+
+ private void setInvisible()
+ {
+ for (TabFolder folder : tabFolderMap.values())
+ {
+ folder.setVisible(false);
+ }
+
+ if (typeTabFolder != null)
+ {
+ typeTabFolder.setVisible(false);
+ }
+
+ if (notificationTabFolder != null)
+ {
+ notificationTabFolder.setVisible(false);
+ }
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
new file mode 100644
index 0000000000..1da13a9b56
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
@@ -0,0 +1,1253 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import static org.apache.qpid.management.ui.Constants.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ManagedServer;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.apache.qpid.management.ui.exceptions.InfoRequiredException;
+import org.apache.qpid.management.ui.jmx.JMXServerRegistry;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.ITreeViewerListener;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeExpansionEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * Navigation View for navigating the managed servers and managed beans on
+ * those servers
+ * @author Bhupendra Bhardwaj
+ */
+public class NavigationView extends ViewPart
+{
+ public static final String ID = "org.apache.qpid.management.ui.navigationView";
+ public static final String INI_FILENAME = System.getProperty("user.home") + File.separator + "qpidManagementConsole.ini";
+
+ private static final String INI_SERVERS = "Servers";
+ private static final String INI_QUEUES = QUEUE + "s";
+ private static final String INI_CONNECTIONS = CONNECTION + "s";
+ private static final String INI_EXCHANGES = EXCHANGE + "s";
+
+ private TreeViewer _treeViewer = null;
+ private TreeObject _rootNode = null;
+ private TreeObject _serversRootNode = null;
+
+ private PreferenceStore _preferences;
+ // Map of connected servers
+ private HashMap<ManagedServer, TreeObject> _managedServerMap = new HashMap<ManagedServer, TreeObject>();
+
+ private void createTreeViewer(Composite parent)
+ {
+ _treeViewer = new TreeViewer(parent);
+ _treeViewer.setContentProvider(new ContentProviderImpl());
+ _treeViewer.setLabelProvider(new LabelProviderImpl());
+ _treeViewer.setSorter(new ViewerSorterImpl());
+
+ // layout the tree viewer below the label field, to cover the area
+ GridData layoutData = new GridData();
+ layoutData = new GridData();
+ layoutData.grabExcessHorizontalSpace = true;
+ layoutData.grabExcessVerticalSpace = true;
+ layoutData.horizontalAlignment = GridData.FILL;
+ layoutData.verticalAlignment = GridData.FILL;
+ _treeViewer.getControl().setLayoutData(layoutData);
+ _treeViewer.setUseHashlookup(true);
+
+ createListeners();
+ }
+
+ /**
+ * Creates listeners for the JFace treeviewer
+ */
+ private void createListeners()
+ {
+ _treeViewer.addDoubleClickListener(new IDoubleClickListener()
+ {
+ public void doubleClick(DoubleClickEvent event)
+ {
+ IStructuredSelection ss = (IStructuredSelection) event.getSelection();
+ if ((ss == null) || (ss.getFirstElement() == null))
+ {
+ return;
+ }
+
+ boolean state = _treeViewer.getExpandedState(ss.getFirstElement());
+ _treeViewer.setExpandedState(ss.getFirstElement(), !state);
+ }
+ });
+
+ _treeViewer.addTreeListener(new ITreeViewerListener()
+ {
+ public void treeExpanded(TreeExpansionEvent event)
+ {
+ _treeViewer.setExpandedState(event.getElement(), true);
+ // Following will cause the selection event to be sent, so commented
+ // _treeViewer.setSelection(new StructuredSelection(event.getElement()));
+ _treeViewer.refresh();
+ }
+
+ public void treeCollapsed(TreeExpansionEvent event)
+ {
+ _treeViewer.setExpandedState(event.getElement(), false);
+ _treeViewer.refresh();
+ }
+ });
+
+ // This listener is for popup menu, which pops up if a queue,exchange or connection is selected
+ // with right click.
+ _treeViewer.getTree().addListener(SWT.MenuDetect, new Listener()
+ {
+ Display display = getSite().getShell().getDisplay();
+ final Shell shell = new Shell(display);
+
+ public void handleEvent(Event event)
+ {
+ Tree widget = (Tree) event.widget;
+ TreeItem[] items = widget.getSelection();
+ if (items == null)
+ {
+ return;
+ }
+
+ // Get the selected node
+ final TreeObject selectedNode = (TreeObject) items[0].getData();
+ final TreeObject parentNode = selectedNode.getParent();
+
+ // This popup is only for mbeans and only connection,exchange and queue types
+ if ((parentNode == null) || !MBEAN.equals(selectedNode.getType())
+ || !(CONNECTION.equals(parentNode.getName()) || QUEUE.equals(parentNode.getName())
+ || EXCHANGE.equals(parentNode.getName())))
+ {
+ return;
+ }
+
+ Menu menu = new Menu(shell, SWT.POP_UP);
+ MenuItem item = new MenuItem(menu, SWT.PUSH);
+ // Add the action item, which will remove the node from the tree if selected
+ item.setText(ACTION_REMOVE_MBEANNODE);
+ item.addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event e)
+ {
+ removeManagedObject(parentNode, (ManagedBean) selectedNode.getManagedObject());
+ _treeViewer.refresh();
+ // set the selection to the parent node
+ _treeViewer.setSelection(new StructuredSelection(parentNode));
+ }
+ });
+ menu.setLocation(event.x, event.y);
+ menu.setVisible(true);
+ while (!menu.isDisposed() && menu.isVisible())
+ {
+ if (!display.readAndDispatch())
+ {
+ display.sleep();
+ }
+ }
+
+ menu.dispose();
+ }
+ });
+ }
+
+ /**
+ * Creates Qpid Server connection using JMX RMI protocol
+ * @param server
+ * @throws Exception
+ */
+ private void createRMIServerConnection(ManagedServer server) throws Exception
+ {
+ // Currently Qpid Management Console only supports JMX MBeanServer
+ ServerRegistry serverRegistry = new JMXServerRegistry(server);
+ ApplicationRegistry.addServer(server, serverRegistry);
+ }
+
+ /**
+ * Adds a new server node in the navigation view if server connection is successful.
+ * @param transportProtocol
+ * @param host
+ * @param port
+ * @param domain
+ * @throws Exception
+ */
+ public void addNewServer(String transportProtocol, String host, int port, String domain, String user, String pwd)
+ throws Exception
+ {
+ String serverAddress = host + ":" + port;
+ String url = null;
+ ManagedServer managedServer = new ManagedServer(host, port, domain, user, pwd);
+
+ if ("RMI".equals(transportProtocol))
+ {
+ url = managedServer.getUrl();
+ List<TreeObject> list = _serversRootNode.getChildren();
+ for (TreeObject node : list)
+ {
+ ManagedServer nodeServer = (ManagedServer)node.getManagedObject();
+ if (url.equals(nodeServer.getUrl()))
+ {
+ // Server is already in the list of added servers, so now connect it.
+ // Set the server node as selected and then connect it.
+ _treeViewer.setSelection(new StructuredSelection(node));
+ reconnect(user, pwd);
+
+ return;
+ }
+ }
+
+ // The server is not in the list of already added servers, so now connect and add it.
+ managedServer.setName(serverAddress);
+ createRMIServerConnection(managedServer);
+ }
+ else
+ {
+ throw new InfoRequiredException(transportProtocol + " transport is not supported");
+ }
+
+ // Server connection is successful. Now add the server in the tree
+ TreeObject serverNode = new TreeObject(serverAddress, NODE_TYPE_SERVER);
+ serverNode.setManagedObject(managedServer);
+ _serversRootNode.addChild(serverNode);
+
+ // Add server in the connected server map
+ _managedServerMap.put(managedServer, serverNode);
+
+ // populate the server tree
+ try
+ {
+ populateServer(serverNode);
+ }
+ catch (SecurityException ex)
+ {
+ disconnect(managedServer);
+ throw ex;
+ }
+
+ // Add the Queue/Exchanges/Connections from config file into the navigation tree
+ addConfiguredItems(managedServer);
+
+ _treeViewer.refresh();
+
+ // save server address in file
+ addServerInConfigFile(serverAddress);
+ }
+
+ /**
+ * Create the config file, if it doesn't already exist.
+ * Exits the application if the file could not be created.
+ */
+ private void createConfigFile()
+ {
+ File file = new File(INI_FILENAME);
+ try
+ {
+ if (!file.exists())
+ {
+ file.createNewFile();
+ }
+ }
+ catch (IOException ex)
+ {
+ System.out.println("Could not write to the file " + INI_FILENAME);
+ System.out.println(ex);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Server addresses are stored in a file. When user launches the application again, the
+ * server addresses are picked up from the file and shown in the navigfation view. This method
+ * adds the server address in a file, when a new server is added in the navigation view.
+ * @param serverAddress
+ */
+ private void addServerInConfigFile(String serverAddress)
+ {
+ // Check if the address already exists
+ List<String> list = getServerListFromFile();
+ if ((list != null) && list.contains(serverAddress))
+ {
+ return;
+ }
+
+ // Get the existing server list and add to that
+ String servers = _preferences.getString(INI_SERVERS);
+ String value = (servers.length() != 0) ? (servers + "," + serverAddress) : serverAddress;
+ _preferences.putValue(INI_SERVERS, value);
+ try
+ {
+ _preferences.save();
+ }
+ catch (IOException ex)
+ {
+ System.err.println("Could not add " + serverAddress + " in " + INI_SERVERS + " (" + INI_FILENAME + ")");
+ System.out.println(ex);
+ }
+ }
+
+ /**
+ * Adds the item (Queue/Exchange/Connection) to the config file
+ * @param server
+ * @param virtualhost
+ * @param type - (Queue or Exchange or Connection)
+ * @param name - item name
+ */
+ private void addItemInConfigFile(TreeObject node)
+ {
+ ManagedBean mbean = (ManagedBean) node.getManagedObject();
+ String server = mbean.getServer().getName();
+ String virtualhost = mbean.getVirtualHostName();
+ String type = node.getParent().getName() + "s";
+ String name = node.getName();
+ String itemKey = server + "." + virtualhost + "." + type;
+
+ // Check if the item already exists in the config file
+ List<String> list = getConfiguredItemsFromFile(itemKey);
+ if ((list != null) && list.contains(name))
+ {
+ return;
+ }
+
+ // Add this item to the existing list of items
+ String items = _preferences.getString(itemKey);
+ String value = (items.length() != 0) ? (items + "," + name) : name;
+ _preferences.putValue(itemKey, value);
+ try
+ {
+ _preferences.save();
+ }
+ catch (IOException ex)
+ {
+ System.err.println("Could not add " + name + " in " + itemKey + " (" + INI_FILENAME + ")");
+ System.out.println(ex);
+ }
+ }
+
+ private void removeItemFromConfigFile(TreeObject node)
+ {
+ ManagedBean mbean = (ManagedBean) node.getManagedObject();
+ String server = mbean.getServer().getName();
+ String vHost = mbean.getVirtualHostName();
+ String type = node.getParent().getName() + "s";
+ String itemKey = server + "." + vHost + "." + type;
+
+ List<String> list = getConfiguredItemsFromFile(itemKey);
+ if (list.contains(node.getName()))
+ {
+ list.remove(node.getName());
+ String value = "";
+ for (String item : list)
+ {
+ value += item + ",";
+ }
+
+ value = (value.lastIndexOf(",") != -1) ? value.substring(0, value.lastIndexOf(",")) : value;
+
+ _preferences.putValue(itemKey, value);
+ try
+ {
+ _preferences.save();
+ }
+ catch (IOException ex)
+ {
+ System.err.println("Error in updating the config file " + INI_FILENAME);
+ System.out.println(ex);
+ }
+ }
+ }
+
+ /**
+ * Queries the qpid server for MBeans and populates the navigation view with all MBeans for
+ * the given server node.
+ * @param serverNode
+ */
+ private void populateServer(TreeObject serverNode) throws Exception
+ {
+ ManagedServer server = (ManagedServer) serverNode.getManagedObject();
+ String domain = server.getDomain();
+ if (!domain.equals(ALL))
+ {
+ TreeObject domainNode = new TreeObject(domain, NODE_TYPE_DOMAIN);
+ domainNode.setParent(serverNode);
+
+ populateDomain(domainNode);
+ }
+ else
+ {
+ List<TreeObject> domainList = new ArrayList<TreeObject>();
+ List<String> domains = MBeanUtility.getAllDomains(server);
+
+ for (String domainName : domains)
+ {
+ TreeObject domainNode = new TreeObject(domainName, NODE_TYPE_DOMAIN);
+ domainNode.setParent(serverNode);
+
+ domainList.add(domainNode);
+ populateDomain(domainNode);
+ }
+ }
+ }
+
+ /**
+ * Queries the Qpid Server and populates the given domain node with all MBeans undser that domain.
+ * @param domain
+ * @throws IOException
+ * @throws Exception
+ */
+ @SuppressWarnings("unchecked")
+ private void populateDomain(TreeObject domain) throws IOException, Exception
+ {
+ ManagedServer server = (ManagedServer) domain.getParent().getManagedObject();
+
+ // Now populate the mbenas under those types
+ List<ManagedBean> mbeans = MBeanUtility.getManagedObjectsForDomain(server, domain.getName());
+ for (ManagedBean mbean : mbeans)
+ {
+ mbean.setServer(server);
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(server);
+ serverRegistry.addManagedObject(mbean);
+
+ // Add all mbeans other than Connections, Exchanges and Queues. Because these will be added
+ // manually by selecting from MBeanView
+ if (!(mbean.isConnection() || mbean.isExchange() || mbean.isQueue()))
+ {
+ addManagedBean(domain, mbean);
+ }
+ }
+ // To make it work with the broker without virtual host implementation.
+ // This will add the default nodes to the domain node
+ for (TreeObject child : domain.getChildren())
+ {
+ if (!child.getName().startsWith(VIRTUAL_HOST))
+ {
+ addDefaultNodes(domain);
+ }
+
+ break;
+ }
+ }
+
+ /**
+ * Add these three types - Connection, Exchange, Queue
+ * By adding these, these will always be available, even if there are no mbeans under thse types
+ * This is required because, the mbeans will be added from mbeanview, by selecting from the list
+ * @param parent Node
+ */
+ private void addDefaultNodes(TreeObject parent)
+ {
+ TreeObject typeChild = new TreeObject(CONNECTION, NODE_TYPE_MBEANTYPE);
+ typeChild.setParent(parent);
+ typeChild.setVirtualHost(parent.getVirtualHost());
+ typeChild = new TreeObject(EXCHANGE, NODE_TYPE_MBEANTYPE);
+ typeChild.setParent(parent);
+ typeChild.setVirtualHost(parent.getVirtualHost());
+ typeChild = new TreeObject(QUEUE, NODE_TYPE_MBEANTYPE);
+ typeChild.setParent(parent);
+ typeChild.setVirtualHost(parent.getVirtualHost());
+
+ // Add common notification node for virtual host
+ TreeObject notificationNode = new TreeObject(NOTIFICATIONS, NOTIFICATIONS);
+ notificationNode.setParent(parent);
+ notificationNode.setVirtualHost(parent.getVirtualHost());
+ }
+
+ /**
+ * Checks if a particular mbeantype is already there in the navigation view for a domain.
+ * This is used while populating domain with mbeans.
+ * @param parent
+ * @param typeName
+ * @return Node if given mbeantype already exists, otherwise null
+ */
+ private TreeObject getMBeanTypeNode(TreeObject parent, String typeName)
+ {
+ List<TreeObject> childNodes = parent.getChildren();
+ for (TreeObject child : childNodes)
+ {
+ if ((NODE_TYPE_MBEANTYPE.equals(child.getType()) || NODE_TYPE_TYPEINSTANCE.equals(child.getType()))
+ && typeName.equals(child.getName()))
+ {
+ return child;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean doesMBeanNodeAlreadyExist(TreeObject typeNode, String mbeanName)
+ {
+ List<TreeObject> childNodes = typeNode.getChildren();
+ for (TreeObject child : childNodes)
+ {
+ if (MBEAN.equals(child.getType()) && mbeanName.equals(child.getName()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds the given MBean to the given domain node. Creates Notification node for the MBean.
+ * sample ObjectNames -
+ * org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=localhost
+ * org.apache.qpid:type=VirtualHost.Queue,VirtualHost=test,name=ping_1
+ * @param domain
+ * @param mbean
+ * @throws Exception
+ */
+ private void addManagedBean(TreeObject domain, ManagedBean mbean) // throws Exception
+ {
+ String name = mbean.getName();
+ // Split the mbean type into array of Strings, to create hierarchy
+ // eg. type=VirtualHost.VirtualHostManager,VirtualHost=localhost will be:
+ // localhost->VirtualHostManager
+ // eg. type=org.apache.qpid:type=VirtualHost.Queue,VirtualHost=test,name=ping will be:
+ // test->Queue->ping
+ String[] types = mbean.getType().split("\\.");
+ TreeObject typeNode = null;
+ TreeObject parentNode = domain;
+
+ // Run this loop till all nodes(hierarchy) for this mbean are created. This loop only creates
+ // all the required parent nodes for the mbean
+ for (int i = 0; i < types.length; i++)
+ {
+ String type = types[i];
+ String valueOftype = mbean.getProperty(type);
+ // If value is not null, then there will be a parent node for this mbean
+ // eg. for type=VirtualHost the value is "test"
+ typeNode = getMBeanTypeNode(parentNode, type);
+
+ // create the type node if not already created
+ if (typeNode == null)
+ {
+ // If the ObjectName doesn't have name property, that means there will be only one instance
+ // of this mbean for given "type". So there will be no type node created for this mbean.
+ if ((name == null) && (i == (types.length - 1)))
+ {
+ break;
+ }
+
+ // create a node for "type"
+ typeNode = createTypeNode(parentNode, type);
+ if (!type.equals(VIRTUAL_HOST))
+ {
+ typeNode.setVirtualHost(mbean.getVirtualHostName());
+ }
+ }
+
+ // now type node create becomes the parent node for next node in hierarchy
+ parentNode = typeNode;
+
+ /*
+ * Now create instances node for this type if value exists.
+ */
+ if (valueOftype == null)
+ {
+ // No instance node will be created when value is null (eg type=Queue)
+ break;
+ }
+
+ // For different virtual hosts, the nodes with given value will be created.
+ // eg type=VirtualHost, value=test
+ typeNode = getMBeanTypeNode(parentNode, valueOftype);
+ if (typeNode == null)
+ {
+ typeNode = createTypeInstanceNode(parentNode, valueOftype);
+ typeNode.setVirtualHost(mbean.getVirtualHostName());
+
+ // Create default nodes for VHost instances
+ if (type.equals(VIRTUAL_HOST))
+ {
+ addDefaultNodes(typeNode);
+ }
+ }
+
+ parentNode = typeNode;
+ }
+
+ if (typeNode == null)
+ {
+ typeNode = parentNode;
+ }
+
+ // Check if an MBean is already added
+ if (doesMBeanNodeAlreadyExist(typeNode, name))
+ {
+ return;
+ }
+
+ // Add the mbean node now
+ TreeObject mbeanNode = new TreeObject(mbean);
+ mbeanNode.setParent(typeNode);
+
+ // Add the mbean to the config file
+ if (mbean.isQueue() || mbean.isExchange() || mbean.isConnection())
+ {
+ addItemInConfigFile(mbeanNode);
+ }
+
+ // Add notification node
+ // TODO: show this only if the mbean sends any notification
+ //TreeObject notificationNode = new TreeObject(NOTIFICATION, NOTIFICATION);
+ //notificationNode.setParent(mbeanNode);
+ }
+
+ private TreeObject createTypeNode(TreeObject parent, String name)
+ {
+ TreeObject typeNode = new TreeObject(name, NODE_TYPE_MBEANTYPE);
+ typeNode.setParent(parent);
+
+ return typeNode;
+ }
+
+ private TreeObject createTypeInstanceNode(TreeObject parent, String name)
+ {
+ TreeObject typeNode = new TreeObject(name, NODE_TYPE_TYPEINSTANCE);
+ typeNode.setParent(parent);
+
+ return typeNode;
+ }
+
+ /**
+ * Removes all the child nodes of the given parent node. Used when closing a server.
+ * @param parent
+ */
+ private void removeManagedObject(TreeObject parent)
+ {
+ List<TreeObject> list = parent.getChildren();
+ for (TreeObject child : list)
+ {
+ removeManagedObject(child);
+ }
+
+ list.clear();
+ }
+
+ /**
+ * Removes the mbean from the tree
+ * @param parent
+ * @param mbean
+ */
+ private void removeManagedObject(TreeObject parent, ManagedBean mbean)
+ {
+ List<TreeObject> list = parent.getChildren();
+ TreeObject objectToRemove = null;
+ for (TreeObject child : list)
+ {
+ if (MBEAN.equals(child.getType()))
+ {
+ String name = (mbean.getName() != null) ? mbean.getName() : mbean.getType();
+ if (child.getName().equals(name))
+ {
+ objectToRemove = child;
+
+ break;
+ }
+ }
+ else
+ {
+ removeManagedObject(child, mbean);
+ }
+ }
+
+ if (objectToRemove != null)
+ {
+ list.remove(objectToRemove);
+ removeItemFromConfigFile(objectToRemove);
+ }
+
+ }
+
+ /**
+ * Closes the Qpid server connection
+ */
+ public void disconnect() throws Exception
+ {
+ TreeObject selectedNode = getSelectedServerNode();
+ ManagedServer managedServer = (ManagedServer) selectedNode.getManagedObject();
+ disconnect(managedServer);
+ }
+
+ private void disconnect(ManagedServer managedServer) throws Exception
+ {
+ if (!_managedServerMap.containsKey(managedServer))
+ {
+ return;
+ }
+
+ // Close server connection
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(managedServer);
+ if (serverRegistry == null) // server connection is already closed
+ {
+ return;
+ }
+
+ serverRegistry.closeServerConnection();
+ // Add server to the closed server list and the worker thread will remove the server from required places.
+ ApplicationRegistry.serverConnectionClosed(managedServer);
+ }
+
+ /**
+ * Connects the selected server node
+ * @throws Exception
+ */
+ public void reconnect(String user, String password) throws Exception
+ {
+ TreeObject selectedNode = getSelectedServerNode();
+ ManagedServer managedServer = (ManagedServer) selectedNode.getManagedObject();
+ if (_managedServerMap.containsKey(managedServer))
+ {
+ throw new InfoRequiredException("Server " + managedServer.getName() + " is already connected");
+ }
+
+ managedServer.setUser(user);
+ managedServer.setPassword(password);
+ createRMIServerConnection(managedServer);
+
+ // put the server in the managed server map
+ _managedServerMap.put(managedServer, selectedNode);
+
+ try
+ {
+ // populate the server tree now
+ populateServer(selectedNode);
+ }
+ catch (SecurityException ex)
+ {
+ disconnect(managedServer);
+ throw ex;
+ }
+
+
+ // Add the Queue/Exchanges/Connections from config file into the navigation tree
+ addConfiguredItems(managedServer);
+
+ _treeViewer.refresh();
+ }
+
+ /**
+ * Adds the items(queues/exchanges/connectins) from config file to the server tree
+ * @param server
+ */
+ private void addConfiguredItems(ManagedServer server)
+ {
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(server);
+ List<String> list = serverRegistry.getVirtualHosts();
+ for (String virtualHost : list)
+ {
+ // Add Queues
+ String itemKey = server.getName() + "." + virtualHost + "." + INI_QUEUES;
+ List<String> items = getConfiguredItemsFromFile(itemKey);
+ List<ManagedBean> mbeans = serverRegistry.getQueues(virtualHost);
+ addConfiguredItems(items, mbeans);
+
+ // Add Exchanges
+ itemKey = server.getName() + "." + virtualHost + "." + INI_EXCHANGES;
+ items = getConfiguredItemsFromFile(itemKey);
+ mbeans = serverRegistry.getExchanges(virtualHost);
+ addConfiguredItems(items, mbeans);
+
+ // Add Connections
+ itemKey = server.getName() + "." + virtualHost + "." + INI_CONNECTIONS;
+ items = getConfiguredItemsFromFile(itemKey);
+ mbeans = serverRegistry.getConnections(virtualHost);
+ addConfiguredItems(items, mbeans);
+ }
+ }
+
+ /**
+ * Gets the mbeans corresponding to the items and adds those to the navigation tree
+ * @param items
+ * @param mbeans
+ */
+ private void addConfiguredItems(List<String> items, List<ManagedBean> mbeans)
+ {
+ if ((items == null) || (items.isEmpty() | (mbeans == null)) || mbeans.isEmpty())
+ {
+ return;
+ }
+
+ for (String item : items)
+ {
+ for (ManagedBean mbean : mbeans)
+ {
+ if (item.equals(mbean.getName()))
+ {
+ addManagedBean(mbean);
+
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Closes the Qpid server connection if not already closed and removes the server node from the navigation view and
+ * also from the ini file stored in the system.
+ * @throws Exception
+ */
+ public void removeServer() throws Exception
+ {
+ disconnect();
+
+ // Remove from the Tree
+ String serverNodeName = getSelectedServerNode().getName();
+ List<TreeObject> list = _serversRootNode.getChildren();
+ TreeObject objectToRemove = null;
+ for (TreeObject child : list)
+ {
+ if (child.getName().equals(serverNodeName))
+ {
+ objectToRemove = child;
+
+ break;
+ }
+ }
+
+ if (objectToRemove != null)
+ {
+ list.remove(objectToRemove);
+ }
+
+ _treeViewer.refresh();
+
+ // Remove from the ini file
+ removeServerFromConfigFile(serverNodeName);
+ }
+
+ private void removeServerFromConfigFile(String serverNodeName)
+ {
+ List<String> serversList = getServerListFromFile();
+ serversList.remove(serverNodeName);
+
+ String value = "";
+ for (String item : serversList)
+ {
+ value += item + ",";
+ }
+
+ value = (value.lastIndexOf(",") != -1) ? value.substring(0, value.lastIndexOf(",")) : value;
+
+ _preferences.putValue(INI_SERVERS, value);
+
+ try
+ {
+ _preferences.save();
+ }
+ catch (IOException ex)
+ {
+ System.err.println("Error in updating the config file " + INI_FILENAME);
+ System.out.println(ex);
+ }
+ }
+
+ /**
+ * @return the server addresses from the ini file
+ * @throws Exception
+ */
+ private List<String> getServerListFromFile()
+ {
+ return getConfiguredItemsFromFile(INI_SERVERS);
+ }
+
+ /**
+ * Returns the list of items from the config file.
+ * sample ini file:
+ * Servers=localhost:8999,127.0.0.1:8999
+ * localhost.virtualhost1.Queues=queue1,queue2
+ * localhost.virtualhost1.Exchanges=exchange1,exchange2
+ * localhost.virtualhost2.Connections=conn1
+ * @param key
+ * @return
+ */
+ private List<String> getConfiguredItemsFromFile(String key)
+ {
+ List<String> list = new ArrayList<String>();
+ String items = _preferences.getString(key);
+ if (items.length() != 0)
+ {
+ String[] array = items.split(",");
+ for (String item : array)
+ {
+ list.add(item);
+ }
+ }
+
+ return list;
+ }
+
+ public TreeObject getSelectedServerNode() throws Exception
+ {
+ IStructuredSelection ss = (IStructuredSelection) _treeViewer.getSelection();
+ TreeObject selectedNode = (TreeObject) ss.getFirstElement();
+ if (ss.isEmpty() || (selectedNode == null) || (!selectedNode.getType().equals(NODE_TYPE_SERVER)))
+ {
+ throw new InfoRequiredException("Please select the server");
+ }
+
+ return selectedNode;
+ }
+
+ /**
+ * This is a callback that will allow us to create the viewer and initialize
+ * it.
+ */
+ public void createPartControl(Composite parent)
+ {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginHeight = 2;
+ gridLayout.marginWidth = 2;
+ gridLayout.horizontalSpacing = 0;
+ gridLayout.verticalSpacing = 2;
+ composite.setLayout(gridLayout);
+
+ createTreeViewer(composite);
+ _rootNode = new TreeObject("ROOT", "ROOT");
+ _serversRootNode = new TreeObject(NAVIGATION_ROOT, "ROOT");
+ _serversRootNode.setParent(_rootNode);
+
+ _treeViewer.setInput(_rootNode);
+ // set viewer as selection event provider for MBeanView
+ getSite().setSelectionProvider(_treeViewer);
+
+ // Start worker thread to refresh tree for added or removed objects
+ (new Thread(new Worker())).start();
+
+ createConfigFile();
+ _preferences = new PreferenceStore(INI_FILENAME);
+
+ try
+ {
+ _preferences.load();
+ }
+ catch (IOException ex)
+ {
+ System.out.println(ex);
+ }
+
+ // load the list of servers already added from file
+ List<String> serversList = getServerListFromFile();
+ if (serversList != null)
+ {
+ for (String serverAddress : serversList)
+ {
+ String[] server = serverAddress.split(":");
+ ManagedServer managedServer = new ManagedServer(server[0], Integer.parseInt(server[1]), "org.apache.qpid");
+ TreeObject serverNode = new TreeObject(serverAddress, NODE_TYPE_SERVER);
+ serverNode.setManagedObject(managedServer);
+ _serversRootNode.addChild(serverNode);
+ }
+ }
+
+ _treeViewer.refresh();
+
+ }
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ public void setFocus()
+ { }
+
+ public void refresh()
+ {
+ _treeViewer.refresh();
+ }
+
+ /**
+ * Content provider class for the tree viewer
+ */
+ private class ContentProviderImpl implements ITreeContentProvider
+ {
+ public Object[] getElements(Object parent)
+ {
+ return getChildren(parent);
+ }
+
+ public Object[] getChildren(final Object parentElement)
+ {
+ final TreeObject node = (TreeObject) parentElement;
+
+ return node.getChildren().toArray(new TreeObject[0]);
+ }
+
+ public Object getParent(final Object element)
+ {
+ final TreeObject node = (TreeObject) element;
+
+ return node.getParent();
+ }
+
+ public boolean hasChildren(final Object element)
+ {
+ final TreeObject node = (TreeObject) element;
+
+ return !node.getChildren().isEmpty();
+ }
+
+ public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput)
+ {
+ // Do nothing
+ }
+
+ public void dispose()
+ {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Label provider class for the tree viewer
+ */
+ private class LabelProviderImpl extends LabelProvider implements IFontProvider
+ {
+ public Image getImage(Object element)
+ {
+ TreeObject node = (TreeObject) element;
+ if (node.getType().equals(NOTIFICATIONS))
+ {
+ return ApplicationRegistry.getImage(NOTIFICATION_IMAGE);
+ }
+ else if (!node.getType().equals(MBEAN))
+ {
+ if (_treeViewer.getExpandedState(node))
+ {
+ return ApplicationRegistry.getImage(OPEN_FOLDER_IMAGE);
+ }
+ else
+ {
+ return ApplicationRegistry.getImage(CLOSED_FOLDER_IMAGE);
+ }
+
+ }
+ else
+ {
+ return ApplicationRegistry.getImage(MBEAN_IMAGE);
+ }
+ }
+
+ public String getText(Object element)
+ {
+ TreeObject node = (TreeObject) element;
+ if (node.getType().equals(NODE_TYPE_MBEANTYPE))
+ {
+ return node.getName() + "s";
+ }
+ else
+ {
+ return node.getName();
+ }
+ }
+
+ public Font getFont(Object element)
+ {
+ TreeObject node = (TreeObject) element;
+ if (node.getType().equals(NODE_TYPE_SERVER))
+ {
+ if (node.getChildren().isEmpty())
+ {
+ return ApplicationRegistry.getFont(FONT_NORMAL);
+ }
+ else
+ {
+ return ApplicationRegistry.getFont(FONT_BOLD);
+ }
+ }
+
+ return ApplicationRegistry.getFont(FONT_NORMAL);
+ }
+ } // End of LabelProviderImpl
+
+ private class ViewerSorterImpl extends ViewerSorter
+ {
+ public int category(Object element)
+ {
+ TreeObject node = (TreeObject) element;
+ if (node.getType().equals(MBEAN))
+ {
+ return 1;
+ }
+ if (node.getType().equals(NOTIFICATIONS))
+ {
+ return 2;
+ }
+ return 3;
+ }
+ }
+
+ /**
+ * Worker thread, which keeps looking for new ManagedObjects to be added and
+ * unregistered objects to be removed from the tree.
+ * @author Bhupendra Bhardwaj
+ */
+ private class Worker implements Runnable
+ {
+ public void run()
+ {
+ while (true)
+ {
+ if (!_managedServerMap.isEmpty())
+ {
+ refreshRemovedObjects();
+ refreshClosedServerConnections();
+ }
+
+ try
+ {
+ Thread.sleep(3000);
+ }
+ catch (Exception ex)
+ { }
+
+ } // end of while loop
+ } // end of run method.
+ } // end of Worker class
+
+ /**
+ * Adds the mbean to the navigation tree
+ * @param mbean
+ * @throws Exception
+ */
+ public void addManagedBean(ManagedBean mbean) // throws Exception
+ {
+ TreeObject treeServerObject = _managedServerMap.get(mbean.getServer());
+ List<TreeObject> domains = treeServerObject.getChildren();
+ TreeObject domain = null;
+ for (TreeObject child : domains)
+ {
+ if (child.getName().equals(mbean.getDomain()))
+ {
+ domain = child;
+
+ break;
+ }
+ }
+
+ addManagedBean(domain, mbean);
+ _treeViewer.refresh();
+ }
+
+ private void refreshRemovedObjects()
+ {
+ for (ManagedServer server : _managedServerMap.keySet())
+ {
+ final ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(server);
+ if (serverRegistry == null) // server connection is closed
+ {
+ continue;
+ }
+
+ final List<ManagedBean> removalList = serverRegistry.getObjectsToBeRemoved();
+ if (removalList != null)
+ {
+ Display display = getSite().getShell().getDisplay();
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ for (ManagedBean mbean : removalList)
+ {
+ TreeObject treeServerObject = _managedServerMap.get(mbean.getServer());
+ List<TreeObject> domains = treeServerObject.getChildren();
+ TreeObject domain = null;
+ for (TreeObject child : domains)
+ {
+ if (child.getName().equals(mbean.getDomain()))
+ {
+ domain = child;
+
+ break;
+ }
+ }
+
+ removeManagedObject(domain, mbean);
+ // serverRegistry.removeManagedObject(mbean);
+ }
+
+ _treeViewer.refresh();
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Gets the list of closed server connection from the ApplicationRegistry and then removes
+ * the closed server nodes from the navigation view
+ */
+ private void refreshClosedServerConnections()
+ {
+ final List<ManagedServer> closedServers = ApplicationRegistry.getClosedServers();
+ if (closedServers != null)
+ {
+ Display display = getSite().getShell().getDisplay();
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ for (ManagedServer server : closedServers)
+ {
+ removeManagedObject(_managedServerMap.get(server));
+ _managedServerMap.remove(server);
+ ApplicationRegistry.removeServer(server);
+ }
+
+ _treeViewer.refresh();
+ }
+ });
+ }
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NotificationsTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NotificationsTabControl.java
new file mode 100644
index 0000000000..6894080859
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NotificationsTabControl.java
@@ -0,0 +1,427 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import static org.apache.qpid.management.ui.Constants.BUTTON_CLEAR;
+import static org.apache.qpid.management.ui.Constants.BUTTON_REFRESH;
+import static org.apache.qpid.management.ui.Constants.DESCRIPTION;
+import static org.apache.qpid.management.ui.Constants.FONT_BOLD;
+import static org.apache.qpid.management.ui.Constants.FONT_BUTTON;
+import static org.apache.qpid.management.ui.Constants.FONT_ITALIC;
+import static org.apache.qpid.management.ui.Constants.SUBSCRIBE_BUTTON;
+import static org.apache.qpid.management.ui.Constants.UNSUBSCRIBE_BUTTON;
+
+import java.util.List;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.model.NotificationInfoModel;
+import org.apache.qpid.management.ui.model.NotificationObject;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.TabFolder;
+
+/**
+ * Creates control composite for Notifications tab
+ * @author Bhupendra Bhardwaj
+ */
+public class NotificationsTabControl extends VHNotificationsTabControl
+{
+ private static final String SELECT_NOTIFICATIONNAME = "Select Notification";
+ private static final String SELECT_NOTIFICATIONTYPE = "Select Type";
+ private SelectionListener selectionListener;
+ private SelectionListener comboListener;
+
+ private Combo notificationNameCombo = null;
+ private Combo typesCombo = null;
+ private Label descriptionLabel = null;
+ private Button _subscribeButton = null;
+ private Button _unsubscribeButton = null;
+
+ public NotificationsTabControl(TabFolder tabFolder)
+ {
+ super(tabFolder);
+ }
+
+ protected void createWidgets()
+ {
+ selectionListener = new SelectionListenerImpl();
+ comboListener = new ComboSelectionListener();
+ createNotificationInfoComposite();
+ //addFilterComposite();
+ addButtons();
+ createTableViewer();
+ }
+
+ /**
+ * Creates composite and populates for displaying Notification Information (name, type, description)
+ * and creates buttons for subscribing or unsubscribing for notifications
+ */
+ private void createNotificationInfoComposite()
+ {
+ Composite composite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ composite.setLayout(new FormLayout());
+
+ Label label = _toolkit.createLabel(composite, "Select the notification to subscribe or unsubscribe");
+ label.setFont(ApplicationRegistry.getFont(FONT_BOLD));
+ FormData formData = new FormData();
+ formData.top = new FormAttachment(0, 10);
+ formData.left = new FormAttachment(0, 10);
+ label.setLayoutData(formData);
+
+ notificationNameCombo = new Combo(composite, SWT.READ_ONLY | SWT.DROP_DOWN);
+ formData = new FormData();
+ formData.top = new FormAttachment(label, 10);
+ formData.left = new FormAttachment(0, 10);
+ formData.right = new FormAttachment(40);
+ notificationNameCombo.setLayoutData(formData);
+ notificationNameCombo.addSelectionListener(comboListener);
+
+ typesCombo = new Combo(composite, SWT.READ_ONLY | SWT.DROP_DOWN);
+ formData = new FormData();
+ formData.top = new FormAttachment(label, 10);
+ formData.left = new FormAttachment(notificationNameCombo, 5);
+ formData.right = new FormAttachment(65);
+ typesCombo.setLayoutData(formData);
+ typesCombo.addSelectionListener(comboListener);
+
+ _subscribeButton = new Button(composite, SWT.PUSH | SWT.CENTER);
+ _subscribeButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ _subscribeButton.setText(SUBSCRIBE_BUTTON);
+ formData = new FormData();
+ formData.top = new FormAttachment(label, 10);
+ formData.left = new FormAttachment(65, 10);
+ formData.width = 80;
+ _subscribeButton.setLayoutData(formData);
+ _subscribeButton.addSelectionListener(selectionListener);
+
+ _unsubscribeButton = new Button(composite, SWT.PUSH | SWT.CENTER);
+ _unsubscribeButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ _unsubscribeButton.setText(UNSUBSCRIBE_BUTTON);
+ formData = new FormData();
+ formData.top = new FormAttachment(label, 10);
+ formData.left = new FormAttachment(_subscribeButton, 10);
+ formData.width = 80;
+ _unsubscribeButton.setLayoutData(formData);
+ _unsubscribeButton.addSelectionListener(selectionListener);
+
+ Label fixedLabel = _toolkit.createLabel(composite, "");
+ formData = new FormData();
+ formData.top = new FormAttachment(notificationNameCombo, 5);
+ formData.left = new FormAttachment(0, 10);
+ fixedLabel.setLayoutData(formData);
+ fixedLabel.setText(DESCRIPTION + " : ");
+ fixedLabel.setFont(ApplicationRegistry.getFont(FONT_BOLD));
+
+ descriptionLabel = _toolkit.createLabel(composite, "");
+ formData = new FormData();
+ formData.top = new FormAttachment(notificationNameCombo, 5);
+ formData.left = new FormAttachment(fixedLabel, 10);
+ formData.right = new FormAttachment(100);
+ descriptionLabel.setLayoutData(formData);
+ descriptionLabel.setText(" ");
+ descriptionLabel.setFont(ApplicationRegistry.getFont(FONT_ITALIC));
+ }
+
+ /**
+ * Creates clear buttin and refresh button
+ */
+ protected void addButtons()
+ {
+ Composite composite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ composite.setLayout(new GridLayout(2, true));
+
+ // Add Clear Button
+ _clearButton = _toolkit.createButton(composite, BUTTON_CLEAR, SWT.PUSH | SWT.CENTER);
+ _clearButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ GridData gridData = new GridData(SWT.LEAD, SWT.TOP, true, false);
+ gridData.widthHint = 80;
+ _clearButton.setLayoutData(gridData);
+ _clearButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (_mbean == null)
+ return;
+
+ IStructuredSelection ss = (IStructuredSelection)_tableViewer.getSelection();
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean);
+ serverRegistry.clearNotifications(_mbean, ss.toList());
+ refresh();
+ }
+ });
+
+ // Add Refresh Button
+ _refreshButton = _toolkit.createButton(composite, BUTTON_REFRESH, SWT.PUSH | SWT.CENTER);
+ _refreshButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ gridData = new GridData(SWT.TRAIL, SWT.TOP, true, false);
+ gridData.widthHint = 80;
+ _refreshButton.setLayoutData(gridData);
+ _refreshButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (_mbean == null)
+ return;
+
+ refresh();
+ }
+ });
+ }
+
+ @Override
+ public void refresh(ManagedBean mbean)
+ {
+ _mbean = mbean;
+ _notifications = null;
+ _table.deselectAll();
+ _tableViewer.getTable().clearAll();
+
+ if (_mbean == null)
+ {
+ _tableViewer.getTable().clearAll();
+ _subscribeButton.setEnabled(false);
+ _unsubscribeButton.setEnabled(false);
+ return;
+ }
+
+ if (!doesMBeanSendsNotification())
+ {
+ Control[] children = _form.getBody().getChildren();
+ for (int i = 0; i < children.length; i++)
+ {
+ children[i].setVisible(false);
+ }
+
+ String name = (_mbean.getName() != null) ? _mbean.getName() : _mbean.getType();
+ _form.setText(name + " does not send any notification");
+ return;
+ }
+
+ Control[] children = _form.getBody().getChildren();
+ for (int i = 0; i < children.length; i++)
+ {
+ children[i].setVisible(true);
+ }
+
+ populateNotificationInfo();
+ workerRunning = true;
+ _form.layout(true);
+ _form.getBody().layout(true, true);
+ }
+
+ public void refresh()
+ {
+ _notifications = null;
+ _table.deselectAll();
+ _tableViewer.getTable().clearAll();
+ }
+
+ /**
+ * Fills the notification information widgets for selected mbean
+ */
+ private void populateNotificationInfo()
+ {
+ notificationNameCombo.removeAll();
+ NotificationInfoModel[] items = MBeanUtility.getNotificationInfo(_mbean);
+ if (items.length > 1)
+ {
+ notificationNameCombo.add(SELECT_NOTIFICATIONNAME);
+ }
+
+ for (int i = 0; i < items.length; i++)
+ {
+ notificationNameCombo.add(items[i].getName());
+ notificationNameCombo.setData(items[i].getName(), items[i]);
+ }
+ notificationNameCombo.select(0);
+
+ typesCombo.removeAll();
+ typesCombo.add("Select Type", 0);
+ typesCombo.select(0);
+ typesCombo.setEnabled(false);
+
+ populateNotificationType(notificationNameCombo.getItem(0));
+ checkForEnablingButtons();
+ }
+
+ /**
+ * Checks and the enabing/disabling of buttons
+ */
+ private void checkForEnablingButtons()
+ {
+ int nameIndex = notificationNameCombo.getSelectionIndex();
+ int itemCount = notificationNameCombo.getItems().length;
+ if ((itemCount > 1) && (nameIndex == 0))
+ {
+ _subscribeButton.setEnabled(false);
+ _unsubscribeButton.setEnabled(false);
+ descriptionLabel.setText("");
+ return;
+ }
+
+ int typeIndex = typesCombo.getSelectionIndex();
+ itemCount = typesCombo.getItems().length;
+ if ((itemCount > 1) && (typeIndex == 0))
+ {
+ _subscribeButton.setEnabled(false);
+ _unsubscribeButton.setEnabled(false);
+ return;
+ }
+
+ String type = typesCombo.getItem(typeIndex);
+ String name = notificationNameCombo.getItem(nameIndex);
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean);
+
+ if (serverRegistry.hasSubscribedForNotifications(_mbean, name, type))
+ {
+ _subscribeButton.setEnabled(false);
+ _unsubscribeButton.setEnabled(true);
+ }
+ else
+ {
+ _subscribeButton.setEnabled(true);
+ _unsubscribeButton.setEnabled(false);
+ }
+ }
+
+ private boolean doesMBeanSendsNotification()
+ {
+ NotificationInfoModel[] items = MBeanUtility.getNotificationInfo(_mbean);
+ if (items == null || items.length == 0)
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * Selection listener for subscribing or unsubscribing the notifications
+ */
+ private class SelectionListenerImpl extends SelectionAdapter
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (_mbean == null)
+ return;
+
+ Button source = (Button)e.getSource();
+ String type = typesCombo.getItem(typesCombo.getSelectionIndex());
+ String name = notificationNameCombo.getItem(notificationNameCombo.getSelectionIndex());
+ if (source == _unsubscribeButton)
+ {
+ try
+ {
+ MBeanUtility.removeNotificationListener(_mbean, name, type);
+ }
+ catch(Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ else if (source == _subscribeButton)
+ {
+ try
+ {
+ MBeanUtility.createNotificationlistener(_mbean, name, type);
+ }
+ catch(Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ checkForEnablingButtons();
+ }
+ }
+
+ /**
+ * Selection listener class for the Notification Name. The notification type and description will be
+ * displayed accordingly
+ */
+ private class ComboSelectionListener extends SelectionAdapter
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (_mbean == null)
+ return;
+
+ Combo combo = (Combo)e.getSource();
+ if (combo == notificationNameCombo)
+ {
+ String selectedItem = combo.getItem(combo.getSelectionIndex());
+ populateNotificationType(selectedItem);
+ }
+ checkForEnablingButtons();
+ }
+ }
+
+ private void populateNotificationType(String notificationName)
+ {
+ NotificationInfoModel data = (NotificationInfoModel)notificationNameCombo.getData(notificationName);
+ if (data == null)
+ {
+ descriptionLabel.setText("");
+ typesCombo.select(0);
+ typesCombo.setEnabled(false);
+ return;
+ }
+ descriptionLabel.setText(data.getDescription());
+ typesCombo.removeAll();
+ typesCombo.setItems(data.getTypes());
+ if (typesCombo.getItemCount() > 1)
+ {
+ typesCombo.add(SELECT_NOTIFICATIONTYPE, 0);
+ }
+ typesCombo.select(0);
+ typesCombo.setEnabled(true);
+ }
+
+ /**
+ * Updates the table with new notifications received from mbean server for the selected mbean
+ */
+ protected void updateTableViewer()
+ {
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(_mbean);
+ List<NotificationObject> newList = serverRegistry.getNotifications(_mbean);
+ if (newList == null)
+ return;
+
+ _notifications = newList;
+ _tableViewer.setInput(_notifications);
+ _tableViewer.refresh();
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NumberVerifyListener.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NumberVerifyListener.java
new file mode 100644
index 0000000000..1774209dae
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NumberVerifyListener.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+
+/**
+ * Implementation of VeryfyListener for numeric values
+ * @author Bhupendra Bhardwaj
+ */
+public class NumberVerifyListener implements VerifyListener
+{
+ public void verifyText(VerifyEvent event)
+ {
+ String string = event.text;
+ char [] chars = new char [string.length ()];
+ string.getChars (0, chars.length, chars, 0);
+ for (int i=0; i<chars.length; i++)
+ {
+ if (!('0' <= chars [i] && chars [i] <= '9'))
+ {
+ event.doit = false;
+ return;
+ }
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java
new file mode 100644
index 0000000000..f2cd594684
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java
@@ -0,0 +1,899 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularDataSupport;
+
+import static org.apache.qpid.management.ui.Constants.*;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.model.OperationData;
+import org.apache.qpid.management.ui.model.ParameterData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+
+/**
+ * Control class for the MBean operations tab. It creates the required widgets
+ * for the selected MBean.
+ * @author Bhupendra Bhardwaj
+ */
+public class OperationTabControl extends TabControl
+{
+ private static final int heightForAParameter = 30;
+ private static final int labelWidth = 30;
+ private static final int valueWidth = labelWidth + 25;
+
+ private FormToolkit _toolkit;
+ private Form _form;
+ private OperationData _opData;
+
+ private SelectionListener operationExecutionListener = new OperationExecutionListener();
+ private SelectionListener refreshListener = new RefreshListener();
+ private SelectionListener parameterSelectionListener = new ParameterSelectionListener();
+ private SelectionListener booleanSelectionListener = new BooleanSelectionListener();
+ private VerifyListener verifyListener = new VerifyListenerImpl();
+ private KeyListener keyListener = new KeyListenerImpl();
+ private KeyListener headerBindingListener = new HeaderBindingKeyListener();
+
+ private Composite _headerComposite = null;
+ private Composite _paramsComposite = null;
+ private Composite _resultsComposite = null;
+ private Button _executionButton = null;
+
+ // for customized method in header exchange
+ private HashMap<Text, Text> headerBindingHashMap = null;
+ private String _virtualHostName = null;
+
+ public OperationTabControl(TabFolder tabFolder, OperationData opData)
+ {
+ super(tabFolder);
+ _toolkit = new FormToolkit(_tabFolder.getDisplay());
+ _form = _toolkit.createForm(_tabFolder);
+ _form.getBody().setLayout(new GridLayout());
+ _opData = opData;
+ createComposites();
+ setHeader();
+ }
+
+ /**
+ * Form area is devided in four parts:
+ * Header composite - displays operaiton information
+ * Patameters composite - displays parameters if there
+ * Button - operation execution button
+ * Results composite - displays results for operations, which have
+ * no parameters but have some return value
+ */
+ private void createComposites()
+ {
+ //
+ _headerComposite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
+ _headerComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ List<ParameterData> params = _opData.getParameters();
+ if (params != null && !params.isEmpty())
+ {
+ _paramsComposite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
+ _paramsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ }
+ _executionButton = _toolkit.createButton(_form.getBody(), BUTTON_EXECUTE, SWT.PUSH | SWT.CENTER);
+ _executionButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ GridData layoutData = new GridData(SWT.CENTER, SWT.TOP, true, false);
+ layoutData.verticalIndent = 20;
+ _executionButton.setLayoutData(layoutData);
+
+ _resultsComposite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ layoutData.verticalIndent = 20;
+ _resultsComposite.setLayoutData(layoutData);
+ _resultsComposite.setLayout(new GridLayout());
+ }
+
+ /**
+ * @see TabControl#getControl()
+ */
+ public Control getControl()
+ {
+ return _form;
+ }
+
+ @Override
+ public void refresh(ManagedBean mbean)
+ {
+ _mbean = mbean;
+ _virtualHostName = _mbean.getVirtualHostName();
+
+ // Setting the form to be invisible. Just in case the mbean server connection
+ // is done and it takes time in getting the response, then the ui should be blank
+ // instead of having half the widgets displayed.
+ _form.setVisible(false);
+
+ ViewUtility.disposeChildren(_paramsComposite);
+ createParameterWidgets();
+
+ // Set button text and add appropriate listener to button.
+ // If there are no parameters and it is info operation, then operation gets executed
+ // and result is displayed
+ List<ParameterData> params = _opData.getParameters();
+ if (params != null && !params.isEmpty())
+ {
+ setButton(BUTTON_EXECUTE);
+ }
+ else if (_opData.getImpact() == OPERATION_IMPACT_ACTION)
+ {
+ setButton(BUTTON_EXECUTE);
+ }
+ else if (_opData.getImpact() == OPERATION_IMPACT_INFO)
+ {
+ setButton(BUTTON_REFRESH);
+ executeAndShowResults();
+ }
+
+ _form.setVisible(true);
+ layout();
+ }
+
+ public void layout()
+ {
+ _form.layout(true);
+ _form.getBody().layout(true, true);
+ }
+
+ /**
+ * populates the header composite, containing the operation name and description.
+ */
+ private void setHeader()
+ {
+ _form.setText(ViewUtility.getDisplayText(_opData.getName()));
+ _headerComposite.setLayout(new GridLayout(2, false));
+ //operation description
+ Label label = _toolkit.createLabel(_headerComposite, DESCRIPTION + " : ");
+ label.setFont(ApplicationRegistry.getFont(FONT_BOLD));
+ label.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false));
+
+ label = _toolkit.createLabel(_headerComposite, _opData.getDescription());
+ label.setFont(ApplicationRegistry.getFont(FONT_NORMAL));
+ label.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, true, false));
+
+ _headerComposite.layout();
+ }
+
+ /**
+ * Creates the widgets for operation parameters if there are any
+ */
+ private void createParameterWidgets()
+ {
+ List<ParameterData> params = _opData.getParameters();
+ if (params == null || params.isEmpty())
+ {
+ return;
+ }
+
+ // Customised parameter widgets
+ if (_mbean.isExchange() &&
+ EXCHANGE_TYPE_VALUES[2].equals(_mbean.getProperty(EXCHANGE_TYPE)) &&
+ _opData.getName().equalsIgnoreCase(OPERATION_CREATE_BINDING))
+ {
+ customCreateNewBinding();
+ return;
+ }
+ // end of Customised parameter widgets
+
+ _paramsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ _paramsComposite.setLayout(new FormLayout());
+ int parameterPositionOffset = 0;
+ for (ParameterData param : params)
+ {
+ boolean valueInCombo = false;
+ Label label = _toolkit.createLabel(_paramsComposite, ViewUtility.getDisplayText(param.getName()));
+ FormData formData = new FormData();
+ if (params.indexOf(param) == 0)
+ {
+ parameterPositionOffset = 0;
+ }
+ else
+ {
+ parameterPositionOffset += heightForAParameter;
+ }
+ formData.top = new FormAttachment(0, parameterPositionOffset + 2);
+ formData.right = new FormAttachment(labelWidth);
+ label.setLayoutData(formData);
+ label.setToolTipText(param.getDescription());
+
+ formData = new FormData();
+ formData.top = new FormAttachment(0, parameterPositionOffset);
+ formData.left = new FormAttachment(label, 5);
+ formData.right = new FormAttachment(valueWidth);
+ // this will contain the list of items, if the list is to be made available to choose from
+ // e.g. the list of exchanges
+ String[] items = null;
+ if (param.getName().equals(QUEUE))
+ {
+ List<String> qList = ApplicationRegistry.getServerRegistry(_mbean).getQueueNames(_virtualHostName);
+ // Customization for AMQQueueMBean method OPERATION_MOVE_MESSAGES
+ if (_opData.getName().equals(OPERATION_MOVE_MESSAGES))
+ {
+ qList.remove(_mbean.getName());
+ }
+ // End of Customization
+ items = qList.toArray(new String[0]);
+ }
+ else if (param.getName().equals(EXCHANGE))
+ {
+ items = ApplicationRegistry.getServerRegistry(_mbean).getExchangeNames(_virtualHostName);
+ }
+ else if (param.getName().equals(EXCHANGE_TYPE))
+ {
+ items = EXCHANGE_TYPE_VALUES;
+ }
+ else if (isUserListParameter(param))
+ {
+ List<String> list = ApplicationRegistry.getServerRegistry(_mbean).getUsernames();
+ if (list != null && !list.isEmpty())
+ {
+ items = list.toArray(new String[0]);
+ }
+ }
+
+ if (items != null)
+ {
+ org.eclipse.swt.widgets.List _list = new org.eclipse.swt.widgets.List(_paramsComposite, SWT.BORDER | SWT.V_SCROLL);
+ int listSize = _form.getClientArea().height * 2 / 3;
+ int itemsHeight = items.length * (_list.getItemHeight() + 2);
+ // Set a min height for the list widget (set it to min 4 items)
+ if (items.length < 4)
+ {
+ itemsHeight = 4 * (_list.getItemHeight() + 2);
+ }
+
+ listSize = (listSize > itemsHeight) ? itemsHeight : listSize;
+ parameterPositionOffset = parameterPositionOffset + listSize;
+ formData.bottom = new FormAttachment(0, parameterPositionOffset);
+ _list.setLayoutData(formData);
+ _list.setData(param);
+ _list.setItems(items);
+ _list.addSelectionListener(parameterSelectionListener);
+ valueInCombo = true;
+ }
+ else if (param.isBoolean())
+ {
+ Button booleanButton = _toolkit.createButton(_paramsComposite, "", SWT.CHECK);
+ booleanButton.setLayoutData(formData);
+ booleanButton.setData(param);
+ booleanButton.addSelectionListener(booleanSelectionListener);
+ valueInCombo = true;
+ }
+ else
+ {
+ int style = SWT.NONE;
+ if (PASSWORD.equalsIgnoreCase(param.getName()))
+ {
+ style = SWT.PASSWORD;
+ }
+ Text text = _toolkit.createText(_paramsComposite, "", style);
+ formData = new FormData();
+ formData.top = new FormAttachment(0, parameterPositionOffset);
+ formData.left = new FormAttachment(label, 5);
+ formData.right = new FormAttachment(valueWidth);
+ text.setLayoutData(formData);
+ // Listener to assign value to the parameter
+ text.addKeyListener(keyListener);
+ // Listener to verify if the entered key is valid
+ text.addVerifyListener(verifyListener);
+ text.setData(param);
+ }
+
+ // display the parameter data type next to the text field
+ if (valueInCombo)
+ {
+ label = _toolkit.createLabel(_paramsComposite, "");
+ }
+ else if (PASSWORD.equalsIgnoreCase(param.getName()))
+ {
+ label = _toolkit.createLabel(_paramsComposite, "(String)");
+ }
+ else
+ {
+ String str = param.getType();
+
+ if (param.getType().lastIndexOf(".") != -1)
+ str = param.getType().substring(1 + param.getType().lastIndexOf("."));
+
+ label = _toolkit.createLabel(_paramsComposite, "(" + str + ")");
+ }
+ formData = new FormData();
+ formData.top = new FormAttachment(0, parameterPositionOffset);
+ formData.left = new FormAttachment(valueWidth, 5);
+ label.setLayoutData(formData);
+ }
+ }
+
+ private boolean isUserListParameter(ParameterData param)
+ {
+ if (_mbean.isAdmin() && param.getName().equals(OPERATION_PARAM_USERNAME)
+ && !_opData.getName().equals(OPERATION_CREATEUSER))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Creates customized dispaly for a method "CreateNewBinding" for Headers exchange
+ *
+ */
+ private void customCreateNewBinding()
+ {
+ headerBindingHashMap = new HashMap<Text, Text>();
+
+ _paramsComposite.setLayout(new GridLayout());
+ _paramsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
+ final ScrolledComposite scrolledComposite = new ScrolledComposite(_paramsComposite, SWT.BORDER | SWT.V_SCROLL);
+ scrolledComposite.setExpandHorizontal(true);
+ scrolledComposite.setExpandVertical(true);
+ GridData layoutData = new GridData(SWT.FILL, SWT.TOP, true, true);
+ scrolledComposite.setLayoutData(layoutData);
+ scrolledComposite.setLayout(new GridLayout());
+
+ final Composite composite = _toolkit.createComposite(scrolledComposite, SWT.NONE);
+ scrolledComposite.setContent(composite);
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ layoutData.verticalIndent = 20;
+ composite.setLayoutData(layoutData);
+ composite.setLayout(new FormLayout());
+
+ List<ParameterData> params = _opData.getParameters();
+ ParameterData param = params.get(0);
+ // Queue selection widget
+ Label label = _toolkit.createLabel(composite, ViewUtility.getDisplayText(param.getName()));
+ FormData formData = new FormData();
+ formData.top = new FormAttachment(0, 2);
+ formData.right = new FormAttachment(labelWidth);
+ label.setLayoutData(formData);
+ label.setToolTipText(param.getDescription());
+
+ formData = new FormData();
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(label, 5);
+ formData.right = new FormAttachment(valueWidth);
+
+ Combo combo = new Combo(composite, SWT.READ_ONLY | SWT.DROP_DOWN);
+ List<String> qList = ApplicationRegistry.getServerRegistry(_mbean).getQueueNames(_virtualHostName);
+ combo.setItems(qList.toArray(new String[0]));
+ combo.add("Select Queue", 0);
+ combo.select(0);
+ combo.setLayoutData(formData);
+ combo.setData(param);
+ combo.addSelectionListener(parameterSelectionListener);
+
+ // Binding creation widgets
+ createARowForCreatingHeadersBinding(composite, 1);
+ createARowForCreatingHeadersBinding(composite, 2);
+ createARowForCreatingHeadersBinding(composite, 3);
+ createARowForCreatingHeadersBinding(composite, 4);
+ createARowForCreatingHeadersBinding(composite, 5);
+ createARowForCreatingHeadersBinding(composite, 6);
+ createARowForCreatingHeadersBinding(composite, 7);
+ createARowForCreatingHeadersBinding(composite, 8);
+
+ final Button addMoreButton = _toolkit.createButton(composite, "Add More", SWT.PUSH);
+ formData = new FormData();
+ formData.top = new FormAttachment(0, heightForAParameter);
+ formData.left = new FormAttachment(70, 5);
+ addMoreButton.setLayoutData(formData);
+ addMoreButton.setData("rowCount", 8);
+ addMoreButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ int count = Integer.parseInt(addMoreButton.getData("rowCount").toString());
+ createARowForCreatingHeadersBinding(composite, ++count);
+ addMoreButton.setData("rowCount", count);
+ scrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ composite.layout();
+ _form.layout();
+ }
+ });
+
+ scrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ composite.layout();
+ }
+
+ /**
+ * Adds a row for adding a binding for Headers Exchange. Used by the method, which creates the customized
+ * layout and widgest for Header's exchange method createNewBinding.
+ * @param parent composite
+ * @param rowCount - row number
+ */
+ private void createARowForCreatingHeadersBinding(Composite parent, int rowCount)
+ {
+ Label key = _toolkit.createLabel(parent, "Name");
+ FormData formData = new FormData();
+ formData.top = new FormAttachment(0, rowCount * heightForAParameter + 2);
+ formData.right = new FormAttachment(15);
+ key.setLayoutData(formData);
+
+ Text keyText = _toolkit.createText(parent, "", SWT.NONE);
+ formData = new FormData();
+ formData.top = new FormAttachment(0, rowCount * heightForAParameter);
+ formData.left = new FormAttachment(key, 5);
+ formData.right = new FormAttachment(40);
+ keyText.setLayoutData(formData);
+ keyText.addKeyListener(headerBindingListener);
+
+ Label value = _toolkit.createLabel(parent, "Value");
+ formData = new FormData();
+ formData.top = new FormAttachment(0, rowCount * heightForAParameter + 2);
+ formData.right = new FormAttachment(45);
+ value.setLayoutData(formData);
+
+ Text valueText = _toolkit.createText(parent, "", SWT.NONE);
+ formData = new FormData();
+ formData.top = new FormAttachment(0, rowCount * heightForAParameter);
+ formData.left = new FormAttachment(value, 5);
+ formData.right = new FormAttachment(70);
+ valueText.setLayoutData(formData);
+ valueText.addKeyListener(headerBindingListener);
+
+ // Add these to the map, to retrieve the values while setting the parameter value
+ headerBindingHashMap.put(keyText, valueText);
+ }
+
+ /**
+ * Sets text and listener for the operation execution button
+ * @param text
+ */
+ private void setButton(String text)
+ {
+ _executionButton.setText(text);
+ _executionButton.removeSelectionListener(refreshListener);
+ _executionButton.removeSelectionListener(operationExecutionListener);
+
+ if (BUTTON_EXECUTE.equals(text))
+ {
+ _executionButton.addSelectionListener(operationExecutionListener);
+ }
+ else
+ {
+ _executionButton.addSelectionListener(refreshListener);
+ }
+ }
+
+ /**
+ * displays the operation result in a pop-up window
+ * @param result
+ */
+ private void populateResults(Object result)
+ {
+ Display display = Display.getCurrent();
+ int width = 600;
+ int height = 400;
+ Shell shell = ViewUtility.createPopupShell(RESULT, width, height);
+ shell.setImage(ApplicationRegistry.getImage(CONSOLE_IMAGE));
+ ViewUtility.populateCompositeWithData(_toolkit, shell, result);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ shell.dispose();
+ }
+
+ /**
+ * Clears the parameter values of the operation
+ */
+ private void clearParameters()
+ {
+ List<ParameterData> params = _opData.getParameters();
+ if (params != null && !params.isEmpty())
+ {
+ for (ParameterData param : params)
+ {
+ param.setDefaultValue();
+ }
+ }
+ }
+
+ /**
+ * Clears the values entered by the user from parameter value widgets
+ * @param control
+ */
+ private void clearParameterValues(Composite control)
+ {
+ if (control == null || (control.isDisposed()))
+ return;
+
+ Control[] controls = control.getChildren();
+ if (controls == null || controls.length == 0)
+ return;
+
+ for (int i = 0; i < controls.length; i++)
+ {
+ if (controls[i] instanceof Combo)
+ ((Combo)controls[i]).select(0);
+ if (controls[i] instanceof org.eclipse.swt.widgets.List)
+ ((org.eclipse.swt.widgets.List)controls[i]).deselectAll();
+ else if (controls[i] instanceof Text)
+ ((Text)controls[i]).setText("");
+ else if (controls[i] instanceof Button)
+ ((Button)controls[i]).setSelection(false);
+ else if (controls[i] instanceof Composite)
+ clearParameterValues((Composite)controls[i]);
+ }
+ }
+
+ /**
+ * Listener class for operation execution events
+ */
+ private class OperationExecutionListener extends SelectionAdapter
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ List<ParameterData> params = _opData.getParameters();
+ if (params != null && !params.isEmpty())
+ {
+ for (ParameterData param : params)
+ {
+ if (param.getValue() == null || param.getValue().toString().length() == 0)
+ {
+ // Customized check, because for this parameter null is allowed
+ if (param.getName().equals(ATTRIBUTE_QUEUE_OWNER) &&
+ _opData.getName().equals(OPERATION_CREATE_QUEUE))
+ {
+ continue;
+ }
+ // End of custom code
+
+ ViewUtility.popupInfoMessage(_form.getText(), "Please select the " + ViewUtility.getDisplayText(param.getName()));
+ return;
+ }
+
+ // customized for passwords
+ String securityMechanism = ApplicationRegistry.getSecurityMechanism();
+ if ((MECH_CRAMMD5.equals(securityMechanism)) && PASSWORD.equalsIgnoreCase(param.getName()))
+ {
+ try
+ {
+ param.setValue(ViewUtility.getMD5HashedCharArray(param.getValue()));
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(_mbean, ex);
+ return;
+ }
+ }
+ // end of customization
+ }
+ }
+
+ if (_opData.getImpact() == OPERATION_IMPACT_ACTION)
+ {
+ String bean = _mbean.getName() == null ? _mbean.getType() : _mbean.getName();
+ int response = ViewUtility.popupConfirmationMessage(bean, "Do you want to " + _form.getText()+ " ?");
+ if (response == SWT.YES)
+ {
+ executeAndShowResults();
+ }
+ }
+ else
+ {
+ executeAndShowResults();
+ }
+
+ if (_mbean.isAdmin() && _opData.getName().equals(OPERATION_DELETEUSER))
+ {
+ refresh(_mbean);
+ }
+ else
+ {
+ clearParameters();
+ clearParameterValues(_paramsComposite);
+ }
+ }
+ }
+
+ // Listener for the "Refresh" execution button
+ private class RefreshListener extends SelectionAdapter
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ executeAndShowResults();
+ }
+ }
+
+ /**
+ * Executres the operation, gets the result from server and displays to the user
+ */
+ private void executeAndShowResults()
+ {
+ Object result = null;
+ try
+ {
+ result = MBeanUtility.execute(_mbean, _opData);
+ }
+ catch(Exception ex)
+ {
+ MBeanUtility.handleException(_mbean, ex);
+ return;
+ }
+
+ // Custom code for Admin mbean operation
+ /* These custome codes here are to make the GUI look more user friendly.
+ * Here we are adding the users to a list, which will be used to list username to be selected on
+ * pages like "delete user", "set password" instead of typing the username
+ */
+ if (_mbean.isAdmin())
+ {
+ if (_opData.getName().equals(OPERATION_VIEWUSERS))
+ {
+ ApplicationRegistry.getServerRegistry(_mbean).setUserList(extractUserList(result));
+ }
+ else if (_opData.getName().equals(OPERATION_DELETEUSER))
+ {
+ List<String> list = ApplicationRegistry.getServerRegistry(_mbean).getUsernames();
+ Object userName = _opData.getParameterValue(OPERATION_PARAM_USERNAME);
+ if ((list != null) && !list.isEmpty() && (userName != null))
+ {
+ list.remove(userName);
+ ApplicationRegistry.getServerRegistry(_mbean).setUserList(list);
+ }
+ }
+ else if (_opData.getName().equals(OPERATION_CREATEUSER))
+ {
+ List<String> list = ApplicationRegistry.getServerRegistry(_mbean).getUsernames();
+ Object userName = _opData.getParameterValue(OPERATION_PARAM_USERNAME);
+ if ((list != null) && !list.isEmpty() && (userName != null))
+ {
+ list.add(userName.toString());
+ ApplicationRegistry.getServerRegistry(_mbean).setUserList(list);
+ }
+ }
+ }
+ // end of custom code
+
+ // Some mbeans have only "type" and no "name".
+ String title = _mbean.getType();
+ if (_mbean.getName() != null && _mbean.getName().length() != 0)
+ {
+ title = _mbean.getName();
+ }
+
+ if (_opData.isReturnTypeVoid())
+ {
+ ViewUtility.popupInfoMessage(title, OPERATION_SUCCESSFUL);
+ }
+ else if (_opData.isReturnTypeBoolean())
+ {
+ boolean success = Boolean.parseBoolean(result.toString());
+ String message = success ? OPERATION_SUCCESSFUL : OPERATION_UNSUCCESSFUL;
+ ViewUtility.popupInfoMessage(title, message);
+ }
+ else if (_opData.getParameters() != null && !_opData.getParameters().isEmpty())
+ {
+ populateResults(result);
+ }
+ else
+ {
+ ViewUtility.disposeChildren(_resultsComposite);
+ ViewUtility.populateCompositeWithData(_toolkit, _resultsComposite, result);
+ _resultsComposite.layout();
+ _form.layout();
+ }
+
+ }
+
+ private List<String> extractUserList(Object result)
+ {
+ if (!(result instanceof TabularDataSupport))
+ {
+ return null;
+ }
+
+ TabularDataSupport tabularData = (TabularDataSupport)result;
+ Collection<Object> records = tabularData.values();
+ List<String> list = new ArrayList<String>();
+ for (Object o : records)
+ {
+ CompositeData data = (CompositeData) o;
+ if (data.containsKey(USERNAME))
+ {
+ list.add(data.get(USERNAME).toString());
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Listener class for the operation parameters widget
+ */
+ private class ParameterSelectionListener extends SelectionAdapter
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ ParameterData parameter = (ParameterData)e.widget.getData();
+ parameter.setValue(null);
+ if (e.widget instanceof Combo)
+ {
+ Combo combo = (Combo)e.widget;
+ if (combo.getSelectionIndex() > 0)
+ {
+ String item = combo.getItem(combo.getSelectionIndex());
+ parameter.setValueFromString(item);
+ }
+ }
+ else if (e.widget instanceof org.eclipse.swt.widgets.List)
+ {
+ org.eclipse.swt.widgets.List list = (org.eclipse.swt.widgets.List)e.widget;
+ String[] selectedItems = list.getSelection();
+ if (selectedItems.length > 0)
+ {
+ parameter.setValueFromString(selectedItems[0]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Listener class for boolean parameter widgets
+ */
+ private class BooleanSelectionListener extends SelectionAdapter
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ ParameterData parameter = (ParameterData)(e.widget.getData());
+ if (e.widget instanceof Button)
+ {
+ Button button = (Button)e.widget;
+ parameter.setValue(button.getSelection());
+ }
+ else if (e.widget instanceof Combo)
+ {
+ Combo combo = (Combo)e.widget;
+ String item = combo.getItem(combo.getSelectionIndex());
+ parameter.setValueFromString(item);
+ }
+ }
+ }
+
+ /**
+ * Listener class for the operation parameter value widget (Text field)
+ */
+ private class KeyListenerImpl extends KeyAdapter
+ {
+ public void keyReleased(KeyEvent e)
+ {
+ if (!(e.widget instanceof Text))
+ return;
+
+ Text text = (Text)e.widget;
+ // Get the parameters widget and assign the text to the parameter
+ String strValue = text.getText();
+ ParameterData parameter = (ParameterData)text.getData();
+ try
+ {
+ parameter.setValueFromString(strValue);
+ }
+ catch(Exception ex)
+ {
+ // Exception occured in setting parameter value.
+ // ignore it. The value will not be assigned to the parameter
+ }
+ }
+ }
+
+ /**
+ * Listener class for HeaderExchange's new binding widgets. Used when the new bindings are
+ * being created for Header's Exchange
+ */
+ private class HeaderBindingKeyListener extends KeyAdapter
+ {
+ public void keyReleased(KeyEvent e)
+ {
+ ParameterData param = _opData.getParameters().get(1);
+ StringBuffer paramValue = new StringBuffer();
+ for (Entry<Text, Text> entry : headerBindingHashMap.entrySet())
+ {
+
+ Text nameText = entry.getKey();
+ String name = nameText.getText();
+ Text valueText = entry.getValue();
+ String value = valueText.getText();
+ if ((name != null) && (name.length() != 0) && (value != null) && (value.length() != 0))
+ {
+ if (paramValue.length() != 0)
+ {
+ paramValue.append(",");
+ }
+ paramValue.append(name + "=" + value);
+ }
+ }
+
+ param.setValue(paramValue.toString());
+ }
+ }
+
+ /**
+ * Listener class for verifying the user input with parameter type
+ */
+ private class VerifyListenerImpl implements VerifyListener
+ {
+ public void verifyText(VerifyEvent event)
+ {
+ ParameterData parameter = (ParameterData)event.widget.getData();
+ String text = event.text;
+ char [] chars = new char [text.length ()];
+ text.getChars(0, chars.length, chars, 0);
+ String type = parameter.getType();
+ if (type.equals("int") || type.equals("java.lang.Integer") ||
+ type.equals("long") || type.equals("java.lang.Long"))
+ {
+ for (int i=0; i<chars.length; i++)
+ {
+ if (!('0' <= chars [i] && chars [i] <= '9'))
+ {
+ event.doit = false;
+ return;
+ }
+ }
+
+ }
+ }
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/QueueTypeTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/QueueTypeTabControl.java
new file mode 100644
index 0000000000..31a0bc857b
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/QueueTypeTabControl.java
@@ -0,0 +1,296 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.management.ui.views;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.Constants;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.apache.qpid.management.ui.jmx.MBeanUtility;
+import org.apache.qpid.management.ui.model.AttributeData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.TabFolder;
+
+/**
+ * Controller class, which takes care of displaying appropriate information and widgets for Queues.
+ * This allows user to select Queues and add those to the navigation view
+ */
+public class QueueTypeTabControl extends MBeanTypeTabControl
+{
+ private boolean _showTempQueues = false;
+ private Button _sortBySizeButton = null;
+ private Button _sortByConsumercountButton = null;
+ private Button _sortByNameButton = null;
+ private Button _showTempQueuesButton = null;
+
+ private ComparatorImpl _sorterByAttribute = new ComparatorImpl();
+
+ // Map required for sorting queues based on attribute values
+ private Map<AttributeData, ManagedBean> _queueDepthMap = new LinkedHashMap<AttributeData, ManagedBean>();
+ // Map used for sorting Queues based on consumer count
+ private Map<AttributeData, ManagedBean> _queueConsumerCountMap = new LinkedHashMap<AttributeData, ManagedBean>();
+
+
+ public QueueTypeTabControl(TabFolder tabFolder)
+ {
+ super(tabFolder, Constants.QUEUE);
+ createWidgets();
+ }
+
+ protected void createWidgets()
+ {
+ createHeaderComposite(getFormComposite());
+ createButtonsComposite(getFormComposite());
+ createListComposite();
+ }
+
+ @Override
+ public void refresh() throws Exception
+ {
+ setLabelValues();
+ selectDefaultSortingButton();
+ populateList();
+ layout();
+ }
+
+ /**
+ * populates the map with mbean name and the mbean object.
+ * @throws Exception
+ */
+ protected void populateList() throws Exception
+ {
+ // map should be cleared before populating it with new values
+ getMBeansMap().clear();
+ _queueDepthMap.clear();
+ _queueConsumerCountMap.clear();
+
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(MBeanView.getServer());
+ String[] items = null;
+ java.util.List<ManagedBean> list = null;
+
+ // populate the map and list with appropriate mbeans
+ list = serverRegistry.getQueues(MBeanView.getVirtualHost());
+ items = getQueueItems(list);
+ // sort the refreshed list in the selected order
+ if (_sortBySizeButton.getSelection())
+ {
+ sortQueuesByQueueDepth();
+ }
+ else if (_sortByConsumercountButton.getSelection())
+ {
+ sortQueuesByConsumerCount();
+ }
+ else
+ {
+ getListWidget().setItems(items);
+ }
+ }
+
+ private void selectDefaultSortingButton()
+ {
+ _sortByNameButton.setSelection(true);
+ _sortBySizeButton.setSelection(false);
+ _sortByConsumercountButton.setSelection(false);
+
+ _showTempQueues = false;
+ _showTempQueuesButton.setSelection(_showTempQueues);
+ }
+
+ protected void createListComposite()
+ {
+ // Composite to contain the item list
+ Composite composite = getToolkit().createComposite(getFormComposite());
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ composite.setLayoutData(gridData);
+ GridLayout layout = new GridLayout(2, true);
+ layout.verticalSpacing = 0;
+ composite.setLayout(layout);
+
+ createListComposite(composite);
+
+ // Composite to contain buttons like - Sort by size
+ Composite _sortingComposite = getToolkit().createComposite(composite);
+ gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ _sortingComposite.setLayoutData(gridData);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.verticalSpacing = 20;
+ _sortingComposite.setLayout(gridLayout);
+
+ Group sortingGroup = new Group(_sortingComposite, SWT.SHADOW_NONE);
+ sortingGroup.setBackground(_sortingComposite.getBackground());
+ sortingGroup.setText(" Sort List By ");
+ sortingGroup.setFont(ApplicationRegistry.getFont(Constants.FONT_BOLD));
+ gridData = new GridData(SWT.CENTER, SWT.TOP, true, false);
+ sortingGroup.setLayoutData(gridData);
+ sortingGroup.setLayout(new GridLayout());
+
+ _sortByNameButton = getToolkit().createButton(sortingGroup, Constants.QUEUE_SORT_BY_NAME, SWT.RADIO);
+ gridData = new GridData(SWT.LEAD, SWT.CENTER, true, false);
+ _sortByNameButton.setLayoutData(gridData);
+ _sortByNameButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent e)
+ {
+ try
+ {
+ // sort the stored list of items
+ java.util.List<String> list = new ArrayList<String>(getMBeansMap().keySet());
+ Collections.sort(list);
+ getListWidget().setItems(list.toArray(new String[0]));
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ });
+
+ _sortBySizeButton = getToolkit().createButton(sortingGroup, Constants.QUEUE_SORT_BY_DEPTH, SWT.RADIO);
+ gridData = new GridData(SWT.LEAD, SWT.CENTER, true, false);
+ _sortBySizeButton.setLayoutData(gridData);
+ _sortBySizeButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent e)
+ {
+ try
+ {
+ // sort the stored list of items
+ sortQueuesByQueueDepth();
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ });
+
+ _sortByConsumercountButton = getToolkit().createButton(sortingGroup, Constants.QUEUE_SORT_BY_CONSUMERCOUNT, SWT.RADIO);
+ gridData = new GridData(SWT.LEAD, SWT.CENTER, true, false);
+ _sortByConsumercountButton.setLayoutData(gridData);
+ _sortByConsumercountButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent e)
+ {
+ try
+ {
+ sortQueuesByConsumerCount();
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ });
+
+ _showTempQueuesButton = getToolkit().createButton(_sortingComposite, Constants.QUEUE_SHOW_TEMP_QUEUES, SWT.CHECK);
+ _showTempQueuesButton.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+ _showTempQueuesButton.addSelectionListener(new SelectionAdapter(){
+ public void widgetSelected(SelectionEvent e)
+ {
+ Button button = (Button)e.widget;
+ _showTempQueues = button.getSelection();
+ try
+ {
+ populateList();
+ }
+ catch (Exception ex)
+ {
+ MBeanUtility.handleException(ex);
+ }
+ }
+ });
+ }
+
+
+ private String[] getQueueItems(java.util.List<ManagedBean> list) throws Exception
+ {
+ if (list == null)
+ return new String[0];
+
+ // Sort the list. It will keep the mbeans in sorted order in the _queueMap, which is required for
+ // sorting the queue according to size etc
+ Collections.sort(list, getMBeanNameSorter());
+ java.util.List<String> items = new ArrayList<String>();;
+ int i = 0;
+ for (ManagedBean mbean : list)
+ {
+ if ((!_showTempQueues && mbean.isTempQueue()))
+ {
+ continue;
+ }
+ AttributeData data = MBeanUtility.getAttributeData(mbean, Constants.ATTRIBUTE_QUEUE_DEPTH);
+ String value = mbean.getName() + " (" + data.getValue().toString() + " KB)";
+ items.add(value);
+ //items[i] = mbean.getName() + " (" + value + " KB)";
+ getMBeansMap().put(value, mbean);
+ _queueDepthMap.put(data, mbean);
+ data = MBeanUtility.getAttributeData(mbean, Constants.ATTRIBUTE_QUEUE_CONSUMERCOUNT);
+ _queueConsumerCountMap.put(data, mbean);
+ i++;
+ }
+
+ return items.toArray(new String[0]);
+ }
+
+
+ private void sortQueuesByQueueDepth()
+ {
+ // Queues are already in the alphabetically sorted order in _queueMap, now sort for queueDepth
+ java.util.List<AttributeData> list = new ArrayList<AttributeData>(_queueDepthMap.keySet());
+ Collections.sort(list, _sorterByAttribute);
+
+ String[] items = new String[list.size()];
+ int i = 0;
+ for (AttributeData data : list)
+ {
+ ManagedBean mbean = _queueDepthMap.get(data);
+ String value = data.getValue().toString();
+ items[i++] = mbean.getName() + " (" + value + " KB)";
+ }
+ getListWidget().setItems(items);
+ }
+
+ private void sortQueuesByConsumerCount()
+ {
+ java.util.List<AttributeData> list = new ArrayList<AttributeData>(_queueConsumerCountMap.keySet());
+ Collections.sort(list, _sorterByAttribute);
+
+ String[] items = new String[list.size()];
+ int i = 0;
+ for (AttributeData data : list)
+ {
+ ManagedBean mbean = _queueConsumerCountMap.get(data);
+ String value = data.getValue().toString();
+ items[i++] = mbean.getName() + " (" + value + " )";
+ }
+ getListWidget().setItems(items);
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TabControl.java
new file mode 100644
index 0000000000..c13c92066c
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TabControl.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import java.util.ArrayList;
+
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.model.OperationData;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.TabFolder;
+
+/**
+ * Abstract class for all the control classes of tabs.
+ * @author Bhupendra Bhardwaj
+ */
+public abstract class TabControl
+{
+ protected ManagedBean _mbean = null;
+ protected TabFolder _tabFolder = null;
+
+ private static java.util.List<String> simpleTypes = new ArrayList<String>();
+
+ static
+ {
+ simpleTypes.add("java.math.BigDecimal");
+ simpleTypes.add("java.math.BigInteger");
+ simpleTypes.add("java.lang.Boolean");
+ simpleTypes.add("java.lang.Byte");
+ simpleTypes.add("java.lang.Character");
+ simpleTypes.add("java.util.Date");
+ simpleTypes.add("java.lang.Double");
+ simpleTypes.add("java.lang.Float");
+ simpleTypes.add("java.lang.Integer");
+ simpleTypes.add("java.lang.Long");
+ simpleTypes.add("javax.management.ObjectName");
+ simpleTypes.add("java.lang.Short");
+ simpleTypes.add("java.lang.String");
+ simpleTypes.add("boolean");
+ }
+
+ public TabControl(TabFolder tabFolder)
+ {
+ _tabFolder = tabFolder;
+ }
+
+ /**
+ * @return controller composite for the tab
+ */
+ public Control getControl()
+ {
+ return null;
+ }
+
+ public void refresh(ManagedBean mbean)
+ {
+ if (mbean == null)
+ {
+ refresh();
+ }
+ }
+
+ public void refresh()
+ {
+
+ }
+
+ public void refresh(ManagedBean mbean, OperationData opData)
+ {
+
+ }
+
+ /**
+ * Sets focus on a widget
+ */
+ public void setFocus()
+ {
+
+ }
+
+ public boolean isSimpleType(Object data)
+ {
+ return simpleTypes.contains(data.getClass().getName());
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TreeObject.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TreeObject.java
new file mode 100644
index 0000000000..9545ed9876
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TreeObject.java
@@ -0,0 +1,126 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.qpid.management.ui.Constants;
+import org.apache.qpid.management.ui.ManagedBean;
+import org.apache.qpid.management.ui.ManagedObject;
+
+public class TreeObject
+{
+ private String _name;
+ private String _type;
+ private String _virtualHost;
+ private TreeObject _parent;
+ private List<TreeObject> _children = new ArrayList<TreeObject>();
+ private ManagedObject _object;
+
+ public TreeObject(String name, String type)
+ {
+ this._name = name;
+ this._type = type;
+ }
+
+ public TreeObject(ManagedObject obj)
+ {
+ _name = obj.getName();
+ if (_name == null && (obj instanceof ManagedBean))
+ {
+ String[] types = ((ManagedBean)obj).getType().split("\\.");
+ _name = types[types.length - 1];
+ }
+ this._type = Constants.MBEAN;
+ this._object = obj;
+ }
+
+ public void addChild(TreeObject child)
+ {
+ _children.add(child);
+ }
+
+ public void addChildren(List<TreeObject> subList)
+ {
+ _children.addAll(subList);
+ }
+
+ public List<TreeObject> getChildren()
+ {
+ return _children;
+ }
+
+ public void setChildren(List<TreeObject> children)
+ {
+ this._children = children;
+ }
+
+ public void setName(String value)
+ {
+ _name = value;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+ public String getType()
+ {
+ return _type;
+ }
+
+ public String getVirtualHost()
+ {
+ // To make it work with the broker with no virtual host implementation
+ return _virtualHost == null ? Constants.DEFAULT_VH : _virtualHost;
+ }
+
+ public void setVirtualHost(String vHost)
+ {
+ _virtualHost = vHost;
+ }
+
+ public ManagedObject getManagedObject()
+ {
+ return _object;
+ }
+
+ public void setManagedObject(ManagedObject obj)
+ {
+ this._object = obj;
+ }
+
+ public TreeObject getParent()
+ {
+ return _parent;
+ }
+
+ public void setParent(TreeObject parent)
+ {
+ this._parent = parent;
+
+ if (parent != null)
+ {
+ parent.addChild(this);
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/VHNotificationsTabControl.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/VHNotificationsTabControl.java
new file mode 100644
index 0000000000..be25707bd3
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/VHNotificationsTabControl.java
@@ -0,0 +1,483 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.management.ui.views;
+
+import static org.apache.qpid.management.ui.Constants.BUTTON_CLEAR;
+import static org.apache.qpid.management.ui.Constants.BUTTON_REFRESH;
+import static org.apache.qpid.management.ui.Constants.CONSOLE_IMAGE;
+import static org.apache.qpid.management.ui.Constants.FONT_BUTTON;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.qpid.management.ui.ApplicationRegistry;
+import org.apache.qpid.management.ui.ServerRegistry;
+import org.apache.qpid.management.ui.model.NotificationObject;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class VHNotificationsTabControl extends TabControl
+{
+ protected FormToolkit _toolkit;
+ protected Form _form;
+ protected Table _table = null;
+ protected TableViewer _tableViewer = null;
+
+ protected Thread worker = null;
+
+ protected List<NotificationObject> _notifications = null;
+
+ private static final String COLUMN_OBJ = "Object Name";
+ private static final String COLUMN_SEQ = "Sequence No";
+ private static final String COLUMN_TIME = "TimeStamp";
+ private static final String COLUMN_TYPE = "Type";
+ private static final String COLUMN_MSG = "Notification Message";
+ protected static final String[] _tableTitles = new String [] {
+ COLUMN_OBJ,
+ COLUMN_SEQ,
+ COLUMN_TIME,
+ COLUMN_TYPE,
+ COLUMN_MSG
+ };
+
+ protected Button _clearButton = null;
+ protected Button _refreshButton = null;
+
+ public VHNotificationsTabControl(TabFolder tabFolder)
+ {
+ super(tabFolder);
+ _toolkit = new FormToolkit(_tabFolder.getDisplay());
+ _form = _toolkit.createForm(_tabFolder);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ _form.getBody().setLayout(gridLayout);
+
+ worker = new Thread(new Worker());
+ worker.start();
+ }
+
+ protected void createWidgets()
+ {
+ addButtons();
+ createTableViewer();
+ }
+
+ /**
+ * @see TabControl#getControl()
+ */
+ public Control getControl()
+ {
+ if (_table == null)
+ {
+ createWidgets();
+ }
+ return _form;
+ }
+
+ /**
+ * Creates clear buttin and refresh button
+ */
+ protected void addButtons()
+ {
+ Composite composite = _toolkit.createComposite(_form.getBody(), SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ composite.setLayout(new GridLayout(2, true));
+
+ // Add Clear Button
+ _clearButton = _toolkit.createButton(composite, BUTTON_CLEAR, SWT.PUSH | SWT.CENTER);
+ _clearButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ GridData gridData = new GridData(SWT.LEAD, SWT.TOP, true, false);
+ gridData.widthHint = 80;
+ _clearButton.setLayoutData(gridData);
+ _clearButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ //TODO : Get selected rows and clear those
+ IStructuredSelection ss = (IStructuredSelection)_tableViewer.getSelection();
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(MBeanView.getServer());
+ serverRegistry.clearNotifications(null, ss.toList());
+ refresh();
+ }
+ });
+
+ // Add Refresh Button
+ _refreshButton = _toolkit.createButton(composite, BUTTON_REFRESH, SWT.PUSH | SWT.CENTER);
+ _refreshButton.setFont(ApplicationRegistry.getFont(FONT_BUTTON));
+ gridData = new GridData(SWT.TRAIL, SWT.TOP, true, false);
+ gridData.widthHint = 80;
+ _refreshButton.setLayoutData(gridData);
+ _refreshButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ refresh();
+ }
+ });
+ }
+
+ /**
+ * Creates table to display notifications
+ */
+ private void createTable()
+ {
+ _table = _toolkit.createTable(_form.getBody(), SWT.MULTI | SWT.FULL_SELECTION);
+ _table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ TableColumn column = new TableColumn(_table, SWT.NONE);
+ column.setText(_tableTitles[0]);
+ column.setWidth(100);
+
+ column = new TableColumn(_table, SWT.NONE);
+ column.setText(_tableTitles[1]);
+ column.setWidth(100);
+
+ column = new TableColumn(_table, SWT.NONE);
+ column.setText(_tableTitles[2]);
+ column.setWidth(130);
+
+ column = new TableColumn(_table, SWT.NONE);
+ column.setText(_tableTitles[3]);
+ column.setWidth(100);
+
+ column = new TableColumn(_table, SWT.NONE);
+ column.setText(_tableTitles[4]);
+ column.setWidth(500);
+
+ _table.setHeaderVisible(true);
+ _table.setLinesVisible(true);
+ }
+
+ /**
+ * Creates JFace viewer for the notifications table
+ */
+ protected void createTableViewer()
+ {
+ createTable();
+ _tableViewer = new TableViewer(_table);
+ //_tableViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ _tableViewer.setUseHashlookup(true);
+ _tableViewer.setContentProvider(new ContentProviderImpl());
+ _tableViewer.setLabelProvider(new LabelProviderImpl());
+ _tableViewer.setColumnProperties(_tableTitles);
+ /*
+ CellEditor[] cellEditors = new CellEditor[_tableTitles.length];
+ TextCellEditor textEditor = new TextCellEditor(table);
+ cellEditors[0] = textEditor;
+ textEditor = new TextCellEditor(table);
+ cellEditors[1] = textEditor;
+ textEditor = new TextCellEditor(table);
+ cellEditors[2] = textEditor;
+ textEditor = new TextCellEditor(table);
+ cellEditors[3] = textEditor;
+
+ // Assign the cell editors to the viewer
+ _tableViewer.setCellEditors(cellEditors);
+ _tableViewer.setCellModifier(new TableCellModifier());
+ */
+
+ addTableListeners();
+
+ //_tableViewer.addSelectionChangedListener(new );
+
+ //_notificationDetails = new Composite(_tabControl, SWT.BORDER);
+ //_notificationDetails.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ //_tabControl.layout();
+ //viewerComposite.layout();
+ }
+
+ /**
+ * Adds listeners to the viewer for displaying notification details
+ */
+ protected void addTableListeners()
+ {
+ _tableViewer.addDoubleClickListener(new IDoubleClickListener()
+ {
+ Display display = null;
+ Shell shell = null;
+ public void doubleClick(DoubleClickEvent event)
+ {
+ display = Display.getCurrent();
+ shell = new Shell(display, SWT.BORDER | SWT.CLOSE | SWT.MIN | SWT.MAX | SWT.RESIZE);
+ shell.setText("Notification");
+ shell.setImage(ApplicationRegistry.getImage(CONSOLE_IMAGE));
+
+ int x = display.getBounds().width;
+ int y = display.getBounds().height;
+ shell.setBounds(x/4, y/4, x/2, y/3);
+ StructuredSelection selection = (StructuredSelection)event.getSelection();
+ createPopupContents((NotificationObject)selection.getFirstElement());
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+ //If you create it, you dispose it.
+ shell.dispose();
+ }
+
+ private void createPopupContents(NotificationObject obj)
+ {
+ shell.setLayout(new GridLayout());
+
+ Composite parent = _toolkit.createComposite(shell, SWT.NONE);
+ parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ GridLayout layout = new GridLayout(4, true);
+ parent.setLayout(layout);
+
+ // Object name record
+ Label key = _toolkit.createLabel(parent, COLUMN_OBJ, SWT.TRAIL);
+ GridData layoutData = new GridData(SWT.TRAIL, SWT.TOP, false, false,1,1);
+ key.setLayoutData(layoutData);
+ Text value = _toolkit.createText(parent, obj.getSourceName(), SWT.BEGINNING | SWT.BORDER |SWT.READ_ONLY);
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false,3,1));
+
+ // Sequence no record
+ key = _toolkit.createLabel(parent, COLUMN_SEQ, SWT.TRAIL);
+ layoutData = new GridData(SWT.TRAIL, SWT.TOP, false, false,1,1);
+ key.setLayoutData(layoutData);
+ value = _toolkit.createText(parent, ""+obj.getSequenceNo(), SWT.BEGINNING | SWT.BORDER |SWT.READ_ONLY);
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false,3,1));
+
+ // Time row
+ key = _toolkit.createLabel(parent, COLUMN_TIME, SWT.TRAIL);
+ key.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, true, false,1,1));
+ value = _toolkit.createText(parent, obj.getTimeStamp(), SWT.BEGINNING | SWT.BORDER | SWT.READ_ONLY);
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false,3,1));
+
+ key = _toolkit.createLabel(parent, COLUMN_TYPE, SWT.TRAIL);
+ key.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, true, false,1,1));
+ value = _toolkit.createText(parent, obj.getType(), SWT.BEGINNING | SWT.BORDER | SWT.READ_ONLY);
+ value.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false,3,1));
+
+ key = _toolkit.createLabel(parent, COLUMN_MSG, SWT.TRAIL);
+ key.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, true, false,1,1));
+ value = _toolkit.createText(parent, obj.getMessage(), SWT.MULTI | SWT.WRAP| SWT.BORDER | SWT.V_SCROLL | SWT.READ_ONLY);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+ gridData.heightHint = 100;
+ value.setLayoutData(gridData);
+ }
+ });
+ }
+
+ public void refresh()
+ {
+ _notifications = null;
+ _table.deselectAll();
+ _tableViewer.getTable().clearAll();
+
+ Control[] children = _form.getBody().getChildren();
+ for (int i = 0; i < children.length; i++)
+ {
+ children[i].setVisible(true);
+ }
+
+ workerRunning = true;
+ _form.layout(true);
+ _form.getBody().layout(true, true);
+ }
+
+ /**
+ * Content provider class for the table viewer
+ */
+ protected class ContentProviderImpl implements IStructuredContentProvider, INotificationViewer
+ {
+ public void inputChanged(Viewer v, Object oldInput, Object newInput)
+ {
+
+ }
+ public void dispose()
+ {
+
+ }
+ public Object[] getElements(Object parent)
+ {
+ return _notifications.toArray(new NotificationObject[0]);
+ }
+ public void addNotification(NotificationObject notification)
+ {
+ _tableViewer.add(notification);
+ }
+
+ public void addNotification(List<NotificationObject> notificationList)
+ {
+ _tableViewer.add(notificationList.toArray(new NotificationObject[0]));
+ }
+ }
+
+ /**
+ * Label provider for the table viewer
+ */
+ protected class LabelProviderImpl implements ITableLabelProvider
+ {
+ List<ILabelProviderListener> listeners = new ArrayList<ILabelProviderListener>();
+ public void addListener(ILabelProviderListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ public void dispose(){
+
+ }
+
+ public Image getColumnImage(Object element, int columnIndex)
+ {
+ return null;
+ }
+
+ public String getColumnText(Object element, int columnIndex)
+ {
+ String result = null;
+ NotificationObject t = (NotificationObject)element;
+ switch(columnIndex)
+ {
+ case 0 :
+ result = t.getSourceName();
+ break;
+ case 1 :
+ result = String.valueOf(t.getSequenceNo());
+ break;
+ case 2 :
+ result = String.valueOf(t.getTimeStamp());
+ break;
+ case 3 :
+ result = t.getType();
+ break;
+ case 4 :
+ result = t.getMessage();
+ break;
+ default :
+ result = "";
+ }
+
+ return result;
+ }
+
+ public boolean isLabelProperty(Object element, String property)
+ {
+ return false;
+ }
+
+ public void removeListener(ILabelProviderListener listener)
+ {
+ listeners.remove(listener);
+ }
+ } // end of LabelProviderImpl
+
+ protected boolean workerRunning = false;
+ protected void setWorkerRunning(boolean running)
+ {
+ workerRunning = running;
+ }
+
+ /**
+ * Worker class which keeps looking if there are new notifications coming from server for the selected mbean
+ */
+ private class Worker implements Runnable
+ {
+ public void run()
+ {
+ Display display = _tabFolder.getDisplay();
+ while(true)
+ {
+ if (!workerRunning || display == null)
+ {
+ sleep();
+ continue;
+ }
+
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (_form == null || _form.isDisposed())
+ return;
+ setWorkerRunning(_form.isVisible());
+ if (!workerRunning) return;
+
+ updateTableViewer();
+ }
+ });
+
+ sleep();
+ }
+ }
+
+ private void sleep()
+ {
+ try
+ {
+ Thread.sleep(2000);
+ }
+ catch(Exception ex)
+ {
+
+ }
+ }
+ }
+
+ /**
+ * Updates the table with new notifications received from mbean server for all mbeans
+ */
+ protected void updateTableViewer()
+ {
+ ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(MBeanView.getServer());
+ List<NotificationObject> newList = serverRegistry.getNotifications(null);
+ if (newList == null)
+ return;
+
+ _notifications = newList;
+ _tableViewer.setInput(_notifications);
+ _tableViewer.refresh();
+ }
+
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ViewUtility.java b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ViewUtility.java
new file mode 100644
index 0000000000..4f2b70f869
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ViewUtility.java
@@ -0,0 +1,641 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui.views;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.qpid.management.ui.ApplicationWorkbenchAdvisor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * Utility Class for displaying OpenMbean data types by creating required SWT widgets
+ * @author Bhupendra Bhardwaj
+ */
+public class ViewUtility
+{
+ public static final String OP_NAME = "operation_name";
+ public static final String OP_PARAMS = "parameters";
+ public static final String PARAMS_TEXT = "text";
+
+ public static final String FIRST = "First";
+ public static final String LAST = "Last";
+ public static final String NEXT = "Next";
+ public static final String PREV = "Previous";
+ public static final String INDEX = "Index";
+
+ private static final Comparator tabularDataComparator = new TabularDataComparator();
+
+ private static List<String> SUPPORTED_ARRAY_DATATYPES = new ArrayList<String>();
+ static
+ {
+ SUPPORTED_ARRAY_DATATYPES.add("java.lang.String");
+ SUPPORTED_ARRAY_DATATYPES.add("java.lang.Boolean");
+ SUPPORTED_ARRAY_DATATYPES.add("java.lang.Character");
+ SUPPORTED_ARRAY_DATATYPES.add("java.lang.Integer");
+ SUPPORTED_ARRAY_DATATYPES.add("java.lang.Long");
+ SUPPORTED_ARRAY_DATATYPES.add("java.lang.Double");
+ SUPPORTED_ARRAY_DATATYPES.add("java.util.Date");
+ }
+
+ /**
+ * Populates the composite with given openmbean data type (TabularType or CompositeType)
+ * @param toolkit
+ * @param parent composite
+ * @param data open mbean data type(either composite type or tabular data type)
+ */
+ public static void populateCompositeWithData(FormToolkit toolkit, Composite parent, Object data)
+ {
+ if (data instanceof TabularDataSupport)
+ {
+ ViewUtility.createTabularDataHolder(toolkit, parent, (TabularDataSupport)data);
+ }
+ else if (data instanceof CompositeDataSupport)
+ {
+ ViewUtility.populateCompositeWithCompositeData(toolkit, parent, (CompositeDataSupport)data);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void createTabularDataHolder(FormToolkit toolkit, Composite parent, TabularDataSupport tabularData)
+ {
+ Composite composite = toolkit.createComposite(parent, SWT.BORDER);
+ GridLayout layout = new GridLayout(4, true);
+ layout.horizontalSpacing = 0;
+ layout.marginWidth = 0;
+ layout.marginHeight = 10;
+ layout.verticalSpacing = 10;
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Set entrySet = tabularData.entrySet();
+ ArrayList<Map.Entry> list = new ArrayList<Map.Entry>(entrySet);
+ if (list.size() == 0)
+ {
+ Text text = toolkit.createText(composite, " No records ", SWT.CENTER | SWT.SINGLE | SWT.READ_ONLY);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1);
+ text.setLayoutData(layoutData);
+ return;
+ }
+
+ Collections.sort(list, tabularDataComparator);
+
+ // Attach the tabular record to be retrieved and shown later when record is traversed
+ // using first/next/previous/last buttons
+ composite.setData(list);
+
+ // Create button and the composite for CompositeData
+ Composite compositeDataHolder = createCompositeDataHolder(toolkit, composite,
+ tabularData.getTabularType().getRowType());
+
+ // display the first record
+ CompositeData data = (CompositeData)(list.get(0)).getValue();
+ composite.setData(INDEX, 0);
+ populateCompositeWithCompositeData(toolkit, compositeDataHolder, data);
+ enableOrDisableTraversalButtons(composite);
+ }
+
+ private static void enableOrDisableTraversalButtons(Composite composite)
+ {
+ int index = (Integer)composite.getData(INDEX);
+ int size = ((List)composite.getData()).size();
+
+ ((Button)composite.getData(FIRST)).setEnabled(true);
+ ((Button)composite.getData(PREV)).setEnabled(true);
+ ((Button)composite.getData(NEXT)).setEnabled(true);
+ ((Button)composite.getData(LAST)).setEnabled(true);
+
+ if (index == 0)
+ {
+ ((Button)composite.getData(FIRST)).setEnabled(false);
+ ((Button)composite.getData(PREV)).setEnabled(false);
+ }
+ if (index == size -1)
+ {
+ ((Button)composite.getData(NEXT)).setEnabled(false);
+ ((Button)composite.getData(LAST)).setEnabled(false);
+ }
+ }
+
+ /**
+ * Sets up the given composite for holding a CompositeData. Create traversal buttons, label etc and
+ * creates a child Composite, which should be populated with the CompositeData
+ * @param toolkit
+ * @param dataHolder
+ * @param compositeType
+ * @return
+ */
+ private static Composite createCompositeDataHolder(final FormToolkit toolkit, final Composite dataHolder, CompositeType compositeType)
+ {
+ String desc = compositeType.getDescription();
+ Label description = toolkit.createLabel(dataHolder, desc, SWT.CENTER);
+ description.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false, 4, 1));
+ // TODO nameLabel.setFont(font);
+ description.setText(desc);
+
+ // Add traversal buttons
+ final Button firstRecordButton = toolkit.createButton(dataHolder, FIRST, SWT.PUSH);
+ GridData layoutData = new GridData (GridData.HORIZONTAL_ALIGN_END);
+ layoutData.widthHint = 80;
+ firstRecordButton.setLayoutData(layoutData);
+
+ final Button nextRecordButton = toolkit.createButton(dataHolder, NEXT, SWT.PUSH);
+ layoutData = new GridData (GridData.HORIZONTAL_ALIGN_END);
+ layoutData.widthHint = 80;
+ nextRecordButton.setLayoutData(layoutData);
+
+ final Button previousRecordButton = toolkit.createButton(dataHolder, PREV, SWT.PUSH);
+ layoutData = new GridData (GridData.HORIZONTAL_ALIGN_BEGINNING);
+ layoutData.widthHint = 80;
+ previousRecordButton.setLayoutData(layoutData);
+
+ final Button lastRecordButton = toolkit.createButton(dataHolder, LAST, SWT.PUSH);
+ layoutData = new GridData (GridData.HORIZONTAL_ALIGN_BEGINNING);
+ layoutData.widthHint = 80;
+ lastRecordButton.setLayoutData(layoutData);
+
+ // Now create the composite, which will hold the CompositeData
+ final Composite composite = toolkit.createComposite(dataHolder, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.horizontalSpacing = layout.verticalSpacing = 0;
+ layout.marginHeight = layout.marginWidth = 0;
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
+
+ // Add button references. These references will be used when buttons
+ // are to enabled or disabled based of record index. e.g. for first record
+ // First and Previous buttons will be disabled.
+ dataHolder.setData(FIRST, firstRecordButton);
+ dataHolder.setData(NEXT, nextRecordButton);
+ dataHolder.setData(PREV, previousRecordButton);
+ dataHolder.setData(LAST, lastRecordButton);
+
+ // Listener for the traversal buttons. When a button is clicked the respective
+ // CompositeData will be populated in the composite
+ SelectionListener listener = new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (!(e.widget instanceof Button))
+ return;
+
+ Button traverseButton =(Button)e.widget;
+ // Get the CompositeData respective to the button selected
+ CompositeData data = getCompositeData(dataHolder, traverseButton.getText());
+ populateCompositeWithCompositeData(toolkit, composite, data);
+ enableOrDisableTraversalButtons(dataHolder);
+ }
+ };
+
+ firstRecordButton.addSelectionListener(listener);
+ nextRecordButton.addSelectionListener(listener);
+ previousRecordButton.addSelectionListener(listener);
+ lastRecordButton.addSelectionListener(listener);
+
+ return composite;
+ }
+
+ /**
+ * The CompositeData is set as data with the Composite and using the index, this method will
+ * return the corresponding CompositeData
+ * @param compositeHolder
+ * @param dataIndex
+ * @return the CompositeData respective to the index
+ */
+ private static CompositeData getCompositeData(Composite compositeHolder, String dataIndex)
+ {
+ List objectData = (List)compositeHolder.getData();
+ if (objectData == null || objectData.isEmpty())
+ {
+ // TODO
+ }
+
+ // Get the index of record to be shown.
+ int index = 0;
+ if (compositeHolder.getData(INDEX) != null)
+ {
+ index = (Integer)compositeHolder.getData(INDEX);
+ }
+
+ if (FIRST.equals(dataIndex))
+ {
+ index = 0;
+ }
+ else if (NEXT.equals(dataIndex))
+ {
+ index = index + 1;
+ }
+ else if (PREV.equals(dataIndex))
+ {
+ index = (index != 0) ? (index = index - 1) : index;
+ }
+ else if (LAST.equals(dataIndex))
+ {
+ index = objectData.size() -1;
+ }
+
+ // Set the index being shown.
+ compositeHolder.setData(INDEX, index);
+
+ return (CompositeData)((Map.Entry)objectData.get(index)).getValue();
+ }
+
+ /**
+ * Populates the given composite with the CompositeData. Creates required widgets to hold the data types
+ * @param toolkit
+ * @param parent
+ * @param data CompositeData
+ */
+ @SuppressWarnings("unchecked")
+ private static void populateCompositeWithCompositeData(FormToolkit toolkit, Composite parent, CompositeData data)
+ {
+ Control[] oldControls = parent.getChildren();
+ for (int i = 0; i < oldControls.length; i++)
+ {
+ oldControls[i].dispose();
+ }
+
+ Composite compositeHolder = toolkit.createComposite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(4, false);
+ layout.horizontalSpacing = 10;
+ compositeHolder.setLayout(layout);
+ compositeHolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+
+ // ItemNames in composite data
+ List<String> itemNames = new ArrayList<String>(data.getCompositeType().keySet());
+
+ for (String itemName : itemNames)
+ {
+ OpenType itemType = data.getCompositeType().getType(itemName);
+ Label keyLabel = toolkit.createLabel(compositeHolder, itemName, SWT.TRAIL);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
+ layoutData.minimumWidth = 70;
+ keyLabel.setLayoutData(layoutData);
+
+ if (itemType.isArray())
+ {
+ OpenType type = ((ArrayType)itemType).getElementOpenType();
+ // If Byte array and mimetype is text, convert to text string
+ if (type.getClassName().equals(Byte.class.getName()))
+ {
+ String mimeType = null;
+ String encoding = null;
+ if (data.containsKey("MimeType"))
+ {
+ mimeType = (String)data.get("MimeType");
+ }
+ if (data.containsKey("Encoding"))
+ {
+ encoding = (String)data.get("Encoding");
+ }
+
+ if (encoding == null || encoding.length() == 0)
+ {
+ encoding = Charset.defaultCharset().name();
+ }
+
+ if ("text/plain".equals(mimeType))
+ {
+ convertByteArray(toolkit, compositeHolder, data, itemName, encoding);
+ }
+ else
+ {
+ setNotSupportedDataType(toolkit, compositeHolder);
+ }
+ }
+ // If array of any other supported type, show as a list of String array
+ else if (SUPPORTED_ARRAY_DATATYPES.contains(type.getClassName()))
+ {
+ convertArrayItemForDisplay(compositeHolder, data, itemName);
+ }
+ else
+ {
+ setNotSupportedDataType(toolkit, compositeHolder);
+ }
+ }
+ else if (itemType instanceof TabularType)
+ {
+ Composite composite = toolkit.createComposite(compositeHolder, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1));
+ layout = new GridLayout();
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ composite.setLayout(layout);
+ createTabularDataHolder(toolkit, composite, (TabularDataSupport)data.get(itemName));
+ }
+ else
+ {
+ Text valueText = toolkit.createText(compositeHolder, String.valueOf(data.get(itemName)), SWT.READ_ONLY | SWT.BORDER);
+ valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1));
+ }
+ }
+
+ // layout the composite after creating new widgets.
+ parent.layout();
+ } //end of method
+
+
+ private static void convertByteArray(FormToolkit toolkit, Composite compositeHolder, CompositeData data, String itemName, String encoding)
+ {
+ Byte[] arrayItems = (Byte[])data.get(itemName);
+ byte[] byteArray = new byte[arrayItems.length];
+
+ for (int i = 0; i < arrayItems.length; i++)
+ {
+ byteArray[i] = arrayItems[i];
+ }
+ try
+ {
+ String textMessage = new String(byteArray, encoding);
+
+ Text valueText = toolkit.createText(compositeHolder, textMessage, SWT.READ_ONLY | SWT.BORDER |
+ SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+ gridData.heightHint = 300;
+ valueText.setLayoutData(gridData);
+ }
+ catch(Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ public static int popupInfoMessage(String title, String message)
+ {
+ MessageBox messageBox = new MessageBox(Display.getCurrent().getActiveShell(), SWT.ICON_INFORMATION | SWT.OK);
+ messageBox.setMessage(message);
+ messageBox.setText(title);
+ int response = messageBox.open();
+
+ return response;
+ }
+
+ public static int popupErrorMessage(String title, String message)
+ {
+ MessageBox messageBox = new MessageBox(Display.getCurrent().getActiveShell(), SWT.ICON_ERROR | SWT.OK);
+ messageBox.setMessage(message);
+ messageBox.setText(title);
+ int response = messageBox.open();
+
+ return response;
+ }
+
+ public static int popupConfirmationMessage(String title, String message)
+ {
+ MessageBox messageBox = new MessageBox(Display.getCurrent().getActiveShell(),
+ SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL);
+ messageBox.setMessage(message);
+ messageBox.setText(title);
+ int response = messageBox.open();
+
+ return response;
+ }
+
+ public static void popupError(String title, String message, Throwable ex)
+ {
+ IStatus status = new Status(IStatus.ERROR, ApplicationWorkbenchAdvisor.PERSPECTIVE_ID,
+ IStatus.ERROR, ex.toString(), ex);
+ ErrorDialog.openError(Display.getCurrent().getActiveShell(), title, message, status);
+
+ }
+
+ public static void popupError(String errorMsg)
+ {
+ Display display = Display.getCurrent();
+ Shell shell = new Shell(display, SWT.BORDER | SWT.CLOSE | SWT.MIN | SWT.MAX);
+ shell.setText("Attribute");
+ shell.setLayout(new GridLayout());
+ int x = display.getBounds().width;
+ int y = display.getBounds().height;
+ int width = 500;
+ int height = 250;
+ shell.setBounds(x/4, y/4, width, height);
+
+ Label label = new Label(shell, SWT.NONE);
+ label.setText(errorMsg);
+ label.setLayoutData(new GridData(SWT.TRAIL, SWT.TOP, false, false));
+
+ shell.open();
+ while (!shell.isDisposed())
+ {
+ if (!display.readAndDispatch())
+ {
+ display.sleep();
+ }
+ }
+ shell.dispose();
+ }
+
+ public static Shell createPopupShell(String title, int width, int height)
+ {
+ Display display = Display.getCurrent();
+ Shell shell = new Shell(display, SWT.BORDER | SWT.CLOSE | SWT.MIN |SWT.MAX);
+ shell.setText(title);
+ shell.setLayout(new GridLayout());
+ int x = display.getBounds().width;
+ int y = display.getBounds().height;
+ shell.setBounds(x/4, y/4, width, height);
+
+ return shell;
+ }
+
+ /**
+ * Creates a List widget for displaying array of strings
+ * @param compositeHolder
+ * @param data - containing the array item value
+ * @param itemName - item name
+ */
+ private static void convertArrayItemForDisplay(Composite compositeHolder, CompositeData data, String itemName)
+ {
+ Object[] arrayItems = (Object[])data.get(itemName);
+ String[] items = new String[arrayItems.length];
+ for (int i = 0; i < arrayItems.length; i++)
+ {
+ items[i] = String.valueOf(arrayItems[i]);
+ }
+ org.eclipse.swt.widgets.List list = new org.eclipse.swt.widgets.List(compositeHolder,
+ SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.READ_ONLY);
+ list.setItems(items);
+ //list.setBackground(compositeHolder.getBackground());
+ list.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
+ }
+
+ private static void setNotSupportedDataType(FormToolkit toolkit, Composite compositeHolder)
+ {
+ Text valueText = toolkit.createText(compositeHolder, "--- Content can not be displayed ---", SWT.READ_ONLY);
+ valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1));
+ }
+
+ /**
+ * Converts the input string to displayable format by converting some character case or inserting space
+ * @param input
+ * @return formatted string
+ */
+ public static String getDisplayText(String input)
+ {
+ StringBuffer result = new StringBuffer(input);
+ if (Character.isLowerCase(result.charAt(0)))
+ {
+ result.setCharAt(0, Character.toUpperCase(result.charAt(0)));
+ }
+ for (int i = 1; i < input.length(); i++)
+ {
+ if (Character.isUpperCase(result.charAt(i)) && !Character.isWhitespace(result.charAt(i - 1))
+ && Character.isLowerCase(result.charAt(i - 1)))
+ {
+ result.insert(i, " ");
+ i++;
+ }
+ else if (Character.isLowerCase(result.charAt(i)) && Character.isWhitespace(result.charAt(i - 1)))
+ {
+ result.setCharAt(i, Character.toUpperCase(result.charAt(i)));
+ }
+
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Disposes the children of given Composite if not null and not already disposed
+ * @param parent composite
+ */
+ public static void disposeChildren(Composite parent)
+ {
+ if (parent == null || parent.isDisposed())
+ return;
+
+ Control[] oldControls = parent.getChildren();
+ for (int i = 0; i < oldControls.length; i++)
+ {
+ oldControls[i].dispose();
+ }
+ }
+
+ public static char[] getMD5HashedCharArray(Object text) throws NoSuchAlgorithmException, UnsupportedEncodingException
+ {
+ byte[] data = ((String)text).getBytes("utf-8");
+
+ MessageDigest md = MessageDigest.getInstance("MD5");
+
+ for (byte b : data)
+ {
+ md.update(b);
+ }
+
+ byte[] digest = md.digest();
+
+ char[] byteArray = new char[digest.length];
+ int index = 0;
+ for (byte b : digest)
+ {
+ byteArray[index++] = (char)b;
+ }
+ return byteArray;
+ }
+
+ public static char[] getHash(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException
+ {
+ byte[] data = text.getBytes("utf-8");
+
+ MessageDigest md = MessageDigest.getInstance("MD5");
+
+ for (byte b : data)
+ {
+ md.update(b);
+ }
+
+ byte[] digest = md.digest();
+
+ char[] hash = new char[digest.length ];
+
+ int index = 0;
+ for (byte b : digest)
+ {
+ hash[index++] = (char) b;
+ }
+
+ return hash;
+ }
+
+ private static class TabularDataComparator implements java.util.Comparator<Map.Entry>
+ {
+ public int compare(Map.Entry data1, Map.Entry data2)
+ {
+ if (data1.getKey() instanceof List)
+ {
+ Object obj1 = ((List)data1.getKey()).get(0);
+ Object obj2 = ((List)data2.getKey()).get(0);
+ String str1 = obj1.toString();
+ String str2 = obj2.toString();
+ if (obj1 instanceof String)
+ {
+ return str1.compareTo(str2);
+ }
+
+ try
+ {
+ return Long.valueOf(str1).compareTo(Long.valueOf(str2));
+ }
+ catch (Exception ex)
+ {
+ return -1;
+ }
+ }
+
+ return -1;
+ }
+ }
+}
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/.eclipseproduct b/Final/java/management/eclipse-plugin/src/main/resources/.eclipseproduct
new file mode 100644
index 0000000000..28ee27ca17
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/.eclipseproduct
@@ -0,0 +1,23 @@
+#Eclipse Product File
+#Fri Nov 03 14:47:54 GMT 2006
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+version=1.0-incubating-M2-SNAPSHOT
+name=Qpid Management Console
+id=org.apache.qpid.manager.gui.product
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/eclipse.exe b/Final/java/management/eclipse-plugin/src/main/resources/eclipse.exe
new file mode 100644
index 0000000000..7826d1ed80
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/eclipse.exe
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/eclipse.ini b/Final/java/management/eclipse-plugin/src/main/resources/eclipse.ini
new file mode 100644
index 0000000000..6a4ecb5b5d
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/eclipse.ini
@@ -0,0 +1,4 @@
+-vmargs
+-Xms40m
+-Xmx256m
+-Declipse.consoleLog=true
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/license.eclipse.txt b/Final/java/management/eclipse-plugin/src/main/resources/license.eclipse.txt
new file mode 100644
index 0000000000..da433e89f9
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/license.eclipse.txt
@@ -0,0 +1,88 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
+
+
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/sasl/MANIFEST.MF b/Final/java/management/eclipse-plugin/src/main/resources/sasl/MANIFEST.MF
new file mode 100644
index 0000000000..d18b1a073d
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/sasl/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: jmx sasl Plug-in
+Bundle-SymbolicName: jmxremote.sasl
+Bundle-Version: 1.0.1
+Bundle-ClassPath: .
+Bundle-Vendor:
+Bundle-Localization: plugin
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/startup.jar b/Final/java/management/eclipse-plugin/src/main/resources/startup.jar
new file mode 100644
index 0000000000..2f26eceece
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/startup.jar
Binary files differ
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/unix/configuration/config.ini b/Final/java/management/eclipse-plugin/src/main/resources/unix/configuration/config.ini
new file mode 100644
index 0000000000..aa2d21fd48
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/unix/configuration/config.ini
@@ -0,0 +1,27 @@
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+###############################################################################
+
+#Product Runtime Configuration File
+
+osgi.splashPath=platform:/base/plugins/org.apache.qpid.management.ui
+eclipse.product=org.apache.qpid.management.ui.product
+eclipse.application=org.apache.qpid.management.ui.application
+osgi.bundles=org.eclipse.equinox.common@2:start,org.eclipse.core.runtime@start,com.ibm.icu,org.apache.qpid.management.ui,org.eclipse.core.commands,org.eclipse.core.contenttype,org.eclipse.core.expressions,org.eclipse.core.jobs,org.eclipse.core.runtime.compatibility.auth,org.eclipse.core.runtime.compatibility.registry,org.eclipse.equinox.preferences,org.eclipse.equinox.registry,org.eclipse.help,org.eclipse.jface,org.eclipse.swt,org.eclipse.swt.motif.linux.x86,org.eclipse.swt.gtk.linux.x86_64,org.eclipse.swt.gtk.linux.x86,org.eclipse.swt.gtk.linux.ppc,org.eclipse.swt.motif.hpux.PA_RISC,org.eclipse.swt.gtk.solaris.sparc,org.eclipse.swt.motif.solaris.sparc,org.eclipse.swt.carbon.macocx,org.eclipse.ui,org.eclipse.ui.forms,org.eclipse.ui.workbench
+osgi.bundles.defaultStartLevel=4
+eof=eof \ No newline at end of file
diff --git a/Final/java/management/eclipse-plugin/src/main/resources/win32/configuration/config.ini b/Final/java/management/eclipse-plugin/src/main/resources/win32/configuration/config.ini
new file mode 100644
index 0000000000..e83321e650
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/main/resources/win32/configuration/config.ini
@@ -0,0 +1,26 @@
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+###############################################################################
+
+#Product Runtime Configuration File
+
+osgi.splashPath=platform:/base/plugins/org.apache.qpid.management.ui
+eclipse.product=org.apache.qpid.management.ui.product
+eclipse.application=org.apache.qpid.management.ui.application
+osgi.bundles=org.eclipse.equinox.common@2:start,org.eclipse.core.runtime@start,com.ibm.icu,org.apache.qpid.management.ui,org.eclipse.core.commands,org.eclipse.core.contenttype,org.eclipse.core.expressions,org.eclipse.core.jobs,org.eclipse.core.runtime.compatibility.auth,org.eclipse.core.runtime.compatibility.registry,org.eclipse.equinox.preferences,org.eclipse.equinox.registry,org.eclipse.help,org.eclipse.jface,org.eclipse.swt,org.eclipse.swt.win32.win32.x86,org.eclipse.ui,org.eclipse.ui.forms,jmxremote.sasl,org.eclipse.ui.workbench
+osgi.bundles.defaultStartLevel=4
diff --git a/Final/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java b/Final/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java
new file mode 100644
index 0000000000..56eadbb3b2
--- /dev/null
+++ b/Final/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java
@@ -0,0 +1,118 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.ui;
+
+import junit.framework.TestCase;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.DestNameExchange;
+import org.apache.qpid.server.management.AMQManagedObject;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueMBean;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import javax.management.MBeanFeatureInfo;
+import javax.management.MBeanInfo;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Test class to test if any change in the broker JMX code is affesting the management console
+ * There are some hardcoding of management feature names and parameter names to create a customized
+ * look in the console.
+ */
+public class ManagementConsoleTest extends TestCase
+{
+ private VirtualHost _virtualHost;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
+ _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ }
+
+ /**
+ * Test for AMQQueueMBean attribute and operation names, which are used in the management console
+ * @throws Exception
+ */
+ public void testAMQQueueMBeanInfo() throws Exception
+ {
+ // If this test fails due to changes in the broker code,
+ // then the constants in the Constants.java shoule be updated accordingly
+ AMQQueue queue = new AMQQueue(new AMQShortString("testQueueForManagement"), false, null, false, _virtualHost);
+ AMQManagedObject mbean = new AMQQueueMBean(queue);
+ MBeanInfo mbeanInfo = mbean.getMBeanInfo();
+
+ List<String> operationNames = getNamesList(mbeanInfo.getOperations());
+ assertTrue(operationNames.contains(Constants.OPERATION_MOVE_MESSAGES));
+
+ List<String> attributesList = getNamesList(mbeanInfo.getAttributes());
+ assertTrue(attributesList.contains(Constants.ATTRIBUTE_QUEUE_CONSUMERCOUNT));
+ assertTrue(attributesList.contains(Constants.ATTRIBUTE_QUEUE_DEPTH));
+ }
+
+ /**
+ * Test for Exchange MBean attribute and operation names, which are used in the management console
+ * @throws Exception
+ */
+ public void testExchangeMBeanInfo() throws Exception
+ {
+ // If this test fails due to changes in the broker code,
+ // then the constants in the Constants.java shoule be updated accordingly
+ DestNameExchange exchange = new DestNameExchange();
+ exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true);
+ AMQManagedObject mbean = (AMQManagedObject)exchange.getManagedObject();
+ MBeanInfo mbeanInfo = mbean.getMBeanInfo();
+
+ // Check for the Exchange Type property in the ObjectName
+ assertNotNull(mbean.getObjectName().getKeyProperty(Constants.EXCHANGE_TYPE));
+
+ // Check for operation names
+ List<String> operationNames = getNamesList(mbeanInfo.getOperations());
+ assertTrue(operationNames.contains(Constants.OPERATION_CREATE_BINDING));
+ }
+
+ /**
+ * Test for VirtualHostManagerMBean features used in Management console for customizing the GUI
+ * @throws Exception
+ */
+ public void testVirtualHostManagerMBeanInfo() throws Exception
+ {
+ AMQManagedObject mbean = (AMQManagedObject)_virtualHost.getManagedObject();
+ assertTrue(mbean.getType().equals(Constants.VIRTUAL_HOST));
+ }
+
+ private List<String> getNamesList(MBeanFeatureInfo[] features)
+ {
+ List<String> names = new ArrayList<String>();
+ for (MBeanFeatureInfo feature : features)
+ {
+ names.add(feature.getName());
+ }
+
+ return names;
+ }
+}
diff --git a/Final/java/perftests/RunningPerformanceTests.txt b/Final/java/perftests/RunningPerformanceTests.txt
new file mode 100644
index 0000000000..54291483bf
--- /dev/null
+++ b/Final/java/perftests/RunningPerformanceTests.txt
@@ -0,0 +1,141 @@
+The Performance Tests
+-------------------------
+
+Building the Tests (Only develoeprs need to know how to do this).
+-----------------------------------------------------------------
+
+The performance tests are compiled as part of the Maven build by default, but the performance test scripts are not. There is also an additional step to perform, that generates a convenient Jar file containing all of the test dependencies, to simplify invoking Java with a very long command line. The steps to build the performance test suite are:
+
+ 1. Cd to the /java/perftests directory.
+ 2. Execute: mvn uk.co.thebadgerset:junit-toolkit-maven-plugin:tkscriptgen (this generates the scripts).
+ 3. Execute: mvn assembly:assembly
+
+The assembly:assembly step generates a Jar with all the dependecies in it in a file name ending with -all-test-deps.jar, which contains the client code and all its dependencies, plus JUnit and the toolkit test runners. The generated scripts expect to find the jar in the current directory. You can Cd to the /target directory and run the scripts from there. The assembly:assembly step also outputs some archives that contain all the scripts and required Jar for convenient shipping of the test suite. Unpack this anywhere you like and run the tests from there.
+
+Running the Tests
+-----------------
+
+All the performance tests are run through shell scripts, that have been configured with parameters set up in the pom.xml file. You can override any of these parameters on the command line. It is also possible to pass parameters through the script to the JVM that is running the test. For example to change the heap size you might do:
+
+./Ping-Once.sh -java:Xmx1024M
+
+The tests have all been set up to accept a single integer 'size' parameter, passed to the JUnit TestCase for the test, through the JUnit Toolkit asymptotic test case extension. The 'size' parameter is always interpreted in the performance tests as standing for the number of messages to send per test method invocation. Therefore, in the results of the test the throughput of messages is equal to the number of test method invocations times the 'size' divided by the time taken.
+
+** TODO: Change this, use seconds not millis.
+
+Test timing results are output to .csv files, which can easily be imported into a spreadsheet for graphing and analysis. The timings in this file are always given in milliseconds, which may be a bit confusing and should really be changed to seconds.
+
+The JUnit Toolkit provides a framework for controlling how long tests are run for, how many are run at once and what the 'size' parameter is, which is general enough to handle a wide variety of performance tests. Here is the documentation from the TKTestRunner class that explains what its command line parameters are:
+
+ ...
+ * 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.
+ * -v Verbose mode.
+ * </pre>
+ *
+ * <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.
+ *
+ * <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)
+ * </table>
+ ...
+
+The specific performance test cases for QPid are implemented as extensions to JUnit TestCase (asymptotic test cases), and also accept a large number of different parameters to control the characteristics of the test. The are passed into the test scripts as name=value pairs. Here is the documentation from the PingPongProducer class that explains what the available parameters and default values are:
+
+ ...
+ * <p/>This ping tool accepts a vast number of configuration options, all of which are passed in to the constructor. It
+ * can ping topics or queues; ping multiple destinations; do persistent pings; send messages of any size; do pings within
+ * transactions; control the number of pings to send in each transaction; limit its sending rate; and perform failover
+ * testing. A complete list of accepted parameters, default values and comments on their usage is provided here:
+ *
+ * <p/><table><caption>Parameters</caption>
+ * <tr><th> Parameter <th> Default <th> Comments
+ * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers.
+ * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping.
+ * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used.
+ * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions.
+ * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to.
+ * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over.
+ * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit.
+ * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message.
+ * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default.
+ * <tr><td> failAfterCommit <td> false <td> Whether to prompt user to kill broker after a commit batch.
+ * <tr><td> failBeforeCommit <td> false <td> Whether to prompt user to kill broker before a commit batch.
+ * <tr><td> failAfterSend <td> false <td> Whether to prompt user to kill broker after a send.
+ * <tr><td> failBeforeSend <td> false <td> Whether to prompt user to kill broker before a send.
+ * <tr><td> failOnce <td> true <td> Whether to prompt for failover only once.
+ * <tr><td> username <td> guest <td> The username to access the broker with.
+ * <tr><td> password <td> guest <td> The password to access the broker with.
+ * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with.
+ * <tr><td> destinationCount <td> 1 <td> The number of receivers listening to the pings.
+ * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies.
+ * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode.
+ * <tr><td> uniqueDests <td> true <td> Whether each receiver only listens to one ping destination or all.
+ * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used.
+ * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are:
+ * 0 - SESSION_TRANSACTED
+ * 1 - AUTO_ACKNOWLEDGE
+ * 2 - CLIENT_ACKNOWLEDGE
+ * 3 - DUPS_OK_ACKNOWLEDGE
+ * 257 - NO_ACKNOWLEDGE
+ * 258 - PRE_ACKNOWLEDGE
+ * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received.
+ * Limits the volume of messages currently buffered on the client
+ * or broker. Can help scale test clients by limiting amount of buffered
+ * data to avoid out of memory errors.
+ * </table>
+ ...
+
+The most common test case to run is implemented in the class PingAsyncTestPerf, which sends and recieves messages simultaneously. This class uses a PingPongProdicer to do its sending and receiving, and wraps it in a suitable way to make it callable through the extended JUnit test runner. This class also accpets another parameter "batchSize" with a default of "1000". This tells the test how many messages to send before stopping sending and waiting for them all to come back. The actual value entered does not matter too much, but typically values larger than 1000 are used to ensure that there is a reasonable opportunity for simultaneous sending and receiving, and less than 10000 to ensure that each test method invocation does not go on for too long.
+
+The test script parameters can all be seen in the pom.xml file. A three letter code is used on the test scripts, first letter P or T for persistent or transient, second letter Q or T for queue (p2p) or topic (pub/sub), third letter R for reliability tests, C for client scaling tests, M for message size tests.Typically tests run and sample their results for 10 minutes, to get a reasonable measurement of a broker running under a steady load. The tests as configured do not measure 'burst' performance.
+
+The reliability/burn in tests, test the broker running at slightly below its maximum throughput for a period of 24 hours. Their purpose is to check that the broker remains stable under load for a reasonable duration, in order to provide some confidence in the long-term stability of its process. These tests are intended to be run as a two step process. The first two tests run for 10 minutes and are used to asses the broker throughput for the test. The output from these tests are to be fed into the rate limiter for the second set of tests, so that the broker may be set up to run at slightly below its maximum throughput for the 24 hour duration. It is suggested that 90% of the rate achieved by the first two tests should be used for this.
+
+The client scaling tests are split into two sub-sections. The first section tests the performance of increasing numbers of client connections, each sending at a fixed rate. The purpose of this is to determine the brokers saturation load, and to evaluate how its performance degrades uder higher loads. The second section varies the fan-out or fan-in ratio of the number of sending clients to receving clients. This is primarily intended to test the pubsub messaging model, but the tests are also run in p2p mode (with each message being received by one consumer), for completeness and to provide a comparison with the pubsub performance.
+
+The message size scaling tests, examine the brokers performance with different message payload sizes. The purpose of these tests is to evaluate where the broker process switches from being an io-bound to a cpu-bound process (if at all). The expected model is that the amount of CPU processing the broker has to carry out depends largely on the number of messages, and not on their size, because it carries out de-framing and routing for each message header but just copies payloads in-place or in a tight instruction loop. Therefore large message should be io-bound and a constant data rate through the broker should be seen for messages larger than the io/cpu threshold. Small messages require more processing so a constant message rate should be seen for message smaller than the io/cpu threshold. If the broker implementation is extremely efficient the threshold may dissapear altogether and the broker will be purely io-bound.
+The final variation, which is applied to all tests, is to run a transactional and non-transactional version of each. Messages are always batched into transactions of 100 messages each.
+
+Running the entire test suite can take some time, in particular their are about 4 24 hour burn-in tests. There are also 8 30 minute client scaling ramp up tests. If you want to run the test for a short time, to skim test that they work on your environment a command line like the following is usefull:
+
+> find . -name '*.sh' -exec {} -d10S \;
+
+If you want to run just a sub-set of the tests, you can use variations of the above command line. For example, to run just the message size tests using persistent p2p messaging do:
+
+> find . -name 'PPM-*.sh' -exec {} \;
+
+and so on.
+
+Interpreting the Results
+------------------------
+
+TODO: Explain what the results are expected to show and how to look for it. What should be graphed to get a visualization of the broker performance. How to turn the measurements into a description of the performance 'envelope'. \ No newline at end of file
diff --git a/Final/java/perftests/bin/run_many.sh b/Final/java/perftests/bin/run_many.sh
new file mode 100755
index 0000000000..cca2ffec21
--- /dev/null
+++ b/Final/java/perftests/bin/run_many.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+# args:
+# <number of processes to start>
+# <name of run>
+# <command ro run>
+
+for i in `seq 1 $1`; do
+ $3 >$2.$i.out 2>>$2.err &
+ echo $! > $2.$i.pid
+done;
diff --git a/Final/java/perftests/bin/topicListener.sh b/Final/java/perftests/bin/topicListener.sh
new file mode 100755
index 0000000000..a728592cd7
--- /dev/null
+++ b/Final/java/perftests/bin/topicListener.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+# XXX -Xmx512m -Xms512m -XX:NewSize=150m
+QPID_LIBS=$QPID_HOME/lib/qpid-incubating.jar
+TEST_JAR=$QPID_HOME/../../../../perftests/target/qpid-perftests-1.0-incubating-M2-SNAPSHOT.jar
+
+# Set other variables used by the qpid-run script before calling
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM="-Xmx128m -Dlog4j.configuration=$HOME/log4j.properties" \
+ JAVA_GC="-XX:-UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \
+ QPID_CLASSPATH=$QPID_LIBS:$TEST_JAR
+
+. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.oldtopic.Listener $*
diff --git a/Final/java/perftests/bin/topicPublisher.sh b/Final/java/perftests/bin/topicPublisher.sh
new file mode 100755
index 0000000000..f9b6bc19fe
--- /dev/null
+++ b/Final/java/perftests/bin/topicPublisher.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# XXX -Xmx512m -Xms512m -XX:NewSize=150m
+QPID_LIBS=$QPID_HOME/lib/qpid-incubating.jar
+TEST_JAR=$QPID_HOME/../../../../perftests/target/qpid-perftests-1.0-incubating-M2-SNAPSHOT.jar
+
+# Set other variables used by the qpid-run script before calling
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx128m \
+ JAVA_GC="-XX:-UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \
+ QPID_CLASSPATH=$QPID_LIBS:$TEST_JAR
+
+. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.oldtopic.Publisher $*
diff --git a/Final/java/perftests/dist-zip.xml b/Final/java/perftests/dist-zip.xml
new file mode 100644
index 0000000000..0039bfb157
--- /dev/null
+++ b/Final/java/perftests/dist-zip.xml
@@ -0,0 +1,45 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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 zip containing all the test scripts and
+ the all dependencies jar. -->
+<assembly>
+ <id>perftests</id>
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <fileSets>
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>PerformanceTests</outputDirectory>
+ <includes>
+ <include>qpid-perftests-${qpid.version}-all-test-deps.jar</include>
+ <include>*.sh</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>etc/scripts</directory>
+ <outputDirectory>PerformanceTests</outputDirectory>
+ <includes>
+ <include>*.sh</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/Final/java/perftests/distribution/pom.xml b/Final/java/perftests/distribution/pom.xml
new file mode 100644
index 0000000000..24b9bd6b67
--- /dev/null
+++ b/Final/java/perftests/distribution/pom.xml
@@ -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.
+ -->
+<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-perftests-distribution</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Performance Tests Distribution</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <java.source.version>1.5</java.source.version>
+ <qpid.version>${pom.version}</qpid.version>
+ <qpid.targetDir>${project.build.directory}</qpid.targetDir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-perftests</artifactId>
+ <type>jar</type>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-perftests</artifactId>
+ <type>test-jar</type>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>uk.co.thebadgerset</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>uk.co.thebadgerset</groupId>
+ <artifactId>junit-toolkit-maven-plugin</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <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>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>${assembly.version}</version>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/performance.xml</descriptor>
+ </descriptors>
+ <finalName>qpid-${pom.version}</finalName>
+ <outputDirectory>${qpid.targetDir}</outputDirectory>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <finalName>qpid-performance</finalName>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/performance.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ </build>
+
+</project>
diff --git a/Final/java/perftests/distribution/src/main/assembly/performance.xml b/Final/java/perftests/distribution/src/main/assembly/performance.xml
new file mode 100644
index 0000000000..a564261a24
--- /dev/null
+++ b/Final/java/perftests/distribution/src/main/assembly/performance.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <id>performance-test-java</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <!-- Apache Licensing -->
+ <fileSet>
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory>../../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ <!-- Performance txt files-->
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet>
+
+ <!-- Execution Scripts -->
+ <fileSet>
+ <directory>../bin</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <includes>
+ <include>*</include>
+ </includes>
+ <fileMode>777</fileMode>
+ </fileSet>
+
+ <!-- Provide Source in easy access location -->
+ <fileSet>
+ <directory>../src/main</directory>
+ <outputDirectory>qpid-${qpid.version}/src</outputDirectory>
+ <includes>
+ <include>**/*.java</include>
+ <include>**/*.log4j</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>../src/test</directory>
+ <outputDirectory>qpid-${qpid.version}/src</outputDirectory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </fileSet>
+
+ <!-- Metadata Jar -->
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-performance.jar</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-perftests-distribution</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/perftests/etc/scripts/CTQ-Qpid-1.sh b/Final/java/perftests/etc/scripts/CTQ-Qpid-1.sh
new file mode 100755
index 0000000000..057dec5c63
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/CTQ-Qpid-1.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+./PQ-Qpid-1.sh $@ & ./PQ-Qpid-1-P.sh $@
diff --git a/Final/java/perftests/etc/scripts/CTQ-Qpid-2.sh b/Final/java/perftests/etc/scripts/CTQ-Qpid-2.sh
new file mode 100755
index 0000000000..08057cbf44
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/CTQ-Qpid-2.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+./PQ-Qpid-2.sh $@ & ./PQ-Qpid-2-P.sh $@
diff --git a/Final/java/perftests/etc/scripts/CTQ-Qpid-3.sh b/Final/java/perftests/etc/scripts/CTQ-Qpid-3.sh
new file mode 100755
index 0000000000..0bb7e25197
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/CTQ-Qpid-3.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+./PQ-Qpid-3.sh $@ & ./PQ-Qpid-3-P.sh $@
diff --git a/Final/java/perftests/etc/scripts/CTQ-Qpid-4.sh b/Final/java/perftests/etc/scripts/CTQ-Qpid-4.sh
new file mode 100755
index 0000000000..278e44d12b
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/CTQ-Qpid-4.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+./PQ-Qpid-4.sh $@ & ./PQ-Qpid-4-P.sh $@
diff --git a/Final/java/perftests/etc/scripts/CTQ-Qpid-5.sh b/Final/java/perftests/etc/scripts/CTQ-Qpid-5.sh
new file mode 100755
index 0000000000..5dee0176b2
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/CTQ-Qpid-5.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+./PQ-Qpid-5.sh $@ & ./PQ-Qpid-5-P.sh $@
diff --git a/Final/java/perftests/etc/scripts/CTQ-Qpid-6.sh b/Final/java/perftests/etc/scripts/CTQ-Qpid-6.sh
new file mode 100755
index 0000000000..b9996da77d
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/CTQ-Qpid-6.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+./PQ-Qpid-6.sh $@ & ./PQ-Qpid-6-P.sh $@
diff --git a/Final/java/perftests/etc/scripts/PT-Qpid-13.sh b/Final/java/perftests/etc/scripts/PT-Qpid-13.sh
new file mode 100755
index 0000000000..c2c6d6fd81
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/PT-Qpid-13.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Parse arguements taking all - prefixed args as JAVA_OPTS
+for arg in "$@"; do
+ if [[ $arg == -java:* ]]; then
+ JAVA_OPTS="${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` "
+ else
+ ARGS="${ARGS}$arg "
+ fi
+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 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 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 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 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 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}
+
diff --git a/Final/java/perftests/etc/scripts/PT-Qpid-14.sh b/Final/java/perftests/etc/scripts/PT-Qpid-14.sh
new file mode 100755
index 0000000000..f0adaa1c30
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/PT-Qpid-14.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Parse arguements taking all - prefixed args as JAVA_OPTS
+for arg in "$@"; do
+ if [[ $arg == -java:* ]]; then
+ JAVA_OPTS="${JAVA_OPTS}-`echo $arg|cut -d ':' -f 2` "
+ else
+ ARGS="${ARGS}$arg "
+ fi
+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 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 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 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 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 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}
diff --git a/Final/java/perftests/etc/scripts/sendAndWaitClient.sh b/Final/java/perftests/etc/scripts/sendAndWaitClient.sh
new file mode 100755
index 0000000000..af4b788658
--- /dev/null
+++ b/Final/java/perftests/etc/scripts/sendAndWaitClient.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+
+$JAVA_HOME/bin/java -Dlog4j.configuration=backup-log4j.xml -cp qpid-perftests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar org.apache.qpid.ping.PingSendOnlyClient messageSize=512
diff --git a/Final/java/perftests/jar-with-dependencies.xml b/Final/java/perftests/jar-with-dependencies.xml
new file mode 100644
index 0000000000..5813b6334b
--- /dev/null
+++ b/Final/java/perftests/jar-with-dependencies.xml
@@ -0,0 +1,82 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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 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/Final/java/perftests/pom.xml b/Final/java/perftests/pom.xml
new file mode 100644
index 0000000000..e62c316d85
--- /dev/null
+++ b/Final/java/perftests/pom.xml
@@ -0,0 +1,714 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT 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-perftests</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid Performance 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</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <log4j.perftests>perftests.log4j</log4j.perftests>
+ </properties>
+
+ <!-- Temporary local maven repo, whilst JUnit Toolkit is still reaching stable version to add to central maven repository. -->
+ <repositories>
+ <repository>
+ <id>junit-toolkit.snapshots</id>
+ <name>JUnit Toolkit SNAPSHOT Repository</name>
+ <url>http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+
+ <!-- Temporary local maven repo, whilst JUnit Toolkit is still reaching stable version to add to central maven repository. -->
+ <pluginRepositories>
+ <pluginRepository>
+ <id>junit-toolkit-plugin.snapshots</id>
+ <name>JUnit Toolkit SNAPSHOT Repository</name>
+ <url>http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <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>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>uk.co.thebadgerset</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ </dependency>
+
+ <!-- Test dependencies. -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+
+ <!-- The JUnit Toolkit maven2 plugin is in the process of being added to the maven repository.
+
+ Configures the toolkit test runner for performance testing. These can be run from within maven, or by using the generated
+ scripts.
+
+ To run from within maven:
+
+ mvn uk.co.thebadgerset: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
+ -o target org.apache.qpid.requestreply.PingPongTestPerf
+
+ To generate the scripts do:
+
+ mvn uk.co.thebadgerset:junit-toolkit-maven-plugin:tkscriptgen
+
+ Then to run the scripts, in the target directory do (after doing assembly:assembly goal):
+
+ ./script_name.sh
+
+ These scripts can find everything in the 'all test dependencies' jar created by the assembly:assembly goal.
+ -->
+ <plugin>
+ <groupId>uk.co.thebadgerset</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><!-- Turn off most logging messages from the junit-toolkit test tool itself. -->
+ <name>badger.level</name>
+ <value>warn</value>
+ </property>
+ <property>
+ <name>amqj.test.logging.level</name>
+ <value>info</value>
+ </property>
+ </systemproperties>
+
+ <commands>
+ <!-- Single pings. These can be scaled up by overriding the parameters when calling the test script. -->
+ <Ping-Once>-n Ping-Once -s[1] -r 1 -t testPingOk -o . org.apache.qpid.ping.PingTestPerf</Ping-Once>
+ <Ping-Once-Async>-n Ping-Once-Async -s[1] -r 1 -t testAsyncPingOk -o . org.apache.qpid.ping.PingAsyncTestPerf</Ping-Once-Async>
+ <Ping-Latency>-n Ping-Latency -s[1000] -d10S -t testPingLatency -o . org.apache.qpid.ping.PingLatencyTestPerf rate=100 batchSize=100</Ping-Latency>
+
+ <!-- More example Tests. These are examples to exercise all the features of the test harness. Can scale up with option overrides. -->
+ <Ping-Tx>-n Ping-Tx -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf transacted=true</Ping-Tx>
+ <Ping-Size>-n Ping-Size -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf messageSize=512</Ping-Size>
+ <Ping-Concurrent>-n Ping-Concurrent -s[100] -c [4] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf</Ping-Concurrent>
+ <Ping-Many-Queues>-n Ping-Many-Queues -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf destinationCount=4</Ping-Many-Queues>
+ <Ping-Duration>-n Ping-Duration -s[100] -d10S -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf</Ping-Duration>
+ <Ping-Rate>-n Ping-Rate -s[100] -d10S -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf rate=500</Ping-Rate>
+ <Ping-PubSub>-n Ping-PubSub -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true</Ping-PubSub>
+ <Ping-Many-Topics>-n Ping-Many-Topics -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf pubsub=true destinationCount=4</Ping-Many-Topics>
+ <Ping-Persistent>-n Ping-Persistent -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true</Ping-Persistent>
+ <Ping-Batch-Logging>-n Ping-Batch-Logging -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf batchSize=10</Ping-Batch-Logging>
+ <Ping-Failover-Before-Send>-n Ping-Failover-Before-Send -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf commitBatchSize=10 failBeforeSend=true</Ping-Failover-Before-Send>
+ <Ping-Failover-After-Send>-n Ping-Failover-After-Send -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf commitBatchSize=10 failAfterSend=true</Ping-Failover-After-Send>
+ <Ping-Failover-Before-Commit>-n Ping-Failover-Before-Commit -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf commitBatchSize=10 failBeforeCommit=true</Ping-Failover-Before-Commit>
+ <Ping-Failover-After-Commit>-n Ping-Failover-After-Commit -s[100] -o . -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf commitBatchSize=10 failAfterCommit=true</Ping-Failover-After-Commit>
+
+ <!--
+ ***** If editing below, please use a non line wrapping mode and keep in columns, makes it easier to check for consistent
+ ***** parameter setting accross all of the tests.
+ -->
+
+ <!--
+ Reliability tests. The longer these tests can be run, the better.
+ Tests 01 and 02 run for a short time. Use these to get an idea of the expected throughput.
+ Tests 03 to 08 test all ack modes, transient/persistent, p2p/pubsub. There are 24 tests in total running for 1 hour each.
+ This can be shortened or lengthened for the desired burn-in test time.
+ -->
+ <TQR-Qpid-01>-n TQR-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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-01>
+ <TQR-Qpid-02>-n TQR-Qpid-02 -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-02>
+ <TTR-Qpid-01>-n TTR-Qpid-01 -d1M -s[100] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=8 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-01>
+ <TTR-Qpid-02>-n TTR-Qpid-02 -d1M -s[100] -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-02>
+ <PQR-Qpid-01>-n PQR-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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-01>
+ <PQR-Qpid-02>-n PQR-Qpid-02 -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-02>
+ <PTR-Qpid-01>-n PTR-Qpid-01 -d1M -s[100] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=8 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-01>
+ <PTR-Qpid-02>-n PTR-Qpid-02 -d1M -s[100] -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-02>
+
+ <TQR-Qpid-03-TX>-n TQR-Qpid-03 -d1H -s[10000] -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-03-TX>
+ <TQR-Qpid-04-AUTOACK>-n TQR-Qpid-04 -d1H -s[10000] -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-04-AUTOACK>
+ <TQR-Qpid-05-CLIENTACK>-n TQR-Qpid-05 -d1H -s[10000] -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=2 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-05-CLIENTACK>
+ <TQR-Qpid-06-DUPSOKACK>-n TQR-Qpid-06 -d1H -s[10000] -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=3 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-06-DUPSOKACK>
+ <TQR-Qpid-07-NOACK>-n TQR-Qpid-07 -d1H -s[10000] -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-07-NOACK>
+ <TQR-Qpid-08-PREACK>-n TQR-Qpid-08 -d1H -s[10000] -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=258 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TQR-Qpid-08-PREACK>
+
+ <TTR-Qpid-03-TX>-n TTR-Qpid-03 -d1H -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=true consAckMode=0 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-03-TX>
+ <TTR-Qpid-04-AUTOACK>-n TTR-Qpid-04 -d1H -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-04-AUTOACK>
+ <TTR-Qpid-05-CLIENTACK>-n TTR-Qpid-05 -d1H -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=2 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-05-CLIENTACK>
+ <TTR-Qpid-06-DUPSOKACK>-n TTR-Qpid-06 -d1H -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=3 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-06-DUPSOKACK>
+ <TTR-Qpid-07-NOACK>-n TTR-Qpid-07 -d1H -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-07-NOACK>
+ <TTR-Qpid-08-PREACK>-n TTR-Qpid-08 -d1H -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=258 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </TTR-Qpid-08-PREACK>
+
+ <PQR-Qpid-03-TX>-n PQR-Qpid-03 -d1H -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-03-TX>
+ <PQR-Qpid-04-AUTOACK>-n PQR-Qpid-04 -d1H -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-04-AUTOACK>
+ <PQR-Qpid-05-CLIENTACK>-n PQR-Qpid-05 -d1H -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=2 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-05-CLIENTACK>
+ <PQR-Qpid-06-DUPSOKACK>-n PQR-Qpid-06 -d1H -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=3 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-06-DUPSOKACK>
+ <PQR-Qpid-07-NOACK>-n PQR-Qpid-07 -d1H -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=257 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-07-NOACK>
+ <PQR-Qpid-08-PREACK>-n PQR-Qpid-08 -d1H -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=258 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PQR-Qpid-08-PREACK>
+
+ <PTR-Qpid-03-TX>-n PTR-Qpid-03 -d1H -s[100] -c[2] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=8 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-03-TX>
+ <PTR-Qpid-04-AUTOACK>-n PTR-Qpid-04 -d1H -s[100] -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=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-04-AUTOACK>
+ <PTR-Qpid-05-CLIENTACK>-n PTR-Qpid-05 -d1H -s[100] -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=2 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-05-CLIENTACK>
+ <PTR-Qpid-06-DUPSOKACK>-n PTR-Qpid-06 -d1H -s[100] -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=3 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-06-DUPSOKACK>
+ <PTR-Qpid-07-NOACK>-n PTR-Qpid-07 -d1H -s[100] -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=257 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-07-NOACK>
+ <PTR-Qpid-08-PREACK>-n PTR-Qpid-08 -d1H -s[100] -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=258 commitBatchSize=10 batchSize=100000 messageSize=256 destinationCount=1 rate=0 maxPending=500000 </PTR-Qpid-08-PREACK>
+
+ <!-- Performance Tests. -->
+
+ <!-- Transient, P2P Tests -->
+ <TQCT-Qpid-01>-n TQCT-Qpid-01 -d1M -s[1000] -c[1,30],samples=30 -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=2000 maxPending=2000000 </TQCT-Qpid-01>
+ <TQCT-Qpid-02>-n TQCT-Qpid-02 -d1M -s[1000] -c[1,30],samples=30 -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=2000 maxPending=2000000 </TQCT-Qpid-02>
+ <TQCL-Qpid-01>-n TQCL-Qpid-01 -d1M -s[1000] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=2000 maxPending=2000000 </TQCL-Qpid-01>
+ <TQCL-Qpid-02>-n TQCL-Qpid-02 -d1M -s[1000] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=2000 maxPending=2000000 </TQCL-Qpid-02>
+
+ <!-- <TQC-Qpid-05>-n TQC-Qpid-05 -d10M -s[10] -c[100] -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=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </TQC-Qpid-05> -->
+ <!-- <TQC-Qpid-06>-n TQC-Qpid-06 -d10M -s[10] -c[100] -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=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </TQC-Qpid-06> -->
+
+ <TQM-Qpid-01-512b>-n TQM-Qpid-01-512b -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-01-512b>
+ <TQM-Qpid-02-512b>-n TQM-Qpid-02-512b -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-02-512b>
+ <TQM-Qpid-01-1K>-n TQM-Qpid-01-1K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-01-1K>
+ <TQM-Qpid-02-1K>-n TQM-Qpid-02-1K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-02-1K>
+ <TQM-Qpid-01-5K>-n TQM-Qpid-01-5K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-01-5K>
+ <TQM-Qpid-02-5K>-n TQM-Qpid-02-5K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-02-5K>
+ <TQM-Qpid-01-10K>-n TQM-Qpid-01-10K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-01-10K>
+ <TQM-Qpid-02-10K>-n TQM-Qpid-02-10K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TQM-Qpid-02-10K>
+ <TQM-Qpid-01-50K>-n TQM-Qpid-01-50K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-01-50K>
+ <TQM-Qpid-02-50K>-n TQM-Qpid-02-50K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-02-50K>
+ <TQM-Qpid-01-100K>-n TQM-Qpid-01-100K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-01-100K>
+ <TQM-Qpid-02-100K>-n TQM-Qpid-02-100K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-02-100K>
+ <TQM-Qpid-01-500K>-n TQM-Qpid-01-500K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-01-500K>
+ <TQM-Qpid-02-500K>-n TQM-Qpid-02-500K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-02-500K>
+ <TQM-Qpid-01-1M>-n TQM-Qpid-01-1M -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=1048576 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-01-1M>
+ <TQM-Qpid-02-1M>-n TQM-Qpid-02-1M -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=1048576 destinationCount=1 rate=0 maxPending=100000000</TQM-Qpid-02-1M>
+
+ <!-- Transient, Pub/Sub Tests -->
+ <TTCT-Qpid-01>-n TTCT-Qpid-01 -d1M -s[10] -c[1,30],samples=30 -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=40 maxPending=2000000 </TTCT-Qpid-01>
+ <TTCT-Qpid-02>-n TTCT-Qpid-02 -d1M -s[10] -c[1,30],samples=30 -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=40 maxPending=2000000 </TTCT-Qpid-02>
+ <TTCL-Qpid-01>-n TTCL-Qpid-01 -d1M -s[10] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=40 maxPending=2000000 </TTCL-Qpid-01>
+ <TTCL-Qpid-02>-n TTCL-Qpid-02 -d1M -s[10] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=40 maxPending=2000000 </TTCL-Qpid-02>
+
+ <!-- <TTC-Qpid-05>-n TTC-Qpid-05 -d10M -s[10] -c[100] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </TTC-Qpid-05> -->
+ <!-- <TTC-Qpid-06>-n TTC-Qpid-06 -d10M -s[10] -c[100] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </TTC-Qpid-06> -->
+
+ <TTM-Qpid-01-512b>-n TTM-Qpid-01-512b -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-01-512b>
+ <TTM-Qpid-02-512b>-n TTM-Qpid-02-512b -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-02-512b>
+ <TTM-Qpid-01-1K>-n TTM-Qpid-01-1K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-01-1K>
+ <TTM-Qpid-02-1K>-n TTM-Qpid-02-1K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-02-1K>
+ <TTM-Qpid-01-5K>-n TTM-Qpid-01-5K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-01-5K>
+ <TTM-Qpid-02-5K>-n TTM-Qpid-02-5K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-02-5K>
+ <TTM-Qpid-01-10K>-n TTM-Qpid-01-10K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-01-10K>
+ <TTM-Qpid-02-10K>-n TTM-Qpid-02-10K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TTM-Qpid-02-10K>
+ <TTM-Qpid-01-50K>-n TTM-Qpid-01-50K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-01-50K>
+ <TTM-Qpid-02-50K>-n TTM-Qpid-02-50K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-02-50K>
+ <TTM-Qpid-01-100K>-n TTM-Qpid-01-100K -d10M -s[40] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-01-100K>
+ <TTM-Qpid-02-100K>-n TTM-Qpid-02-100K -d10M -s[40] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-02-100K>
+ <TTM-Qpid-01-500K>-n TTM-Qpid-01-500K -d10M -s[8] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-01-500K>
+ <TTM-Qpid-02-500K>-n TTM-Qpid-02-500K -d10M -s[8] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-02-500K>
+ <TTM-Qpid-01-1M>-n TTM-Qpid-01-1M -d10M -s[4] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=1048576 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-01-1M>
+ <TTM-Qpid-02-1M>-n TTM-Qpid-02-1M -d10M -s[4] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=1048476 destinationCount=1 rate=0 maxPending=20000000</TTM-Qpid-02-1M>
+
+ <!-- Persistent, P2P Tests -->
+ <PQCT-Qpid-01>-n PQCT-Qpid-01 -d1M -s[1000] -c[1,30],samples=30 -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=600 maxPending=2000000 </PQCT-Qpid-01>
+ <PQCT-Qpid-02>-n PQCT-Qpid-02 -d1M -s[1000] -c[1,30],samples=30 -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=10 batchSize=1000 messageSize=256 destinationCount=1 rate=100 maxPending=2000000 </PQCT-Qpid-02>
+ <PQCL-Qpid-01>-n PQCL-Qpid-01 -d1M -s[1000] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=600 maxPending=2000000 </PQCL-Qpid-01>
+ <PQCL-Qpid-02>-n PQCL-Qpid-02 -d1M -s[1000] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=100 maxPending=2000000 </PQCL-Qpid-02>
+
+ <!-- <PQC-Qpid-05>-n PQC-Qpid-05 -d10M -s[10] -c[100] -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=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </PQC-Qpid-05> -->
+ <!-- <PQC-Qpid-06>-n PQC-Qpid-06 -d10M -s[10] -c[100] -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=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </PQC-Qpid-06> -->
+
+ <PQM-Qpid-01-512b>-n PQM-Qpid-01-512b -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-01-512b>
+ <PQM-Qpid-02-512b>-n PQM-Qpid-02-512b -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-02-512b>
+ <PQM-Qpid-01-1K>-n PQM-Qpid-01-1K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-01-1K>
+ <PQM-Qpid-02-1K>-n PQM-Qpid-02-1K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-02-1K>
+ <PQM-Qpid-01-5K>-n PQM-Qpid-01-5K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-01-5K>
+ <PQM-Qpid-02-5K>-n PQM-Qpid-02-5K -d10M -s[1000] -c[8] -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=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-02-5K>
+ <PQM-Qpid-01-10K>-n PQM-Qpid-01-10K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-01-10K>
+ <PQM-Qpid-02-10K>-n PQM-Qpid-02-10K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PQM-Qpid-02-10K>
+ <PQM-Qpid-01-50K>-n PQM-Qpid-01-50K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-01-50K>
+ <PQM-Qpid-02-50K>-n PQM-Qpid-02-50K -d10M -s[100] -c[8] -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=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-02-50K>
+ <PQM-Qpid-01-100K>-n PQM-Qpid-01-100K -d10M -s[40] -c[8] -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=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-01-100K>
+ <PQM-Qpid-02-100K>-n PQM-Qpid-02-100K -d10M -s[40] -c[8] -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=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-02-100K>
+ <PQM-Qpid-01-500K>-n PQM-Qpid-01-500K -d10M -s[8] -c[8] -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=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-01-500K>
+ <PQM-Qpid-02-500K>-n PQM-Qpid-02-500K -d10M -s[8] -c[8] -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=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-02-500K>
+ <PQM-Qpid-01-1M>-n PQM-Qpid-01-1M -d10M -s[4] -c[8] -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=10 batchSize=1000 messageSize=1048576 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-01-1M>
+ <PQM-Qpid-02-1M>-n PQM-Qpid-02-1M -d10M -s[4] -c[8] -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=10 batchSize=1000 messageSize=1048576 destinationCount=1 rate=0 maxPending=20000000</PQM-Qpid-02-1M>
+
+ <!-- Persistent, Pub/Sub Tests -->
+ <PTCT-Qpid-01>-n PTCT-Qpid-01 -d1M -s[1] -c[1,30],samples=30 -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=1 maxPending=2000000 </PTCT-Qpid-01>
+ <PTCT-Qpid-02>-n PTCT-Qpid-02 -d1M -s[1] -c[1,30],samples=30 -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=1 maxPending=2000000 </PTCT-Qpid-02>
+ <PTCL-Qpid-01>-n PTCL-Qpid-01 -d1M -s[1] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=1 maxPending=2000000 </PTCL-Qpid-01>
+ <PTCL-Qpid-02>-n PTCL-Qpid-02 -d1M -s[1] -c[1,30],samples=30 -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=1 rate=1 maxPending=2000000 </PTCL-Qpid-02>
+
+ <!-- <PTC-Qpid-05>-n PTC-Qpid-05 -d10M -s[10] -c[100] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </PTC-Qpid-05> -->
+ <!-- <PTC-Qpid-06>-n PTC-Qpid-06 -d10M -s[10] -c[100] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=256 destinationCount=10 rate=0 maxPending=100000 </PTC-Qpid-06> -->
+
+ <PTM-Qpid-01-512b>-n PTM-Qpid-01-512b -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-01-512b>
+ <PTM-Qpid-02-512b>-n PTM-Qpid-02-512b -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-02-512b>
+ <PTM-Qpid-01-1K>-n PTM-Qpid-01-1K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-01-1K>
+ <PTM-Qpid-02-1K>-n PTM-Qpid-02-1K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-02-1K>
+ <PTM-Qpid-01-5K>-n PTM-Qpid-01-5K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-01-5K>
+ <PTM-Qpid-02-5K>-n PTM-Qpid-02-5K -d10M -s[1000] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-02-5K>
+ <PTM-Qpid-01-10K>-n PTM-Qpid-01-10K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-01-10K>
+ <PTM-Qpid-02-10K>-n PTM-Qpid-02-10K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PTM-Qpid-02-10K>
+ <PTM-Qpid-01-50K>-n PTM-Qpid-01-50K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-01-50K>
+ <PTM-Qpid-02-50K>-n PTM-Qpid-02-50K -d10M -s[100] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=51200 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-02-50K>
+ <PTM-Qpid-01-100K>-n PTM-Qpid-01-100K -d10M -s[40] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-01-100K>
+ <PTM-Qpid-02-100K>-n PTM-Qpid-02-100K -d10M -s[40] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=102400 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-02-100K>
+ <PTM-Qpid-01-500K>-n PTM-Qpid-01-500K -d10M -s[8] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-01-500K>
+ <PTM-Qpid-02-500K>-n PTM-Qpid-02-500K -d10M -s[8] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=512000 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-02-500K>
+ <PTM-Qpid-01-1M>-n PTM-Qpid-01-1M -d10M -s[4] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=true consTransacted=true consAckMode=0 commitBatchSize=10 batchSize=1000 messageSize=1048576 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-01-1M>
+ <PTM-Qpid-02-1M>-n PTM-Qpid-02-1M -d10M -s[4] -c[8] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=10 batchSize=1000 messageSize=1048476 destinationCount=1 rate=0 maxPending=20000000</PTM-Qpid-02-1M>
+
+ <!-- Benchmarking tests. -->
+ <!-- Throughput. -->
+ <TQBT-TX-Qpid-01>-n TQBT-TX-Qpid-01 -d10M -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 -d10M -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 -d10M -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 -d10M -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=true consAckMode=0 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 -d10M -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 -d10M -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>
+
+ <PQBT-TX-Qpid-01>-n PQBT-TX-Qpid-01 -d10M -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 -d10M -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 -d10M -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=true consAckMode=0 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 -d10M -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>
+
+ <!-- Job Queueing. 1:10 -->
+ <TQBL-AA-Qpid-02-01>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=1000 maxPending=2000000 </TQBL-AA-Qpid-02-01>
+ <TQBL-AA-Qpid-02-02>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=2000 maxPending=2000000 </TQBL-AA-Qpid-02-02>
+ <TQBL-AA-Qpid-02-03>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=3000 maxPending=2000000 </TQBL-AA-Qpid-02-03>
+ <TQBL-AA-Qpid-02-04>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=4000 maxPending=2000000 </TQBL-AA-Qpid-02-04>
+ <TQBL-AA-Qpid-02-05>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=5000 maxPending=2000000 </TQBL-AA-Qpid-02-05>
+ <TQBL-AA-Qpid-02-06>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=6000 maxPending=2000000 </TQBL-AA-Qpid-02-06>
+ <TQBL-AA-Qpid-02-07>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=7000 maxPending=2000000 </TQBL-AA-Qpid-02-07>
+ <TQBL-AA-Qpid-02-08>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=8000 maxPending=2000000 </TQBL-AA-Qpid-02-08>
+ <TQBL-AA-Qpid-02-09>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=9000 maxPending=2000000 </TQBL-AA-Qpid-02-09>
+ <TQBL-AA-Qpid-02-10>-n TQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=10000 maxPending=2000000 </TQBL-AA-Qpid-02-10>
+
+ <PQBL-AA-Qpid-02-01>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=100 maxPending=2000000 </PQBL-AA-Qpid-02-01>
+ <PQBL-AA-Qpid-02-02>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=200 maxPending=2000000 </PQBL-AA-Qpid-02-02>
+ <PQBL-AA-Qpid-02-03>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=300 maxPending=2000000 </PQBL-AA-Qpid-02-03>
+ <PQBL-AA-Qpid-02-04>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=400 maxPending=2000000 </PQBL-AA-Qpid-02-04>
+ <PQBL-AA-Qpid-02-05>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=500 maxPending=2000000 </PQBL-AA-Qpid-02-05>
+ <PQBL-AA-Qpid-02-06>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=600 maxPending=2000000 </PQBL-AA-Qpid-02-06>
+ <PQBL-AA-Qpid-02-07>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=700 maxPending=2000000 </PQBL-AA-Qpid-02-07>
+ <PQBL-AA-Qpid-02-08>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=800 maxPending=2000000 </PQBL-AA-Qpid-02-08>
+ <PQBL-AA-Qpid-02-09>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=900 maxPending=2000000 </PQBL-AA-Qpid-02-09>
+ <PQBL-AA-Qpid-02-10>-n PQBL-AA-Qpid-02 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=10 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=5120 destinationCount=1 rate=1000 maxPending=2000000 </PQBL-AA-Qpid-02-10>
+
+ <!-- Broadcast of small sized time critical messages. Lowish rates. Transient/Persistent. 1:100, auto ack. -->
+ <TTBL-AA-Qpid-03-01>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=200 maxPending=2000000 </TTBL-AA-Qpid-03-01>
+ <TTBL-AA-Qpid-03-02>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=400 maxPending=2000000 </TTBL-AA-Qpid-03-02>
+ <TTBL-AA-Qpid-03-03>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=600 maxPending=2000000 </TTBL-AA-Qpid-03-03>
+ <TTBL-AA-Qpid-03-04>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=800 maxPending=2000000 </TTBL-AA-Qpid-03-04>
+ <TTBL-AA-Qpid-03-05>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=1000 maxPending=2000000 </TTBL-AA-Qpid-03-05>
+ <TTBL-AA-Qpid-03-06>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=1200 maxPending=2000000 </TTBL-AA-Qpid-03-06>
+ <TTBL-AA-Qpid-03-07>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=1400 maxPending=2000000 </TTBL-AA-Qpid-03-07>
+ <TTBL-AA-Qpid-03-08>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=1600 maxPending=2000000 </TTBL-AA-Qpid-03-08>
+ <TTBL-AA-Qpid-03-09>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=1800 maxPending=2000000 </TTBL-AA-Qpid-03-09>
+ <TTBL-AA-Qpid-03-10>-n TTBL-AA-Qpid-03 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=2000 maxPending=2000000 </TTBL-AA-Qpid-03-10>
+
+ <PTBL-AA-Qpid-03-01>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=10 maxPending=2000000 </PTBL-AA-Qpid-03-01>
+ <PTBL-AA-Qpid-03-02>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=20 maxPending=2000000 </PTBL-AA-Qpid-03-02>
+ <PTBL-AA-Qpid-03-03>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=30 maxPending=2000000 </PTBL-AA-Qpid-03-03>
+ <PTBL-AA-Qpid-03-04>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=40 maxPending=2000000 </PTBL-AA-Qpid-03-04>
+ <PTBL-AA-Qpid-03-05>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=50 maxPending=2000000 </PTBL-AA-Qpid-03-05>
+ <PTBL-AA-Qpid-03-06>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=60 maxPending=2000000 </PTBL-AA-Qpid-03-06>
+ <PTBL-AA-Qpid-03-07>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=70 maxPending=2000000 </PTBL-AA-Qpid-03-07>
+ <PTBL-AA-Qpid-03-08>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=80 maxPending=2000000 </PTBL-AA-Qpid-03-08>
+ <PTBL-AA-Qpid-03-09>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=90 maxPending=2000000 </PTBL-AA-Qpid-03-09>
+ <PTBL-AA-Qpid-03-10>-n PTBL-AA-Qpid-03 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=256 destinationCount=1 rate=100 maxPending=2000000 </PTBL-AA-Qpid-03-10>
+
+ <!-- Broadcast of medium sized time critical messages. Lowish rates. Transient/Persistent. 1:100, auto ack. -->
+ <TTBL-AA-Qpid-04-01>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=100 maxPending=2000000 </TTBL-AA-Qpid-04-01>
+ <TTBL-AA-Qpid-04-02>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=200 maxPending=2000000 </TTBL-AA-Qpid-04-02>
+ <TTBL-AA-Qpid-04-03>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=300 maxPending=2000000 </TTBL-AA-Qpid-04-03>
+ <TTBL-AA-Qpid-04-04>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=400 maxPending=2000000 </TTBL-AA-Qpid-04-04>
+ <TTBL-AA-Qpid-04-05>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=500 maxPending=2000000 </TTBL-AA-Qpid-04-05>
+ <TTBL-AA-Qpid-04-06>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=600 maxPending=2000000 </TTBL-AA-Qpid-04-06>
+ <TTBL-AA-Qpid-04-07>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=700 maxPending=2000000 </TTBL-AA-Qpid-04-07>
+ <TTBL-AA-Qpid-04-08>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=800 maxPending=2000000 </TTBL-AA-Qpid-04-08>
+ <TTBL-AA-Qpid-04-09>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=900 maxPending=2000000 </TTBL-AA-Qpid-04-09>
+ <TTBL-AA-Qpid-04-10>-n TTBL-AA-Qpid-04 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=1000 maxPending=2000000 </TTBL-AA-Qpid-04-10>
+
+ <PTBL-AA-Qpid-04-01>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=10 maxPending=2000000 </PTBL-AA-Qpid-04-01>
+ <PTBL-AA-Qpid-04-02>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=20 maxPending=2000000 </PTBL-AA-Qpid-04-02>
+ <PTBL-AA-Qpid-04-03>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=30 maxPending=2000000 </PTBL-AA-Qpid-04-03>
+ <PTBL-AA-Qpid-04-04>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=40 maxPending=2000000 </PTBL-AA-Qpid-04-04>
+ <PTBL-AA-Qpid-04-05>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=50 maxPending=2000000 </PTBL-AA-Qpid-04-05>
+ <PTBL-AA-Qpid-04-06>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=60 maxPending=2000000 </PTBL-AA-Qpid-04-06>
+ <PTBL-AA-Qpid-04-07>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=70 maxPending=2000000 </PTBL-AA-Qpid-04-07>
+ <PTBL-AA-Qpid-04-08>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=80 maxPending=2000000 </PTBL-AA-Qpid-04-08>
+ <PTBL-AA-Qpid-04-09>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=90 maxPending=2000000 </PTBL-AA-Qpid-04-09>
+ <PTBL-AA-Qpid-04-10>-n PTBL-AA-Qpid-04 -d1M -s[100] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=101 messageSize=5120 destinationCount=1 rate=100 maxPending=2000000 </PTBL-AA-Qpid-04-10>
+
+ <!-- Low-latency broadcast of time sensitive events. Transient, no_ack. -->
+ <TTBL-NA-Qpid-05-01>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=400 maxPending=2000000 </TTBL-NA-Qpid-05-01>
+ <TTBL-NA-Qpid-05-02>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=800 maxPending=2000000 </TTBL-NA-Qpid-05-02>
+ <TTBL-NA-Qpid-05-03>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=1200 maxPending=2000000 </TTBL-NA-Qpid-05-03>
+ <TTBL-NA-Qpid-05-04>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=1600 maxPending=2000000 </TTBL-NA-Qpid-05-04>
+ <TTBL-NA-Qpid-05-05>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=2000 maxPending=2000000 </TTBL-NA-Qpid-05-05>
+ <TTBL-NA-Qpid-05-06>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=2400 maxPending=2000000 </TTBL-NA-Qpid-05-06>
+ <TTBL-NA-Qpid-05-07>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=2800 maxPending=2000000 </TTBL-NA-Qpid-05-07>
+ <TTBL-NA-Qpid-05-08>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=3200 maxPending=2000000 </TTBL-NA-Qpid-05-08>
+ <TTBL-NA-Qpid-05-09>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=3600 maxPending=2000000 </TTBL-NA-Qpid-05-09>
+ <TTBL-NA-Qpid-05-10>-n TTBL-NA-Qpid-05 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=256 destinationCount=1 rate=4000 maxPending=2000000 </TTBL-NA-Qpid-05-10>
+
+ <!-- Low-latency broadcast of medium sized time sensitive events. Transient, no_ack. -->
+ <TTBL-NA-Qpid-06-01>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=100 maxPending=2000000 </TTBL-NA-Qpid-06-01>
+ <TTBL-NA-Qpid-06-02>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=200 maxPending=2000000 </TTBL-NA-Qpid-06-02>
+ <TTBL-NA-Qpid-06-03>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=300 maxPending=2000000 </TTBL-NA-Qpid-06-03>
+ <TTBL-NA-Qpid-06-04>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=400 maxPending=2000000 </TTBL-NA-Qpid-06-04>
+ <TTBL-NA-Qpid-06-05>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=500 maxPending=2000000 </TTBL-NA-Qpid-06-05>
+ <TTBL-NA-Qpid-06-06>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=600 maxPending=2000000 </TTBL-NA-Qpid-06-06>
+ <TTBL-NA-Qpid-06-07>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=700 maxPending=2000000 </TTBL-NA-Qpid-06-07>
+ <TTBL-NA-Qpid-06-08>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=800 maxPending=2000000 </TTBL-NA-Qpid-06-08>
+ <TTBL-NA-Qpid-06-09>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=900 maxPending=2000000 </TTBL-NA-Qpid-06-09>
+ <TTBL-NA-Qpid-06-10>-n TTBL-NA-Qpid-06 -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=true uniqueDests=false numConsumers=50 transacted=false consTransacted=false consAckMode=257 commitBatchSize=1 batchSize=1001 messageSize=5120 destinationCount=1 rate=1000 maxPending=2000000 </TTBL-NA-Qpid-06-10>
+
+ <!-- Benchmarking Tests for P2P. -->
+ <!--
+ Bench mark 1. P2P messaging from 1:1 to 1:32, shared queue, load balancing scenario.
+ Non-tx, and 1 msg per tx.
+ Persistent and non-persistent.
+ No rate limiting.
+ Message size 256 bytes.
+ -->
+ <!--
+ <TQBT-Qpid-01-1C> -n TQBT-Qpid-01-1C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-01-1C>
+ <TQBT-Qpid-01-2C> -n TQBT-Qpid-01-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=2 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-01-2C>
+ <TQBT-Qpid-01-4C> -n TQBT-Qpid-01-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=4 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-01-4C>
+ <TQBT-Qpid-01-8C> -n TQBT-Qpid-01-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=8 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-01-8C>
+ <TQBT-Qpid-01-16C>-n TQBT-Qpid-01-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=16 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-01-16C>
+ <TQBT-Qpid-01-32C>-n TQBT-Qpid-01-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=32 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-01-32C>
+
+ <TQBT-Qpid-02-1C> -n TQBT-Qpid-02-1C -d1M -s[1000] -c[1] -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-Qpid-02-1C>
+ <TQBT-Qpid-02-2C> -n TQBT-Qpid-02-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=2 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-02-2C>
+ <TQBT-Qpid-02-4C> -n TQBT-Qpid-02-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=4 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-02-4C>
+ <TQBT-Qpid-02-8C> -n TQBT-Qpid-02-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=8 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-02-8C>
+ <TQBT-Qpid-02-16C>-n TQBT-Qpid-02-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=16 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-02-16C>
+ <TQBT-Qpid-02-32C>-n TQBT-Qpid-02-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=32 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-02-32C>
+
+ <PQBT-Qpid-01-1C> -n PQBT-Qpid-01-1C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-01-1C>
+ <PQBT-Qpid-01-2C> -n PQBT-Qpid-01-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=2 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-01-2C>
+ <PQBT-Qpid-01-4C> -n PQBT-Qpid-01-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=4 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-01-4C>
+ <PQBT-Qpid-01-8C> -n PQBT-Qpid-01-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=8 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-01-8C>
+ <PQBT-Qpid-01-16C>-n PQBT-Qpid-01-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=16 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-01-16C>
+ <PQBT-Qpid-01-32C>-n PQBT-Qpid-01-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=32 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-01-32C>
+
+ <PQBT-Qpid-02-1C> -n PQBT-Qpid-02-1C -d1M -s[1000] -c[1] -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-Qpid-02-1C>
+ <PQBT-Qpid-02-2C> -n PQBT-Qpid-02-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=2 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-02-2C>
+ <PQBT-Qpid-02-4C> -n PQBT-Qpid-02-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=4 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-02-4C>
+ <PQBT-Qpid-02-8C> -n PQBT-Qpid-02-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=8 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-02-8C>
+ <PQBT-Qpid-02-16C>-n PQBT-Qpid-02-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=16 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-02-16C>
+ <PQBT-Qpid-02-32C>-n PQBT-Qpid-02-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=32 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-02-32C>
+
+ <TQBL-Qpid-01-1C> -n TQBL-Qpid-01-1C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-01-1C>
+ <TQBL-Qpid-01-2C> -n TQBL-Qpid-01-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=2 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-01-2C>
+ <TQBL-Qpid-01-4C> -n TQBL-Qpid-01-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=4 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-01-4C>
+ <TQBL-Qpid-01-8C> -n TQBL-Qpid-01-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=8 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-01-8C>
+ <TQBL-Qpid-01-16C>-n TQBL-Qpid-01-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=16 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-01-16C>
+ <TQBL-Qpid-01-32C>-n TQBL-Qpid-01-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=32 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-01-32C>
+
+ <TQBL-Qpid-02-1C> -n TQBL-Qpid-02-1C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf 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 </TQBL-Qpid-02-1C>
+ <TQBL-Qpid-02-2C> -n TQBL-Qpid-02-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=2 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-02-2C>
+ <TQBL-Qpid-02-4C> -n TQBL-Qpid-02-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=4 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-02-4C>
+ <TQBL-Qpid-02-8C> -n TQBL-Qpid-02-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=8 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-02-8C>
+ <TQBL-Qpid-02-16C>-n TQBL-Qpid-02-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=16 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-02-16C>
+ <TQBL-Qpid-02-32C>-n TQBL-Qpid-02-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=32 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-02-32C>
+
+ <PQBL-Qpid-01-1C> -n PQBL-Qpid-01-1C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-01-1C>
+ <PQBL-Qpid-01-2C> -n PQBL-Qpid-01-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=2 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-01-2C>
+ <PQBL-Qpid-01-4C> -n PQBL-Qpid-01-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=4 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-01-4C>
+ <PQBL-Qpid-01-8C> -n PQBL-Qpid-01-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=8 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-01-8C>
+ <PQBL-Qpid-01-16C>-n PQBL-Qpid-01-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=16 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-01-16C>
+ <PQBL-Qpid-01-32C>-n PQBL-Qpid-01-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=32 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-01-32C>
+
+ <PQBL-Qpid-02-1C> -n PQBL-Qpid-02-1C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf 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 </PQBL-Qpid-02-1C>
+ <PQBL-Qpid-02-2C> -n PQBL-Qpid-02-2C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=2 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-02-2C>
+ <PQBL-Qpid-02-4C> -n PQBL-Qpid-02-4C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=4 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-02-4C>
+ <PQBL-Qpid-02-8C> -n PQBL-Qpid-02-8C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=8 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-02-8C>
+ <PQBL-Qpid-02-16C>-n PQBL-Qpid-02-16C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=16 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-02-16C>
+ <PQBL-Qpid-02-32C>-n PQBL-Qpid-02-32C -d1M -s[1000] -c[1] -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=32 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=1000 messageSize=256 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-02-32C>
+ -->
+
+ <!--
+ Bench mark 2. P2P messaging 1:1, scaled up from 1 to 32 times. Queues not shared, just one to one throughput.
+ Non-tx, and 1 msg per tx.
+ Persistent and non-persistent.
+ No rate limiting.
+ Message size from 128 bytes to 1 Meg.
+ -->
+ <!--
+ <TQBT-Qpid-03-128b>-n TQBT-Qpid-03-128b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-128b>
+ <TQBT-Qpid-03-256b>-n TQBT-Qpid-03-256b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-256b>
+ <TQBT-Qpid-03-512b>-n TQBT-Qpid-03-512b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-512b>
+ <TQBT-Qpid-03-1K>-n TQBT-Qpid-03-1K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-1K>
+ <TQBT-Qpid-03-5K>-n TQBT-Qpid-03-5K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-5K>
+ <TQBT-Qpid-03-10K>-n TQBT-Qpid-03-10K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-10K>
+ <TQBT-Qpid-03-50K>-n TQBT-Qpid-03-50K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-50K>
+ <TQBT-Qpid-03-100K>-n TQBT-Qpid-03-100K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-100K>
+ <TQBT-Qpid-03-500K>-n TQBT-Qpid-03-500K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-500K>
+ <TQBT-Qpid-03-1M>-n TQBT-Qpid-03-1M -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-03-1M>
+
+ <TQBT-Qpid-04-128b>-n TQBT-Qpid-04-128b -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-128b>
+ <TQBT-Qpid-04-256b>-n TQBT-Qpid-04-256b -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-256b>
+ <TQBT-Qpid-04-512b>-n TQBT-Qpid-04-512b -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-512b>
+ <TQBT-Qpid-04-1K>-n TQBT-Qpid-04-1K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-1K>
+ <TQBT-Qpid-04-5K>-n TQBT-Qpid-04-5K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-5K>
+ <TQBT-Qpid-04-10K>-n TQBT-Qpid-04-10K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-10K>
+ <TQBT-Qpid-04-50K>-n TQBT-Qpid-04-50K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-50K>
+ <TQBT-Qpid-04-100K>-n TQBT-Qpid-04-100K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-100K>
+ <TQBT-Qpid-04-500K>-n TQBT-Qpid-04-500K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-500K>
+ <TQBT-Qpid-04-1M>-n TQBT-Qpid-04-1M -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </TQBT-Qpid-04-1M>
+
+ <PQBT-Qpid-03-128b>-n PQBT-Qpid-03-128b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-128b>
+ <PQBT-Qpid-03-256b>-n PQBT-Qpid-03-256b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-256b>
+ <PQBT-Qpid-03-512b>-n PQBT-Qpid-03-512b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-512b>
+ <PQBT-Qpid-03-1K>-n PQBT-Qpid-03-1K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-1K>
+ <PQBT-Qpid-03-5K>-n PQBT-Qpid-03-5K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-5K>
+ <PQBT-Qpid-03-10K>-n PQBT-Qpid-03-10K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-10K>
+ <PQBT-Qpid-03-50K>-n PQBT-Qpid-03-50K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-50K>
+ <PQBT-Qpid-03-100K>-n PQBT-Qpid-03-100K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-100K>
+ <PQBT-Qpid-03-500K>-n PQBT-Qpid-03-500K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-500K>
+ <PQBT-Qpid-03-1M>-n PQBT-Qpid-03-1M -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-03-1M>
+
+ <PQBT-Qpid-04-128b>-n PQBT-Qpid-04-128b -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-128b>
+ <PQBT-Qpid-04-256b>-n PQBT-Qpid-04-256b -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-256b>
+ <PQBT-Qpid-04-512b>-n PQBT-Qpid-04-512b -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-512b>
+ <PQBT-Qpid-04-1K>-n PQBT-Qpid-04-1K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-1K>
+ <PQBT-Qpid-04-5K>-n PQBT-Qpid-04-5K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-5K>
+ <PQBT-Qpid-04-10K>-n PQBT-Qpid-04-10K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-10K>
+ <PQBT-Qpid-04-50K>-n PQBT-Qpid-04-50K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-50K>
+ <PQBT-Qpid-04-100K>-n PQBT-Qpid-04-100K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-100K>
+ <PQBT-Qpid-04-500K>-n PQBT-Qpid-04-500K -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-500K>
+ <PQBT-Qpid-04-1M>-n PQBT-Qpid-04-1M -d1M -s[100] -c[1,32],samples=6,exp -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=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </PQBT-Qpid-04-1M>
+
+ <TQBL-Qpid-03-128b>-n TQBL-Qpid-03-128b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-128b>
+ <TQBL-Qpid-03-256b>-n TQBL-Qpid-03-256b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-256b>
+ <TQBL-Qpid-03-512b>-n TQBL-Qpid-03-512b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-512b>
+ <TQBL-Qpid-03-1K>-n TQBL-Qpid-03-1K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-1K>
+ <TQBL-Qpid-03-5K>-n TQBL-Qpid-03-5K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-5K>
+ <TQBL-Qpid-03-10K>-n TQBL-Qpid-03-10K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-10K>
+ <TQBL-Qpid-03-50K>-n TQBL-Qpid-03-50K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-50K>
+ <TQBL-Qpid-03-100K>-n TQBL-Qpid-03-100K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-100K>
+ <TQBL-Qpid-03-500K>-n TQBL-Qpid-03-500K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-500K>
+ <TQBL-Qpid-03-1M>-n TQBL-Qpid-03-1M -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-03-1M>
+
+ <TQBL-Qpid-04-128b>-n TQBL-Qpid-04-128b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-128b>
+ <TQBL-Qpid-04-256b>-n TQBL-Qpid-04-256b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-256b>
+ <TQBL-Qpid-04-512b>-n TQBL-Qpid-04-512b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-512b>
+ <TQBL-Qpid-04-1K>-n TQBL-Qpid-04-1K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-1K>
+ <TQBL-Qpid-04-5K>-n TQBL-Qpid-04-5K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-5K>
+ <TQBL-Qpid-04-10K>-n TQBL-Qpid-04-10K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-10K>
+ <TQBL-Qpid-04-50K>-n TQBL-Qpid-04-50K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-50K>
+ <TQBL-Qpid-04-100K>-n TQBL-Qpid-04-100K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-100K>
+ <TQBL-Qpid-04-500K>-n TQBL-Qpid-04-500K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-500K>
+ <TQBL-Qpid-04-1M>-n TQBL-Qpid-04-1M -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=false pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </TQBL-Qpid-04-1M>
+
+ <PQBL-Qpid-03-128b>-n PQBL-Qpid-03-128b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-128b>
+ <PQBL-Qpid-03-256b>-n PQBL-Qpid-03-256b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-256b>
+ <PQBL-Qpid-03-512b>-n PQBL-Qpid-03-512b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-512b>
+ <PQBL-Qpid-03-1K>-n PQBL-Qpid-03-1K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-1K>
+ <PQBL-Qpid-03-5K>-n PQBL-Qpid-03-5K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-5K>
+ <PQBL-Qpid-03-10K>-n PQBL-Qpid-03-10K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-10K>
+ <PQBL-Qpid-03-50K>-n PQBL-Qpid-03-50K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-50K>
+ <PQBL-Qpid-03-100K>-n PQBL-Qpid-03-100K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-100K>
+ <PQBL-Qpid-03-500K>-n PQBL-Qpid-03-500K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-500K>
+ <PQBL-Qpid-03-1M>-n PQBL-Qpid-03-1M -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=true consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-03-1M>
+
+ <PQBL-Qpid-04-128b>-n PQBL-Qpid-04-128b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-128b>
+ <PQBL-Qpid-04-256b>-n PQBL-Qpid-04-256b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-256b>
+ <PQBL-Qpid-04-512b>-n PQBL-Qpid-04-512b -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-512b>
+ <PQBL-Qpid-04-1K>-n PQBL-Qpid-04-1K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1024 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-1K>
+ <PQBL-Qpid-04-5K>-n PQBL-Qpid-04-5K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=5120 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-5K>
+ <PQBL-Qpid-04-10K>-n PQBL-Qpid-04-10K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=10240 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-10K>
+ <PQBL-Qpid-04-50K>-n PQBL-Qpid-04-50K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=51200 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-50K>
+ <PQBL-Qpid-04-100K>-n PQBL-Qpid-04-100K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=102400 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-100K>
+ <PQBL-Qpid-04-500K>-n PQBL-Qpid-04-500K -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=512000 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-500K>
+ <PQBL-Qpid-04-1M>-n PQBL-Qpid-04-1M -d1M -s[100] -c[1,32],samples=6,exp -o $QPID_WORK/results -t testPingLatency org.apache.qpid.ping.PingLatencyTestPerf persistent=true pubsub=false uniqueDests=true numConsumers=1 transacted=false consTransacted=false consAckMode=1 commitBatchSize=1 batchSize=100 messageSize=1048576 destinationCount=1 rate=0 maxPending=2000000 </PQBL-Qpid-04-1M>
+ -->
+
+ <!-- Failover Tests. -->
+ <FT-Qpid-01>-n FT-Qpid-01 -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf messageSize=256 batchSize=10000 transacted=true broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" failBeforeSend=true -o $QPID_WORK/results</FT-Qpid-01>
+ <FT-Qpid-02>-n FT-Qpid-02 -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf messageSize=256 batchSize=10000 transacted=true broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" failAfterSend=true -o $QPID_WORK/results</FT-Qpid-02>
+ <FT-Qpid-03>-n FT-Qpid-03 -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf messageSize=256 batchSize=1000 transacted=true broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" failAfterCommit=true -o $QPID_WORK/results</FT-Qpid-03>
+ <FT-Qpid-04>-n FT-Qpid-04 -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf messageSize=256 batchSize=1000 broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" transacted=false failBeforeSend=true -o $QPID_WORK/results</FT-Qpid-04>
+ <FT-Qpid-05>-n FT-Qpid-05 -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf messageSize=256 batchSize=1000 broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" transacted=false failAfterSend=true -o $QPID_WORK/results</FT-Qpid-05>
+ <FT-Qpid-01-P>-n FT-Qpid-01-P -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true messageSize=256 batchSize=1000 transacted=true broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" failBeforeSend=true -o $QPID_WORK/results</FT-Qpid-01-P>
+ <FT-Qpid-02-P>-n FT-Qpid-02-P -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true messageSize=256 batchSize=1000 transacted=true broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" failAfterSend=true -o $QPID_WORK/results</FT-Qpid-02-P>
+ <FT-Qpid-03-P>-n FT-Qpid-03-P -s[2500] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true messageSize=256 batchSize=1000 transacted=true broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" failAfterCommit=true -o $QPID_WORK/results</FT-Qpid-03-P>
+ <FT-Qpid-04-P>-n FT-Qpid-04-P -s[250] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true messageSize=256 batchSize=1000 broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" transacted=false failBeforeSend=true -o $QPID_WORK/results</FT-Qpid-04-P>
+ <FT-Qpid-05-P>-n FT-Qpid-05-P -s[250] -t testAsyncPingOk org.apache.qpid.ping.PingAsyncTestPerf persistent=true messageSize=256 batchSize=1000 broker="tcp://127.0.0.1:5001;tcp://127.0.0.1:5002" transacted=false failAfterSend=true -o $QPID_WORK/results</FT-Qpid-05-P>
+
+ </commands>
+ </configuration>
+
+ <executions>
+ <execution>
+ <phase>test</phase>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- Bundles all the dependencies, fully expanded into a single jar, required to run the tests.
+ Also builds all thescripts and this jar into distributable .zip and .tar.gz files.
+
+ Usefull when bundling system, integration or performance tests into a convenient
+ package to hand over to testers. To use it run:
+
+ java -cp target/your_app_name-all-test-deps.jar path.to.your.Class
+
+ or often:
+
+ java -cp target/your_app_name-all-test-deps.jar junit.framework.textui.TestRunner path.to.your.test.Class
+
+ or other JUnit test runner invocations.
+ -->
+ <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>
+ <!--<descriptor>dist-zip.xml</descriptor>-->
+ </descriptors>
+ <outputDirectory>target</outputDirectory>
+ <workDirectory>target/assembly/work</workDirectory>
+ </configuration>
+ </plugin>
+
+ <!-- Build a manifest only jar with all the required jars for the broker in its classpath. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>test_jar</id>
+ <configuration>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ </manifest>
+ </archive>
+ </configuration>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+
+ <resources>
+ <!-- Include source files in built jar -->
+ <resource>
+ <targetPath>src/</targetPath>
+ <filtering>false</filtering>
+ <directory>src/main/java</directory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </resource>
+ <!-- Include a log4j configuration in the jar at the root level (don't name this log4j.properties though as won't be able to override it). -->
+ <resource>
+ <targetPath>/</targetPath>
+ <filtering>false</filtering>
+ <directory>src/main/java</directory>
+ <includes>
+ <include>perftests.log4j</include>
+ </includes>
+ </resource>
+ </resources>
+ </build>
+
+</project>
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java b/Final/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java
new file mode 100644
index 0000000000..64ccb719b6
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java
@@ -0,0 +1,121 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.client.message;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.SimpleByteBufferAllocator;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.ObjectMessage;
+import javax.jms.StreamMessage;
+import javax.jms.BytesMessage;
+import javax.jms.TextMessage;
+import javax.jms.Queue;
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+
+public class TestMessageFactory
+{
+ private static final String MESSAGE_DATA_BYTES = "-message payload-message paylaod-message payload-message paylaod";
+
+ public static TextMessage newTextMessage(Session session, int size) throws JMSException
+ {
+ return session.createTextMessage(createMessagePayload(size));
+ }
+
+ public static JMSTextMessage newJMSTextMessage(int size, String encoding) throws JMSException
+ {
+ ByteBuffer byteBuffer = (new SimpleByteBufferAllocator()).allocate(size, true);
+ JMSTextMessage message = new JMSTextMessage(byteBuffer, encoding);
+ message.clearBody();
+ message.setText(createMessagePayload(size));
+ return message;
+ }
+
+ public static BytesMessage newBytesMessage(Session session, int size) throws JMSException
+ {
+ BytesMessage message = session.createBytesMessage();
+ message.writeUTF(createMessagePayload(size));
+ return message;
+ }
+
+ public static StreamMessage newStreamMessage(Session session, int size) throws JMSException
+ {
+ StreamMessage message = session.createStreamMessage();
+ message.writeString(createMessagePayload(size));
+ return message;
+ }
+
+ public static ObjectMessage newObjectMessage(Session session, int size) throws JMSException
+ {
+ if (size == 0)
+ {
+ return session.createObjectMessage();
+ }
+ else
+ {
+ return session.createObjectMessage(createMessagePayload(size));
+ }
+ }
+
+ /**
+ * Creates an ObjectMessage with given size and sets the JMS properties (JMSReplyTo and DeliveryMode)
+ * @param session
+ * @param replyDestination
+ * @param size
+ * @param persistent
+ * @return the new ObjectMessage
+ * @throws JMSException
+ */
+ public static ObjectMessage newObjectMessage(Session session, Destination replyDestination, int size, boolean persistent) throws JMSException
+ {
+ ObjectMessage msg = newObjectMessage(session, size);
+
+ // Set the messages persistent delivery flag.
+ msg.setJMSDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
+
+ // Ensure that the temporary reply queue is set as the reply to destination for the message.
+ if (replyDestination != null)
+ {
+ msg.setJMSReplyTo(replyDestination);
+ }
+
+ return msg;
+ }
+
+ public static String createMessagePayload(int size)
+ {
+ StringBuffer buf = new StringBuffer(size);
+ int count = 0;
+ while (count <= (size - MESSAGE_DATA_BYTES.length()))
+ {
+ buf.append(MESSAGE_DATA_BYTES);
+ count += MESSAGE_DATA_BYTES.length();
+ }
+ if (count < size)
+ {
+ buf.append(MESSAGE_DATA_BYTES, 0, size - count);
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java b/Final/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java
new file mode 100644
index 0000000000..cac0064785
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.config.ConnectionFactoryInitialiser;
+import org.apache.qpid.config.ConnectorConfig;
+
+import javax.jms.ConnectionFactory;
+
+class AMQConnectionFactoryInitialiser implements ConnectionFactoryInitialiser
+{
+ public ConnectionFactory getFactory(ConnectorConfig config)
+ {
+ return new AMQConnectionFactory(config.getHost(), config.getPort(), "/test_path");
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java b/Final/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java
new file mode 100644
index 0000000000..14db74438f
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java
@@ -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.
+ *
+ */
+package org.apache.qpid.config;
+
+public abstract class AbstractConfig
+{
+ public boolean setOptions(String[] argv)
+ {
+ try
+ {
+ for(int i = 0; i < argv.length - 1; i += 2)
+ {
+ String key = argv[i];
+ String value = argv[i+1];
+ setOption(key, value);
+ }
+ return true;
+ }
+ catch(Exception e)
+ {
+ System.out.println(e.getMessage());
+ }
+ return false;
+ }
+
+ protected int parseInt(String msg, String i)
+ {
+ try
+ {
+ return Integer.parseInt(i);
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException(msg + ": " + i, e);
+ }
+ }
+
+ protected long parseLong(String msg, String i)
+ {
+ try
+ {
+ return Long.parseLong(i);
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException(msg + ": " + i, e);
+ }
+ }
+
+ public abstract void setOption(String key, String value);
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java b/Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java
new file mode 100644
index 0000000000..a9984eb09a
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+
+public interface ConnectionFactoryInitialiser
+{
+ public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException;
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/config/Connector.java b/Final/java/perftests/src/main/java/org/apache/qpid/config/Connector.java
new file mode 100644
index 0000000000..ff2377f087
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/config/Connector.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+
+public class Connector
+{
+ public Connection createConnection(ConnectorConfig config) throws Exception
+ {
+ return getConnectionFactory(config).createConnection();
+ }
+
+ ConnectionFactory getConnectionFactory(ConnectorConfig config) throws Exception
+ {
+ String factory = config.getFactory();
+ if(factory == null) factory = AMQConnectionFactoryInitialiser.class.getName();
+ System.out.println("Using " + factory);
+ return ((ConnectionFactoryInitialiser) Class.forName(factory).newInstance()).getFactory(config);
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java b/Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java
new file mode 100644
index 0000000000..b120ed3f12
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+public interface ConnectorConfig
+{
+ public String getHost();
+ public int getPort();
+ public String getFactory();
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java b/Final/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java
new file mode 100644
index 0000000000..a0248a8f79
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java
@@ -0,0 +1,112 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.config;
+
+import org.apache.qpid.config.ConnectionFactoryInitialiser;
+import org.apache.qpid.config.ConnectorConfig;
+import org.apache.qpid.client.JMSAMQException;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.MBeanException;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.NameNotFoundException;
+import java.util.Hashtable;
+
+public class JBossConnectionFactoryInitialiser implements ConnectionFactoryInitialiser
+{
+ public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException
+ {
+ ConnectionFactory cf = null;
+ InitialContext ic = null;
+ Hashtable ht = new Hashtable();
+ ht.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
+ String jbossHost = System.getProperty("jboss.host", "eqd-lxamq01");
+ String jbossPort = System.getProperty("jboss.port", "1099");
+ ht.put(InitialContext.PROVIDER_URL, "jnp://" + jbossHost + ":" + jbossPort);
+ ht.put(InitialContext.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
+
+ try
+ {
+ ic = new InitialContext(ht);
+ if (!doesDestinationExist("topictest.messages", ic))
+ {
+ deployTopic("topictest.messages", ic);
+ }
+ if (!doesDestinationExist("topictest.control", ic))
+ {
+ deployTopic("topictest.control", ic);
+ }
+
+ cf = (ConnectionFactory) ic.lookup("/ConnectionFactory");
+ return cf;
+ }
+ catch (NamingException e)
+ {
+ throw new JMSAMQException("Unable to lookup object: " + e, e);
+ }
+ catch (Exception e)
+ {
+ throw new JMSAMQException("Error creating topic: " + e, e);
+ }
+ }
+
+ private boolean doesDestinationExist(String name, InitialContext ic) throws Exception
+ {
+ try
+ {
+ ic.lookup("/" + name);
+ }
+ catch (NameNotFoundException e)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private void deployTopic(String name, InitialContext ic) throws Exception
+ {
+ MBeanServerConnection mBeanServer = lookupMBeanServerProxy(ic);
+
+ ObjectName serverObjectName = new ObjectName("jboss.messaging:service=ServerPeer");
+
+ String jndiName = "/" + name;
+ try
+ {
+ mBeanServer.invoke(serverObjectName, "createTopic",
+ new Object[]{name, jndiName},
+ new String[]{"java.lang.String", "java.lang.String"});
+ }
+ catch (MBeanException e)
+ {
+ System.err.println("Error: " + e);
+ System.err.println("Cause: " + e.getCause());
+ }
+ }
+
+ private MBeanServerConnection lookupMBeanServerProxy(InitialContext ic) throws NamingException
+ {
+ return (MBeanServerConnection) ic.lookup("jmx/invoker/RMIAdaptor");
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java
new file mode 100644
index 0000000000..5b6169ed2d
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java
@@ -0,0 +1,243 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.oldtopic;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.config.ConnectorConfig;
+import org.apache.qpid.config.ConnectionFactoryInitialiser;
+import org.apache.qpid.config.Connector;
+import org.apache.qpid.config.AbstractConfig;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+
+class Config extends AbstractConfig implements ConnectorConfig
+{
+
+ private String host = "localhost";
+ private int port = 5672;
+ private String factory = null;
+
+ private int payload = 256;
+ private int messages = 1000;
+ private int clients = 1;
+ private int batch = 1;
+ private long delay = 1;
+ private int warmup;
+ private int ackMode= AMQSession.NO_ACKNOWLEDGE;
+ private String clientId;
+ private String subscriptionId;
+ private boolean persistent;
+
+ public Config()
+ {
+ }
+
+ int getAckMode()
+ {
+ return ackMode;
+ }
+
+ void setPayload(int payload)
+ {
+ this.payload = payload;
+ }
+
+ int getPayload()
+ {
+ return payload;
+ }
+
+ void setClients(int clients)
+ {
+ this.clients = clients;
+ }
+
+ int getClients()
+ {
+ return clients;
+ }
+
+ void setMessages(int messages)
+ {
+ this.messages = messages;
+ }
+
+ int getMessages()
+ {
+ return messages;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public void setHost(String host)
+ {
+ this.host = host;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ public String getFactory()
+ {
+ return factory;
+ }
+
+ public void setPort(int port)
+ {
+ this.port = port;
+ }
+
+ int getBatch()
+ {
+ return batch;
+ }
+
+ void setBatch(int batch)
+ {
+ this.batch = batch;
+ }
+
+ int getWarmup()
+ {
+ return warmup;
+ }
+
+ void setWarmup(int warmup)
+ {
+ this.warmup = warmup;
+ }
+
+ public long getDelay()
+ {
+ return delay;
+ }
+
+ public void setDelay(long delay)
+ {
+ this.delay = delay;
+ }
+
+ String getClientId()
+ {
+ return clientId;
+ }
+
+ String getSubscriptionId()
+ {
+ return subscriptionId;
+ }
+
+ boolean usePersistentMessages()
+ {
+ return persistent;
+ }
+
+ public void setOption(String key, String value)
+ {
+ if("-host".equalsIgnoreCase(key))
+ {
+ setHost(value);
+ }
+ else if("-port".equalsIgnoreCase(key))
+ {
+ try
+ {
+ setPort(Integer.parseInt(value));
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException("Bad port number: " + value);
+ }
+ }
+ else if("-payload".equalsIgnoreCase(key))
+ {
+ setPayload(parseInt("Bad payload size", value));
+ }
+ else if("-messages".equalsIgnoreCase(key))
+ {
+ setMessages(parseInt("Bad message count", value));
+ }
+ else if("-clients".equalsIgnoreCase(key))
+ {
+ setClients(parseInt("Bad client count", value));
+ }
+ else if("-batch".equalsIgnoreCase(key))
+ {
+ setBatch(parseInt("Bad batch count", value));
+ }
+ else if("-delay".equalsIgnoreCase(key))
+ {
+ setDelay(parseLong("Bad batch delay", value));
+ }
+ else if("-warmup".equalsIgnoreCase(key))
+ {
+ setWarmup(parseInt("Bad warmup count", value));
+ }
+ else if("-ack".equalsIgnoreCase(key))
+ {
+ ackMode = parseInt("Bad ack mode", value);
+ }
+ else if("-factory".equalsIgnoreCase(key))
+ {
+ factory = value;
+ }
+ else if("-clientId".equalsIgnoreCase(key))
+ {
+ clientId = value;
+ }
+ else if("-subscriptionId".equalsIgnoreCase(key))
+ {
+ subscriptionId = value;
+ }
+ else if("-persistent".equalsIgnoreCase(key))
+ {
+ persistent = "true".equalsIgnoreCase(value);
+ }
+ else
+ {
+ System.out.println("Ignoring unrecognised option: " + key);
+ }
+ }
+
+ static String getAckModeDescription(int ackMode)
+ {
+ switch(ackMode)
+ {
+ case AMQSession.NO_ACKNOWLEDGE: return "NO_ACKNOWLEDGE";
+ case AMQSession.AUTO_ACKNOWLEDGE: return "AUTO_ACKNOWLEDGE";
+ case AMQSession.CLIENT_ACKNOWLEDGE: return "CLIENT_ACKNOWLEDGE";
+ case AMQSession.DUPS_OK_ACKNOWLEDGE: return "DUPS_OK_ACKNOWELDGE";
+ case AMQSession.PRE_ACKNOWLEDGE: return "PRE_ACKNOWLEDGE";
+ }
+ return "AckMode=" + ackMode;
+ }
+
+ public Connection createConnection() throws Exception
+ {
+ return new Connector().createConnection(this);
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java
new file mode 100644
index 0000000000..4732782d4c
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java
@@ -0,0 +1,141 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.oldtopic;
+import org.apache.log4j.*;
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+public class Listener implements MessageListener
+{
+ private final Connection _connection;
+ private final MessageProducer _controller;
+ private final javax.jms.Session _session;
+ private final MessageFactory _factory;
+ private boolean init;
+ private int count;
+ private long start;
+
+ Listener(Connection connection, int ackMode) throws Exception
+ {
+ this(connection, ackMode, null);
+ }
+
+ Listener(Connection connection, int ackMode, String name) throws Exception
+ {
+ _connection = connection;
+ _session = connection.createSession(false, ackMode);
+ _factory = new MessageFactory(_session);
+
+ //register for events
+ if(name == null)
+ {
+ _factory.createTopicConsumer().setMessageListener(this);
+ }
+ else
+ {
+ _factory.createDurableTopicConsumer(name).setMessageListener(this);
+ }
+
+ _connection.start();
+
+ _controller = _factory.createControlPublisher();
+ System.out.println("Waiting for messages " +
+ Config.getAckModeDescription(ackMode)
+ + (name == null ? "" : " (subscribed with name " + name + " and client id " + connection.getClientID() + ")")
+ + "...");
+
+ }
+
+ private void shutdown()
+ {
+ try
+ {
+ _session.close();
+ _connection.stop();
+ _connection.close();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private void report()
+ {
+ try
+ {
+ String msg = getReport();
+ _controller.send(_factory.createReportResponseMessage(msg));
+ System.out.println("Sent report: " + msg);
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private String getReport()
+ {
+ long time = (System.currentTimeMillis() - start);
+ return "Received " + count + " in " + time + "ms";
+ }
+
+ public void onMessage(Message message)
+ {
+ if(!init)
+ {
+ start = System.currentTimeMillis();
+ count = 0;
+ init = true;
+ }
+
+ if(_factory.isShutdown(message))
+ {
+ shutdown();
+ }
+ else if(_factory.isReport(message))
+ {
+ //send a report:
+ report();
+ init = false;
+ }
+ else if (++count % 100 == 0)
+ {
+ System.out.println("Received " + count + " messages.");
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config();
+ config.setOptions(argv);
+
+ Connection con = config.createConnection();
+ if(config.getClientId() != null)
+ {
+ con.setClientID(config.getClientId());
+ }
+ new Listener(con, config.getAckMode(), config.getSubscriptionId());
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java
new file mode 100644
index 0000000000..b2fbeb7e35
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java
@@ -0,0 +1,153 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.oldtopic;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+
+import javax.jms.*;
+
+/**
+ */
+class MessageFactory
+{
+ private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray();
+
+ private final Session _session;
+ private final Topic _topic;
+ private final Topic _control;
+ private final byte[] _payload;
+
+
+ MessageFactory(Session session) throws JMSException
+ {
+ this(session, 256);
+ }
+
+ MessageFactory(Session session, int size) throws JMSException
+ {
+ _session = session;
+/* if(session instanceof AMQSession)
+ {
+ _topic = new AMQTopic("topictest.messages");
+ _control = new AMQTopic("topictest.control");
+ }
+ else*/
+ {
+ _topic = session.createTopic("topictest.messages");
+ _control = session.createTopic("topictest.control");
+ }
+ _payload = new byte[size];
+
+ for(int i = 0; i < size; i++)
+ {
+ _payload[i] = (byte) DATA[i % DATA.length];
+ }
+ }
+
+ Topic getTopic()
+ {
+ return _topic;
+ }
+
+ Message createEventMessage() throws JMSException
+ {
+ BytesMessage msg = _session.createBytesMessage();
+ msg.writeBytes(_payload);
+ return msg;
+ }
+
+ Message createShutdownMessage() throws JMSException
+ {
+ return _session.createTextMessage("SHUTDOWN");
+ }
+
+ Message createReportRequestMessage() throws JMSException
+ {
+ return _session.createTextMessage("REPORT");
+ }
+
+ Message createReportResponseMessage(String msg) throws JMSException
+ {
+ return _session.createTextMessage(msg);
+ }
+
+ boolean isShutdown(Message m)
+ {
+ return checkText(m, "SHUTDOWN");
+ }
+
+ boolean isReport(Message m)
+ {
+ return checkText(m, "REPORT");
+ }
+
+ Object getReport(Message m)
+ {
+ try
+ {
+ return ((TextMessage) m).getText();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return e.toString();
+ }
+ }
+
+ MessageConsumer createTopicConsumer() throws Exception
+ {
+ return _session.createConsumer(_topic);
+ }
+
+ MessageConsumer createDurableTopicConsumer(String name) throws Exception
+ {
+ return _session.createDurableSubscriber(_topic, name);
+ }
+
+ MessageConsumer createControlConsumer() throws Exception
+ {
+ return _session.createConsumer(_control);
+ }
+
+ MessageProducer createTopicPublisher() throws Exception
+ {
+ return _session.createProducer(_topic);
+ }
+
+ MessageProducer createControlPublisher() throws Exception
+ {
+ return _session.createProducer(_control);
+ }
+
+ private static boolean checkText(Message m, String s)
+ {
+ try
+ {
+ return m instanceof TextMessage && ((TextMessage) m).getText().equals(s);
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+ return false;
+ }
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java
new file mode 100644
index 0000000000..f811704323
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.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.oldtopic;
+
+import javax.jms.*;
+
+public class Publisher implements MessageListener
+{
+ private final Object _lock = new Object();
+ private final Connection _connection;
+ private final Session _session;
+ private final MessageFactory _factory;
+ private final MessageProducer _publisher;
+ private int _count;
+
+ Publisher(Connection connection, int size, int ackMode, boolean persistent) throws Exception
+ {
+ _connection = connection;
+ _session = _connection.createSession(false, ackMode);
+ _factory = new MessageFactory(_session, size);
+ _publisher = _factory.createTopicPublisher();
+ _publisher.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
+ System.out.println("Publishing " + (persistent ? "persistent" : "non-persistent") + " messages of " + size + " bytes, " + Config.getAckModeDescription(ackMode) + ".");
+ }
+
+ private void test(Config config) throws Exception
+ {
+ test(config.getBatch(), config.getDelay(), config.getMessages(), config.getClients(), config.getWarmup());
+ }
+
+ private void test(int batches, long delay, int msgCount, int consumerCount, int warmup) throws Exception
+ {
+ _factory.createControlConsumer().setMessageListener(this);
+ _connection.start();
+
+ if(warmup > 0)
+ {
+ System.out.println("Runing warmup (" + warmup + " msgs)");
+ long time = batch(warmup, consumerCount);
+ System.out.println("Warmup completed in " + time + "ms");
+ }
+
+ long[] times = new long[batches];
+ for(int i = 0; i < batches; i++)
+ {
+ if(i > 0) Thread.sleep(delay*1000);
+ times[i] = batch(msgCount, consumerCount);
+ System.out.println("Batch " + (i+1) + " of " + batches + " completed in " + times[i] + " ms.");
+ }
+
+ long min = min(times);
+ long max = max(times);
+ System.out.println("min: " + min + ", max: " + max + " avg: " + avg(times, min, max));
+
+ //request shutdown
+ _publisher.send(_factory.createShutdownMessage());
+
+ _connection.stop();
+ _connection.close();
+ }
+
+ private long batch(int msgCount, int consumerCount) throws Exception
+ {
+ _count = consumerCount;
+ long start = System.currentTimeMillis();
+ publish(msgCount);
+ waitForCompletion(consumerCount);
+ return System.currentTimeMillis() - start;
+ }
+
+ private void publish(int count) throws Exception
+ {
+
+ //send events
+ for (int i = 0; i < count; i++)
+ {
+ _publisher.send(_factory.createEventMessage());
+ if ((i + 1) % 100 == 0)
+ {
+ System.out.println("Sent " + (i + 1) + " messages");
+ }
+ }
+
+ //request report
+ _publisher.send(_factory.createReportRequestMessage());
+ }
+
+ private void waitForCompletion(int consumers) throws Exception
+ {
+ System.out.println("Waiting for completion...");
+ synchronized (_lock)
+ {
+ while (_count > 0)
+ {
+ _lock.wait();
+ }
+ }
+ }
+
+
+ public void onMessage(Message message)
+ {
+ System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining");
+ if (_count == 0)
+ {
+ synchronized (_lock)
+ {
+ _lock.notify();
+ }
+ }
+ }
+
+ static long min(long[] times)
+ {
+ long min = times.length > 0 ? times[0] : 0;
+ for(int i = 0; i < times.length; i++)
+ {
+ min = Math.min(min, times[i]);
+ }
+ return min;
+ }
+
+ static long max(long[] times)
+ {
+ long max = times.length > 0 ? times[0] : 0;
+ for(int i = 0; i < times.length; i++)
+ {
+ max = Math.max(max, times[i]);
+ }
+ return max;
+ }
+
+ static long avg(long[] times, long min, long max)
+ {
+ long sum = 0;
+ for(int i = 0; i < times.length; i++)
+ {
+ sum += times[i];
+ }
+ sum -= min;
+ sum -= max;
+
+ return (sum / (times.length - 2));
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config();
+ config.setOptions(argv);
+
+ Connection con = config.createConnection();
+ int size = config.getPayload();
+ int ackMode = config.getAckMode();
+ boolean persistent = config.usePersistentMessages();
+ new Publisher(con, size, ackMode, persistent).test(config);
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java b/Final/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java
new file mode 100644
index 0000000000..760d1c84a4
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.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.perftests;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.Assertion;
+import org.apache.qpid.test.framework.Circuit;
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+import org.apache.qpid.test.framework.MessagingTestConfigProperties;
+import org.apache.qpid.test.framework.sequencers.CircuitFactory;
+
+import 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 java.util.LinkedList;
+
+/**
+ * QpidTestThroughputPerf runs a test over a {@link Circuit} controlled by the test parameters. It logs timings of
+ * the time required to receive samples consisting of batches of messages. As the time samples is taken over a reasonable
+ * sized message batch, it measures message throughput.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ *
+ * @todo Check that all of the messages were sent. Check that the receiving end got the same number of messages as
+ * the publishing end.
+ */
+public class QpidTestThroughputPerf extends FrameworkBaseCase implements TimingControllerAware, TestThreadAware
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(QpidTestThroughputPerf.class);
+
+ /** Holds the timing controller, used to log test timings from self-timed tests. */
+ private TimingController timingController;
+
+ /** Thread local to hold the per-thread test setup fields. */
+ ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
+
+ /**
+ * Creates a new test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public QpidTestThroughputPerf(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Performs the a basic P2P test case.
+ *
+ * @param numMessages The number of messages to send in the test.
+ */
+ public void testThroughput(int numMessages)
+ {
+ log.debug("public void testThroughput(): called");
+
+ PerThreadSetup setup = threadSetup.get();
+ assertNoFailures(setup.testCircuit.test(numMessages, new LinkedList<Assertion>()));
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as known to the test
+ * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test
+ * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case
+ * name "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ *
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ log.debug("public String getTestCaseNameForTestMethod(String methodName = " + methodName + "): called");
+
+ return "DEFAULT_CIRCUIT_TEST";
+ }
+
+ /**
+ * Used by test runners that can supply a {@link uk.co.thebadgerset.junit.extensions.TimingController} to set the
+ * controller on an aware test.
+ *
+ * @param controller The timing controller.
+ */
+ public void setTimingController(TimingController controller)
+ {
+ timingController = controller;
+ }
+
+ /**
+ * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
+ */
+ public void threadSetUp()
+ {
+ // Get the test parameters, any overrides on the command line will have been applied.
+ ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
+
+ // Customize the test parameters.
+ testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST");
+ testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue");
+
+ // Get the test circuit factory to create test circuits and run the standard test procedure through.
+ CircuitFactory circuitFactory = getCircuitFactory();
+
+ // Create the test circuit. This projects the circuit onto the available test nodes and connects it up.
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Store the test configuration for the thread.
+ PerThreadSetup setup = new PerThreadSetup();
+ setup.testCircuit = testCircuit;
+ threadSetup.set(setup);
+ }
+
+ /**
+ * Called when a test thread is destroyed.
+ */
+ public void threadTearDown()
+ { }
+
+ /**
+ * Holds the per-thread test configurations.
+ */
+ protected static class PerThreadSetup
+ {
+ /** Holds the test circuit to run tests on. */
+ Circuit testCircuit;
+ }
+
+ /**
+ * Compiles all the tests in this class into a suite.
+ *
+ * @return The test suite.
+ */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("Qpid Throughput Performance Tests");
+
+ suite.addTest(new QpidTestThroughputPerf("testThroughput"));
+
+ return suite;
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java
new file mode 100644
index 0000000000..06081e6ebf
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java
@@ -0,0 +1,292 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.ping;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.requestreply.PingPongProducer;
+
+import uk.co.thebadgerset.junit.extensions.TimingController;
+import uk.co.thebadgerset.junit.extensions.TimingControllerAware;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * PingAsyncTestPerf is a performance test that outputs multiple timings from its test method, using the timing controller
+ * interface supplied by the test runner from a seperate listener thread. It differs from the {@link PingTestPerf} test
+ * that it extends because it can output timings as replies are received, rather than waiting until all expected replies
+ * are received. This is less 'blocky' than the tests in {@link PingTestPerf}, and provides a truer simulation of sending
+ * and recieving clients working asynchronously.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><td> Responsibilities <th> Collaborations
+ * <tr><td> Send many ping messages and output timings asynchronously on batches received.
+ * </table>
+ */
+public class PingAsyncTestPerf extends PingTestPerf implements TimingControllerAware
+{
+ private static Logger _logger = Logger.getLogger(PingAsyncTestPerf.class);
+
+ /** Holds the name of the property to get the test results logging batch size. */
+ public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize";
+
+ /** Holds the default test results logging batch size. */
+ public static final int TEST_RESULTS_BATCH_SIZE_DEFAULT = 1000;
+
+ /** Used to hold the timing controller passed from the test runner. */
+ private TimingController _timingController;
+
+ /** Used to generate unique correlation ids for each test run. */
+ private AtomicLong corellationIdGenerator = new AtomicLong();
+
+ /** Holds test specifics by correlation id. This consists of the expected number of messages and the timing controler. */
+ private Map<String, PerCorrelationId> perCorrelationIds =
+ Collections.synchronizedMap(new HashMap<String, PerCorrelationId>());
+
+ /** Holds the batched results listener, that does logging on batch boundaries. */
+ private BatchedResultsListener batchedResultsListener = null;
+
+ /**
+ * Creates a new asynchronous ping performance test with the specified name.
+ *
+ * @param name The test name.
+ */
+ public PingAsyncTestPerf(String name)
+ {
+ super(name);
+
+ // Sets up the test parameters with defaults.
+ testParameters.setPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME,
+ Integer.toString(TEST_RESULTS_BATCH_SIZE_DEFAULT));
+ }
+
+ /**
+ * Compile all the tests into a test suite.
+ * @return The test suite to run. Should only contain testAsyncPingOk method.
+ */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("Ping Performance Tests");
+
+ // Run performance tests in read committed mode.
+ suite.addTest(new PingAsyncTestPerf("testAsyncPingOk"));
+
+ return suite;
+ }
+
+ /**
+ * Accepts a timing controller from the test runner.
+ *
+ * @param timingController The timing controller to register mutliple timings with.
+ */
+ public void setTimingController(TimingController timingController)
+ {
+ _timingController = timingController;
+ }
+
+ /**
+ * Gets the timing controller passed in by the test runner.
+ *
+ * @return The timing controller passed in by the test runner.
+ */
+ public TimingController getTimingController()
+ {
+ return _timingController;
+ }
+
+ /**
+ * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until
+ * all replies have been received or a time out occurs before exiting this method.
+ *
+ * @param numPings The number of pings to send.
+ * @throws Exception pass all errors out to the test harness
+ */
+ public void testAsyncPingOk(int numPings) throws Exception
+ {
+ // _logger.debug("public void testAsyncPingOk(int numPings): called");
+
+ // Ensure that at least one ping was requeusted.
+ if (numPings == 0)
+ {
+ _logger.error("Number of pings requested was zero.");
+ fail("Number of pings requested was zero.");
+ }
+
+ // Get the per thread test setup to run the test through.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+ PingClient pingClient = perThreadSetup._pingClient;
+
+ // Advance the correlation id of messages to send, to make it unique for this run.
+ perThreadSetup._correlationId = Long.toString(corellationIdGenerator.incrementAndGet());
+ // String messageCorrelationId = perThreadSetup._correlationId;
+ // _logger.debug("messageCorrelationId = " + messageCorrelationId);
+
+ // Initialize the count and timing controller for the new correlation id.
+ PerCorrelationId perCorrelationId = new PerCorrelationId();
+ TimingController tc = getTimingController().getControllerForCurrentThread();
+ perCorrelationId._tc = tc;
+ perCorrelationId._expectedCount = pingClient.getExpectedNumPings(numPings);
+ perCorrelationIds.put(perThreadSetup._correlationId, perCorrelationId);
+
+ // Send the requested number of messages, and wait until they have all been received.
+ long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME));
+ int numReplies = pingClient.pingAndWaitForReply(null, numPings, timeout, perThreadSetup._correlationId);
+
+ // Check that all the replies were received and log a fail if they were not.
+ if (numReplies < perCorrelationId._expectedCount)
+ {
+ perCorrelationId._tc.completeTest(false, numPings - perCorrelationId._expectedCount);
+ }
+
+ // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up.
+ perCorrelationIds.remove(perThreadSetup._correlationId);
+ }
+
+ /**
+ * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
+ */
+ public void threadSetUp()
+ {
+ _logger.debug("public void threadSetUp(): called");
+
+ try
+ {
+ // Call the set up method in the super class. This creates a PingClient pinger.
+ super.threadSetUp();
+
+ // Create the chained message listener, only if it has not already been created. This is set up with the
+ // batch size property, to tell it what batch size to output results on. A synchronized block is used to
+ // ensure that only one thread creates this.
+ synchronized (this)
+ {
+ if (batchedResultsListener == null)
+ {
+ int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME));
+ batchedResultsListener = new BatchedResultsListener(batchSize);
+ }
+ }
+
+ // Get the set up that the super class created.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+
+ // Register the chained message listener on the pinger to do its asynchronous test timings from.
+ perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener);
+ }
+ catch (Exception e)
+ {
+ _logger.warn("There was an exception during per thread setup.", e);
+ }
+ }
+
+ /**
+ * BatchedResultsListener is a {@link PingPongProducer.ChainedMessageListener} that can be attached to the
+ * pinger, in order to receive notifications about every message received and the number remaining to be
+ * received. Whenever the number remaining crosses a batch size boundary this results listener outputs
+ * a test timing for the actual number of messages received in the current batch.
+ */
+ private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener
+ {
+ /** The test results logging batch size. */
+ int _batchSize;
+
+ /**
+ * Creates a results listener on the specified batch size.
+ *
+ * @param batchSize The batch size to use.
+ */
+ public BatchedResultsListener(int batchSize)
+ {
+ _batchSize = batchSize;
+ }
+
+ /**
+ * This callback method is called from all of the pingers that this test creates. It uses the correlation id
+ * from the message to identify the timing controller for the test thread that was responsible for sending those
+ * messages.
+ *
+ * @param message The message.
+ * @param remainingCount The count of messages remaining to be received with a particular correlation id.
+ *
+ * @throws JMSException Any underlying JMSException is allowed to fall through.
+ */
+ public void onMessage(Message message, int remainingCount, long latency) throws JMSException
+ {
+ // Check if a batch boundary has been crossed.
+ if ((remainingCount % _batchSize) == 0)
+ {
+ // Extract the correlation id from the message.
+ String correlationId = message.getJMSCorrelationID();
+
+ /*_logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount
+ + "): called on batch boundary for message id: " + correlationId + " with thread id: "
+ + Thread.currentThread().getId());*/
+
+ // Get the details for the correlation id and check that they are not null. They can become null
+ // if a test times out.
+ PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId);
+ if (perCorrelationId != null)
+ {
+ // Get the timing controller and expected count for this correlation id.
+ TimingController tc = perCorrelationId._tc;
+ int expected = perCorrelationId._expectedCount;
+
+ // Calculate how many messages were actually received in the last batch. This will be the batch size
+ // except where the number expected is not a multiple of the batch size and this is the first remaining
+ // count to cross a batch size boundary, in which case it will be the number expected modulo the batch
+ // size.
+ int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize;
+
+ // Register a test result for the correlation id.
+ try
+ {
+ tc.completeTest(true, receivedInBatch);
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore this. It means the test runner wants to stop as soon as possible.
+ _logger.warn("Got InterruptedException.", e);
+ }
+ }
+ // Else ignore, test timed out. Should log a fail here?
+ }
+ }
+ }
+
+ /**
+ * Holds state specific to each correlation id, needed to output test results. This consists of the count of
+ * the total expected number of messages, and the timing controller for the thread sending those message ids.
+ */
+ private static class PerCorrelationId
+ {
+ public int _expectedCount;
+ public TimingController _tc;
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java
new file mode 100644
index 0000000000..b9632eee4c
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java
@@ -0,0 +1,107 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.ping;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.requestreply.PingPongProducer;
+
+import javax.jms.Destination;
+
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * PingClient is a {@link PingPongProducer} that does not need a {@link org.apache.qpid.requestreply.PingPongBouncer}
+ * to send replies to its pings. It simply listens to its own ping destinations, rather than seperate reply queues.
+ * It is an all in one ping client, that produces and consumes its own pings.
+ *
+ * <p/>The constructor increments a count of the number of ping clients created. It is assumed that where many
+ * are created they will all be run in parallel and be active in sending and consuming pings at the same time.
+ * If the unique destinations flag is not set and a pub/sub ping cycle is being run, this means that they will all hear
+ * pings sent by each other. The expected number of pings received will therefore be multiplied up by the number of
+ * active ping clients. The {@link #getConsumersPerDestination()} method is used to supply this multiplier under these
+ * conditions.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create a ping producer that listens to its own pings <td> {@link PingPongProducer}
+ * <tr><td> Count the number of ping producers and produce multiplier for scaling up messages expected over topic pings.
+ * </table>
+ */
+public class PingClient extends PingPongProducer
+{
+ /** Used for debugging. */
+ private final Logger log = Logger.getLogger(PingClient.class);
+
+ /** Used to count the number of ping clients created. */
+ private static int _pingClientCount;
+
+ /**
+ * Creates a ping producer with the specified parameters, of which there are many. See the class level comments
+ * for {@link PingPongProducer} for details. This constructor creates a connection to the broker and creates
+ * producer and consumer sessions on it, to send and recieve its pings and replies on.
+ *
+ * @param overrides Properties containing any desired overrides to the defaults.
+ *
+ * @throws Exception Any exceptions are allowed to fall through.
+ */
+ public PingClient(Properties overrides) throws Exception
+ {
+ super(overrides);
+
+ _pingClientCount++;
+ }
+
+ /**
+ * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the
+ * effect of making this pinger listen to its own pings.
+ *
+ * @return The ping destinations.
+ */
+ public List<Destination> getReplyDestinations()
+ {
+ return _pingDestinations;
+ }
+
+ /**
+ * Supplies the multiplier for the number of ping clients that will hear each ping when doing pub/sub pinging.
+ *
+ * @return The scaling up of the number of expected pub/sub pings.
+ */
+ public int getConsumersPerDestination()
+ {
+ log.debug("public int getConsumersPerDestination(): called");
+
+ if (_isUnique)
+ {
+ log.debug(_noOfConsumers + " consumer per destination.");
+
+ return _noOfConsumers;
+ }
+ else
+ {
+ log.debug((_pingClientCount * _noOfConsumers) + " consumers per destination.");
+
+ return _pingClientCount * _noOfConsumers;
+ }
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java
new file mode 100644
index 0000000000..db6f384914
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java
@@ -0,0 +1,452 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.ping;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.requestreply.PingPongProducer;
+import org.apache.qpid.util.CommandLineParser;
+
+import uk.co.thebadgerset.junit.extensions.util.MathUtils;
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+
+import javax.jms.*;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * PingDurableClient is a variation of the {@link PingPongProducer} ping tool. Instead of sending its pings and
+ * receiving replies to them at the same time, this tool sends pings until it is signalled by some 'event' to stop
+ * sending. It then waits for another signal before it re-opens a fresh connection and attempts to receive all of the
+ * pings that it has succesfully sent. It is intended to be an interactive test that lets a user experiment with
+ * failure conditions when using durable messaging.
+ *
+ * <p/>The events that can stop it from sending are input from the user on the console, failure of its connection to
+ * the broker, completion of sending a specified number of messages, or expiry of a specified duration. In all cases
+ * it will do its best to clean up and close the connection before opening a fresh connection to receive the pings
+ * with.
+ *
+ * <p/>The event to re-connect and attempt to recieve the pings is input from the user on the console.
+ *
+ * <p/>This ping client inherits the configuration properties of its parent class ({@link PingPongProducer}) and
+ * additionally accepts the following parameters:
+ *
+ * <p/><table><caption>Parameters</caption>
+ * <tr><th> Parameter <th> Default <th> Comments
+ * <tr><td> numMessages <td> 100 <td> The total number of messages to send.
+ * <tr><td> numMessagesToAction <td> -1 <td> The number of messages to send before taking a custom 'action'.
+ * <tr><td> duration <td> 30S <td> The length of time to ping for. (Format dDhHmMsS, for d days, h hours,
+ * m minutes and s seconds).
+ * </table>
+ *
+ * <p/>This ping client also overrides some of the defaults of its parent class, to provide a reasonable set up
+ * when no parameters are specified.
+ *
+ * <p/><table><caption>Parameters</caption>
+ * <tr><th> Parameter <th> Default <th> Comments
+ * <tr><td> uniqueDests <td> false <td> Prevents destination names being timestamped.
+ * <tr><td> transacted <td> true <td> Only makes sense to test with transactions.
+ * <tr><td> persistent <td> true <td> Only makes sense to test persistent.
+ * <tr><td> durableDests <td> true <td> Should use durable queues with persistent messages.
+ * <tr><td> commitBatchSize <td> 10
+ * <tr><td> rate <td> 20 <td> Total default test time is 5 seconds.
+ * </table>
+ *
+ * <p/>When a number of messages or duration is specified, this ping client will ping until the first of those limits
+ * is reached. Reaching the limit will be interpreted as the first signal to stop sending, and the ping client will
+ * wait for the second signal before receiving its pings.
+ *
+ * <p/>This class provides a mechanism for extensions to add arbitrary actions, after a particular number of messages
+ * have been sent. When the number of messages equal the value set in the 'numMessagesToAction' property is method,
+ * the {@link #takeAction} method is called. By default this does nothing, but extensions of this class can provide
+ * custom behaviour with alternative implementations of this method (for example taking a backup).
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Send and receive pings.
+ * <tr><td> Accept user input to signal stop sending.
+ * <tr><td> Accept user input to signal start receiving.
+ * <tr><td> Provide feedback on pings sent versus pings received.
+ * <tr><td> Provide extension point for arbitrary action on a particular message count.
+ * </table>
+ */
+public class PingDurableClient extends PingPongProducer implements ExceptionListener
+{
+ private static final Logger log = Logger.getLogger(PingDurableClient.class);
+
+ public static final String NUM_MESSAGES_PROPNAME = "numMessages";
+ public static final String NUM_MESSAGES_DEFAULT = "100";
+ public static final String DURATION_PROPNAME = "duration";
+ public static final String DURATION_DEFAULT = "30S";
+ public static final String NUM_MESSAGES_TO_ACTION_PROPNAME = "numMessagesToAction";
+ public static final String NUM_MESSAGES_TO_ACTION_DEFAULT = "-1";
+
+ /** The maximum length of time to wait whilst receiving pings before assuming that no more are coming. */
+ private static final long TIME_OUT = 3000;
+
+ static
+ {
+ defaults.setProperty(NUM_MESSAGES_PROPNAME, NUM_MESSAGES_DEFAULT);
+ defaults.setProperty(DURATION_PROPNAME, DURATION_DEFAULT);
+ defaults.setProperty(UNIQUE_DESTS_PROPNAME, "false");
+ defaults.setProperty(TRANSACTED_PROPNAME, "true");
+ defaults.setProperty(PERSISTENT_MODE_PROPNAME, "true");
+ defaults.setProperty(TX_BATCH_SIZE_PROPNAME, "10");
+ defaults.setProperty(RATE_PROPNAME, "20");
+ defaults.setProperty(DURABLE_DESTS_PROPNAME, "true");
+ defaults.setProperty(NUM_MESSAGES_TO_ACTION_PROPNAME, NUM_MESSAGES_TO_ACTION_DEFAULT);
+ }
+
+ /** Specifies the number of pings to send, if larger than 0. 0 means send until told to stop. */
+ private int numMessages;
+
+ /** Holds the number of messages to send before taking triggering the action. */
+ private int numMessagesToAction;
+
+ /** Sepcifies how long to ping for, if larger than 0. 0 means send until told to stop. */
+ private long duration;
+
+ /** Used to indciate that this application should terminate. Set by the shutdown hook. */
+ private boolean terminate = false;
+
+ /**
+ * @throws Exception Any exceptions are allowed to fall through.
+ */
+ public PingDurableClient(Properties overrides) throws Exception
+ {
+ super(overrides);
+ log.debug("public PingDurableClient(Properties overrides = " + overrides + "): called");
+
+ // Extract the additional configuration parameters.
+ ParsedProperties properties = new ParsedProperties(defaults);
+ properties.putAll(overrides);
+
+ numMessages = properties.getPropertyAsInteger(NUM_MESSAGES_PROPNAME);
+ String durationSpec = properties.getProperty(DURATION_PROPNAME);
+ numMessagesToAction = properties.getPropertyAsInteger(NUM_MESSAGES_TO_ACTION_PROPNAME);
+
+ if (durationSpec != null)
+ {
+ duration = MathUtils.parseDuration(durationSpec) * 1000000;
+ }
+ }
+
+ /**
+ * Starts the ping/wait/receive process.
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ // Create a ping producer overriding its defaults with all options passed on the command line.
+ Properties options =
+ CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties());
+ PingDurableClient pingProducer = new PingDurableClient(options);
+
+ // Create a shutdown hook to terminate the ping-pong producer.
+ Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook());
+
+ // Ensure that the ping pong producer is registered to listen for exceptions on the connection too.
+ // pingProducer.getConnection().setExceptionListener(pingProducer);
+
+ // Run the test procedure.
+ int sent = pingProducer.send();
+ pingProducer.closeConnection();
+ pingProducer.waitForUser("Press return to begin receiving the pings.");
+ pingProducer.receive(sent);
+
+ System.exit(0);
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ log.error("Top level handler caught execption.", e);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Performs the main test procedure implemented by this ping client. See the class level comment for details.
+ */
+ protected int send() throws Exception
+ {
+ log.debug("public void sendWaitReceive(): called");
+
+ log.debug("duration = " + duration);
+ log.debug("numMessages = " + numMessages);
+
+ if (duration > 0)
+ {
+ System.out.println("Sending for up to " + (duration / 1000000000f) + " seconds.");
+ }
+
+ if (_rate > 0)
+ {
+ System.out.println("Sending at " + _rate + " messages per second.");
+ }
+
+ if (numMessages > 0)
+ {
+ System.out.println("Sending up to " + numMessages + " messages.");
+ }
+
+ // Establish the connection and the message producer.
+ establishConnection(true, false);
+ _connection.start();
+
+ Message message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent);
+
+ // Send pings until a terminating condition is received.
+ boolean endCondition = false;
+ int messagesSent = 0;
+ int messagesCommitted = 0;
+ int messagesNotCommitted = 0;
+ long start = System.nanoTime();
+
+ // Clear console in.
+ clearConsole();
+
+ while (!endCondition)
+ {
+ boolean committed = false;
+
+ try
+ {
+ committed = sendMessage(messagesSent, message) && _transacted;
+
+ messagesSent++;
+ messagesNotCommitted++;
+
+ // Keep count of the number of messsages currently committed and pending commit.
+ if (committed)
+ {
+ log.debug("Adding " + messagesNotCommitted + " messages to the committed count.");
+ messagesCommitted += messagesNotCommitted;
+ messagesNotCommitted = 0;
+
+ System.out.println("Commited: " + messagesCommitted);
+ }
+ }
+ catch (JMSException e)
+ {
+ log.debug("Got JMSException whilst sending.");
+ _publish = false;
+ }
+
+ // Perform the arbitrary action if the number of messages sent has reached the right number.
+ if (messagesSent == numMessagesToAction)
+ {
+ System.out.println("At action point, Messages sent = " + messagesSent + ", Messages Committed = "
+ + messagesCommitted + ", Messages not Committed = " + messagesNotCommitted);
+ takeAction();
+ }
+
+ // Determine if the end condition has been met, based on the number of messages, time passed, errors on
+ // the connection or user input.
+ long now = System.nanoTime();
+
+ if ((duration != 0) && ((now - start) > duration))
+ {
+ System.out.println("Send halted because duration expired.");
+ endCondition = true;
+ }
+ else if ((numMessages != 0) && (messagesSent >= numMessages))
+ {
+ System.out.println("Send halted because # messages completed.");
+ endCondition = true;
+ }
+ else if (System.in.available() > 0)
+ {
+ System.out.println("Send halted by user input.");
+ endCondition = true;
+
+ clearConsole();
+ }
+ else if (!_publish)
+ {
+ System.out.println("Send halted by error on the connection.");
+ endCondition = true;
+ }
+ }
+
+ log.debug("messagesSent = " + messagesSent);
+ log.debug("messagesCommitted = " + messagesCommitted);
+ log.debug("messagesNotCommitted = " + messagesNotCommitted);
+
+ System.out.println("Messages sent: " + messagesSent + ", Messages Committed = " + messagesCommitted
+ + ", Messages not Committed = " + messagesNotCommitted);
+
+ return messagesSent;
+ }
+
+ protected void closeConnection()
+ {
+ // Clean up the connection.
+ try
+ {
+ close();
+ }
+ catch (JMSException e)
+ {
+ log.debug("There was an error whilst closing the connection: " + e, e);
+ System.out.println("There was an error whilst closing the connection.");
+
+ // Ignore as did best could manage to clean up.
+ }
+ }
+
+ protected void receive(int messagesSent) throws Exception
+ {
+ // Re-establish the connection and the message consumer.
+ _queueJVMSequenceID = new AtomicInteger();
+ _queueSharedID = new AtomicInteger();
+
+ establishConnection(false, true);
+ _consumer[0].setMessageListener(null);
+ _consumerConnection[0].start();
+
+ // Try to receive all of the pings that were successfully sent.
+ int messagesReceived = 0;
+ boolean endCondition = false;
+
+ while (!endCondition)
+ {
+ // Message received = _consumer.receiveNoWait();
+ Message received = _consumer[0].receive(TIME_OUT);
+ log.debug("received = " + received);
+
+ if (received != null)
+ {
+ messagesReceived++;
+ }
+
+ // Determine if the end condition has been met, based on the number of messages and time passed since last
+ // receiving a message.
+ if (received == null)
+ {
+ System.out.println("Timed out.");
+ endCondition = true;
+ }
+ else if (messagesReceived >= messagesSent)
+ {
+ System.out.println("Got all messages.");
+ endCondition = true;
+ }
+ }
+
+ // Ensure messages received are committed.
+ if (_consTransacted)
+ {
+ try
+ {
+ _consumerSession[0].commit();
+ System.out.println("Committed for all messages received.");
+ }
+ catch (JMSException e)
+ {
+ log.debug("Error during commit: " + e, e);
+ System.out.println("Error during commit.");
+ try
+ {
+ _consumerSession[0].rollback();
+ System.out.println("Rolled back on all messages received.");
+ }
+ catch (JMSException e2)
+ {
+ log.debug("Error during rollback: " + e, e);
+ System.out.println("Error on roll back of all messages received.");
+ }
+
+ }
+ }
+
+ log.debug("messagesReceived = " + messagesReceived);
+
+ System.out.println("Messages received: " + messagesReceived);
+
+ // Clean up the connection.
+ close();
+ }
+
+ /**
+ * Clears any pending input from the console.
+ */
+ private void clearConsole()
+ {
+ try
+ {
+ BufferedReader bis = new BufferedReader(new InputStreamReader(System.in));
+
+ // System.in.skip(System.in.available());
+ while (bis.ready())
+ {
+ bis.readLine();
+ }
+ }
+ catch (IOException e)
+ { }
+ }
+
+ /**
+ * Returns the ping destinations themselves as the reply destinations for this pinger to listen to. This has the
+ * effect of making this pinger listen to its own pings.
+ *
+ * @return The ping destinations.
+ */
+ public List<Destination> getReplyDestinations()
+ {
+ return _pingDestinations;
+ }
+
+ /**
+ * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered with
+ * the runtime system as a shutdown hook. This shutdown hook sets an additional terminate flag, compared with the
+ * shutdown hook in {@link PingPongProducer}, because the publish flag is used to indicate that sending or receiving
+ * message should stop, not that the application should termiante.
+ *
+ * @return A shutdown hook for the ping loop.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ stop();
+ terminate = true;
+ }
+ });
+ }
+
+ /**
+ * Performs an aribtrary action once the 'numMesagesToAction' count is reached on sending messages. This default
+ * implementation does nothing.
+ */
+ public void takeAction()
+ { }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java
new file mode 100644
index 0000000000..16a6e9c501
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java
@@ -0,0 +1,314 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.ping;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.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 javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.ObjectMessage;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * PingLatencyTestPerf is a performance test that outputs multiple timings from its test method, using the timing
+ * controller interface supplied by the test runner from a seperate listener thread. It outputs round trip timings for
+ * individual ping messages rather than for how long a complete batch of messages took to process. It also differs from
+ * the {@link PingTestPerf} test that it extends because it can output timings as replies are received, rather than
+ * waiting until all expected replies are received.
+ *
+ * <p/>This test does not output timings for every single ping message, as when running at high volume, writing the test
+ * log for a vast number of messages would slow the testing down. Instead samples ping latency occasionally. The
+ * frequency of ping sampling is set using the {@link #TEST_RESULTS_BATCH_SIZE_PROPNAME} property, to override the
+ * default of every {@link #DEFAULT_TEST_RESULTS_BATCH_SIZE}.
+ *
+ * <p/>The size parameter logged for each individual ping is set to the size of the batch of messages that the
+ * individual timed ping was taken from, rather than 1 for a single message. This is so that the total throughput
+ * (messages / time) can be calculated in order to examine the relationship between throughput and latency.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption> <tr><td> Responsibilities <th> Collaborations <tr><td> Send many ping
+ * messages and output timings for sampled individual pings. </table>
+ */
+public class PingLatencyTestPerf extends PingTestPerf implements TimingControllerAware
+{
+ private static Logger _logger = Logger.getLogger(PingLatencyTestPerf.class);
+
+ /** Holds the name of the property to get the test results logging batch size. */
+ public static final String TEST_RESULTS_BATCH_SIZE_PROPNAME = "batchSize";
+
+ /** Holds the default test results logging batch size. */
+ public static final int DEFAULT_TEST_RESULTS_BATCH_SIZE = 1000;
+
+ /** Used to hold the timing controller passed from the test runner. */
+ private TimingController _timingController;
+
+ /** Used to generate unique correlation ids for each test run. */
+ private AtomicLong corellationIdGenerator = new AtomicLong();
+
+ /**
+ * Holds test specifics by correlation id. This consists of the expected number of messages and the timing
+ * controler.
+ */
+ private Map<String, PerCorrelationId> perCorrelationIds =
+ Collections.synchronizedMap(new HashMap<String, PerCorrelationId>());
+
+ /** Holds the batched results listener, that does logging on batch boundaries. */
+ private BatchedResultsListener batchedResultsListener = null;
+
+ /**
+ * Creates a new asynchronous ping performance test with the specified name.
+ *
+ * @param name The test name.
+ */
+ public PingLatencyTestPerf(String name)
+ {
+ super(name);
+
+ // Sets up the test parameters with defaults.
+ ParsedProperties.setSysPropertyIfNull(TEST_RESULTS_BATCH_SIZE_PROPNAME,
+ Integer.toString(DEFAULT_TEST_RESULTS_BATCH_SIZE));
+ }
+
+ /** Compile all the tests into a test suite. */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("Ping Latency Tests");
+
+ // Run performance tests in read committed mode.
+ suite.addTest(new PingLatencyTestPerf("testPingLatency"));
+
+ return suite;
+ }
+
+ /**
+ * Accepts a timing controller from the test runner.
+ *
+ * @param timingController The timing controller to register mutliple timings with.
+ */
+ public void setTimingController(TimingController timingController)
+ {
+ _timingController = timingController;
+ }
+
+ /**
+ * Gets the timing controller passed in by the test runner.
+ *
+ * @return The timing controller passed in by the test runner.
+ */
+ public TimingController getTimingController()
+ {
+ return _timingController;
+ }
+
+ /**
+ * Sends the specified number of pings, asynchronously outputs timings on every batch boundary, and waits until all
+ * replies have been received or a time out occurs before exiting this method.
+ *
+ * @param numPings The number of pings to send.
+ */
+ public void testPingLatency(int numPings) throws Exception
+ {
+ _logger.debug("public void testPingLatency(int numPings): called");
+
+ // Ensure that at least one ping was requeusted.
+ if (numPings == 0)
+ {
+ _logger.error("Number of pings requested was zero.");
+ }
+
+ // Get the per thread test setup to run the test through.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+ PingClient pingClient = perThreadSetup._pingClient;
+
+ // Advance the correlation id of messages to send, to make it unique for this run.
+ String messageCorrelationId = Long.toString(corellationIdGenerator.incrementAndGet());
+ _logger.debug("messageCorrelationId = " + messageCorrelationId);
+
+ // Initialize the count and timing controller for the new correlation id.
+ PerCorrelationId perCorrelationId = new PerCorrelationId();
+ TimingController tc = getTimingController().getControllerForCurrentThread();
+ perCorrelationId._tc = tc;
+ perCorrelationId._expectedCount = numPings;
+ perCorrelationIds.put(messageCorrelationId, perCorrelationId);
+
+ // Attach the chained message listener to the ping producer to listen asynchronously for the replies to these
+ // messages.
+ pingClient.setChainedMessageListener(batchedResultsListener);
+
+ // Generate a sample message of the specified size.
+ Message msg =
+ pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0),
+ testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
+ testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
+
+ // Send the requested number of messages, and wait until they have all been received.
+ long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME));
+ int numReplies = pingClient.pingAndWaitForReply(msg, numPings, timeout, null);
+
+ // Check that all the replies were received and log a fail if they were not.
+ if (numReplies < numPings)
+ {
+ tc.completeTest(false, 0);
+ }
+
+ // Remove the chained message listener from the ping producer.
+ pingClient.removeChainedMessageListener();
+
+ // Remove the expected count and timing controller for the message correlation id, to ensure they are cleaned up.
+ perCorrelationIds.remove(messageCorrelationId);
+ }
+
+ /** Performs test fixture creation on a per thread basis. This will only be called once for each test thread. */
+ public void threadSetUp()
+ {
+ _logger.debug("public void threadSetUp(): called");
+
+ try
+ {
+ // Call the set up method in the super class. This creates a PingClient pinger.
+ super.threadSetUp();
+
+ // Create the chained message listener, only if it has not already been created. This is set up with the
+ // batch size property, to tell it what batch size to output results on. A synchronized block is used to
+ // ensure that only one thread creates this.
+ synchronized (this)
+ {
+ if (batchedResultsListener == null)
+ {
+ int batchSize = Integer.parseInt(testParameters.getProperty(TEST_RESULTS_BATCH_SIZE_PROPNAME));
+ batchedResultsListener = new BatchedResultsListener(batchSize);
+ }
+ }
+
+ // Get the set up that the super class created.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+
+ // Register the chained message listener on the pinger to do its asynchronous test timings from.
+ perThreadSetup._pingClient.setChainedMessageListener(batchedResultsListener);
+ }
+ catch (Exception e)
+ {
+ _logger.warn("There was an exception during per thread setup.", e);
+ }
+ }
+
+ /**
+ * BatchedResultsListener is a {@link org.apache.qpid.requestreply.PingPongProducer.ChainedMessageListener} that can
+ * be attached to the pinger, in order to receive notifications about every message received and the number
+ * remaining to be received. Whenever the number remaining crosses a batch size boundary this results listener
+ * outputs a test timing for the actual number of messages received in the current batch.
+ */
+ private class BatchedResultsListener implements PingPongProducer.ChainedMessageListener
+ {
+ /** The test results logging batch size. */
+ int _batchSize;
+ private boolean _strictAMQP;
+
+ /**
+ * Creates a results listener on the specified batch size.
+ *
+ * @param batchSize The batch size to use.
+ */
+ public BatchedResultsListener(int batchSize)
+ {
+ _batchSize = batchSize;
+ _strictAMQP =
+ Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP,
+ AMQSession.STRICT_AMQP_DEFAULT));
+ }
+
+ /**
+ * This callback method is called from all of the pingers that this test creates. It uses the correlation id
+ * from the message to identify the timing controller for the test thread that was responsible for sending those
+ * messages.
+ *
+ * @param message The message.
+ * @param remainingCount The count of messages remaining to be received with a particular correlation id.
+ *
+ * @throws javax.jms.JMSException Any underlying JMSException is allowed to fall through.
+ */
+ public void onMessage(Message message, int remainingCount, long latency) throws JMSException
+ {
+ _logger.debug("public void onMessage(Message message, int remainingCount = " + remainingCount + "): called");
+
+ // Check if a batch boundary has been crossed.
+ if ((remainingCount % _batchSize) == 0)
+ {
+ // Extract the correlation id from the message.
+ String correlationId = message.getJMSCorrelationID();
+
+ // Get the details for the correlation id and check that they are not null. They can become null
+ // if a test times out.
+ PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationId);
+ if (perCorrelationId != null)
+ {
+ // Get the timing controller and expected count for this correlation id.
+ TimingController tc = perCorrelationId._tc;
+ int expected = perCorrelationId._expectedCount;
+
+ // Calculate how many messages were actually received in the last batch. This will be the batch size
+ // except where the number expected is not a multiple of the batch size and this is the first remaining
+ // count to cross a batch size boundary, in which case it will be the number expected modulo the batch
+ // size.
+ int receivedInBatch = ((expected - remainingCount) < _batchSize) ? (expected % _batchSize) : _batchSize;
+
+ // Register a test result for the correlation id.
+ try
+ {
+ tc.completeTest(true, receivedInBatch, latency);
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore this. It means the test runner wants to stop as soon as possible.
+ _logger.warn("Got InterruptedException.", e);
+ }
+ }
+ // Else ignore, test timed out. Should log a fail here?
+ }
+ }
+ }
+
+ /**
+ * Holds state specific to each correlation id, needed to output test results. This consists of the count of the
+ * total expected number of messages, and the timing controller for the thread sending those message ids.
+ */
+ private static class PerCorrelationId
+ {
+ public int _expectedCount;
+ public TimingController _tc;
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java
new file mode 100644
index 0000000000..2879f0c322
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java
@@ -0,0 +1,93 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.ping;
+
+import java.util.Properties;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.ObjectMessage;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.message.TestMessageFactory;
+import org.apache.qpid.util.CommandLineParser;
+
+/**
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ */
+public class PingSendOnlyClient extends PingDurableClient
+{
+ private static final Logger log = Logger.getLogger(PingSendOnlyClient.class);
+
+ public PingSendOnlyClient(Properties overrides) throws Exception
+ {
+ super(overrides);
+ }
+
+ /**
+ * Starts the ping/wait/receive process.
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ // Create a ping producer overriding its defaults with all options passed on the command line.
+ Properties options = CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties());
+ PingSendOnlyClient pingProducer = new PingSendOnlyClient(options);
+
+ // Create a shutdown hook to terminate the ping-pong producer.
+ Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook());
+
+ // Ensure that the ping pong producer is registered to listen for exceptions on the connection too.
+ // pingProducer.getConnection().setExceptionListener(pingProducer);
+
+ // Run the test procedure.
+ int sent = pingProducer.send();
+ pingProducer.waitForUser("Press return to close connection and quit.");
+ pingProducer.closeConnection();
+
+ System.exit(0);
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ log.error("Top level handler caught execption.", e);
+ System.exit(1);
+ }
+ }
+
+ public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException
+ {
+ Message msg = TestMessageFactory.newTextMessage(_producerSession, messageSize);
+
+ // Timestamp the message in nanoseconds.
+ msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime());
+
+ return msg;
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java
new file mode 100644
index 0000000000..0c2aa80a09
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java
@@ -0,0 +1,196 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.ping;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.requestreply.PingPongProducer;
+
+import 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 javax.jms.*;
+
+/**
+ * PingTestPerf is a ping test, that has been written with the intention of being scaled up to run many times
+ * simultaneously to simluate many clients/producers/connections.
+ *
+ * <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of a single
+ * full round trip ping. This test may be scaled up using a suitable JUnit test runner.
+ *
+ * <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a
+ * temporary queue for replies. This setup is only established once for all the test repeats/threads that may be run,
+ * except if the connection is lost in which case an attempt to re-establish the setup is made.
+ *
+ * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that
+ * is the name of the temporary queue, fires off a message on the original queue and waits for a response on the
+ * temporary queue.
+ *
+ * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ */
+public class PingTestPerf extends AsymptoticTestCase implements TestThreadAware
+{
+ private static Logger _logger = Logger.getLogger(PingTestPerf.class);
+
+ /** Thread local to hold the per-thread test setup fields. */
+ ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
+
+ /** Holds a property reader to extract the test parameters from. */
+ protected ParsedProperties testParameters =
+ TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/);
+
+ public PingTestPerf(String name)
+ {
+ super(name);
+
+ _logger.debug("testParameters = " + testParameters);
+ }
+
+ /**
+ * Compile all the tests into a test suite.
+ * @return The test method testPingOk.
+ */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("Ping Performance Tests");
+
+ // Run performance tests in read committed mode.
+ suite.addTest(new PingTestPerf("testPingOk"));
+
+ return suite;
+ }
+
+ public void testPingOk(int numPings) throws Exception
+ {
+ if (numPings == 0)
+ {
+ Assert.fail("Number of pings requested was zero.");
+ }
+
+ // Get the per thread test setup to run the test through.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+
+ if (perThreadSetup == null)
+ {
+ Assert.fail("Could not get per thread test setup, it was null.");
+ }
+
+ // Generate a sample message. This message is already time stamped and has its reply-to destination set.
+ Message msg =
+ perThreadSetup._pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0),
+ testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
+ testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
+
+ // start the test
+ long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME));
+ int numReplies = perThreadSetup._pingClient.pingAndWaitForReply(msg, numPings, timeout, null);
+
+ // Fail the test if the timeout was exceeded.
+ if (numReplies != perThreadSetup._pingClient.getExpectedNumPings(numPings))
+ {
+ Assert.fail("The ping timed out after " + timeout + " ms. Messages Sent = " + numPings + ", MessagesReceived = "
+ + numReplies);
+ }
+ }
+
+ /**
+ * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
+ */
+ public void threadSetUp()
+ {
+ _logger.debug("public void threadSetUp(): called");
+
+ try
+ {
+ PerThreadSetup perThreadSetup = new PerThreadSetup();
+
+ // This is synchronized because there is a race condition, which causes one connection to sleep if
+ // all threads try to create connection concurrently.
+ synchronized (this)
+ {
+ // Establish a client to ping a Destination and listen the reply back from same Destination
+ perThreadSetup._pingClient = new PingClient(testParameters);
+ perThreadSetup._pingClient.establishConnection(true, true);
+ }
+ // Start the client connection
+ perThreadSetup._pingClient.start();
+
+ // Attach the per-thread set to the thread.
+ threadSetup.set(perThreadSetup);
+ }
+ catch (Exception e)
+ {
+ _logger.warn("There was an exception during per thread setup.", e);
+ }
+ }
+
+ /**
+ * Performs test fixture clean
+ */
+ public void threadTearDown()
+ {
+ _logger.debug("public void threadTearDown(): called");
+
+ try
+ {
+ // Get the per thread test fixture.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+
+ // Close the pingers so that it cleans up its connection cleanly.
+ synchronized (this)
+ {
+ if ((perThreadSetup != null) && (perThreadSetup._pingClient != null))
+ {
+ perThreadSetup._pingClient.close();
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.warn("There was an exception during per thread tear down.");
+ }
+ finally
+ {
+ // Ensure the per thread fixture is reclaimed.
+ threadSetup.remove();
+ }
+ }
+
+ protected static class PerThreadSetup
+ {
+ /**
+ * Holds the test ping client.
+ */
+ protected PingClient _pingClient;
+ protected String _correlationId;
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java b/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java
new file mode 100644
index 0000000000..82b36bf233
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java
@@ -0,0 +1,453 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.requestreply;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.jms.*;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.jms.ConnectionListener;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.topic.Config;
+import org.apache.qpid.exchange.ExchangeDefaults;
+
+/**
+ * PingPongBouncer is a message listener the bounces back messages to their reply to destination. This is used to return
+ * ping messages generated by {@link org.apache.qpid.requestreply.PingPongProducer} but could be used for other purposes
+ * too.
+ *
+ * <p/>The correlation id from the received message is extracted, and placed into the reply as the correlation id. Messages
+ * are bounced back to their reply-to destination. The original sender of the message has the option to use either a unique
+ * temporary queue or the correlation id to correlate the original message to the reply.
+ *
+ * <p/>There is a verbose mode flag which causes information about each ping to be output to the console
+ * (info level logging, so usually console). This can be helpfull to check the bounce backs are happening but should
+ * be disabled for real timing tests as writing to the console will slow things down.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Bounce back messages to their reply to destination.
+ * <tr><td> Provide command line invocation to start the bounce back on a configurable broker url.
+ * </table>
+ *
+ * @todo Replace the command line parsing with a neater tool.
+ *
+ * @todo Make verbose accept a number of messages, only prints to console every X messages.
+ */
+public class PingPongBouncer implements MessageListener
+{
+ private static final Logger _logger = Logger.getLogger(PingPongBouncer.class);
+
+ /** The default prefetch size for the message consumer. */
+ private static final int PREFETCH = 1;
+
+ /** The default no local flag for the message consumer. */
+ private static final boolean NO_LOCAL = true;
+
+ private static final String DEFAULT_DESTINATION_NAME = "ping";
+
+ /** The default exclusive flag for the message consumer. */
+ private static final boolean EXCLUSIVE = false;
+
+ /** A convenient formatter to use when time stamping output. */
+ protected static final SimpleDateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS");
+
+ /** Used to indicate that the reply generator should log timing info to the console (logger info level). */
+ private boolean _verbose = false;
+
+ /** Determines whether this bounce back client bounces back messages persistently. */
+ private boolean _persistent = false;
+
+ private Destination _consumerDestination;
+
+ /** Keeps track of the response destination of the previous message for the last reply to producer cache. */
+ private Destination _lastResponseDest;
+
+ /** The producer for sending replies with. */
+ private MessageProducer _replyProducer;
+
+ /** The consumer controlSession. */
+ private Session _consumerSession;
+
+ /** The producer controlSession. */
+ private Session _producerSession;
+
+ /** Holds the connection to the broker. */
+ private AMQConnection _connection;
+
+ /** Flag used to indicate if this is a point to point or pub/sub ping client. */
+ private boolean _isPubSub = false;
+
+ /**
+ * This flag is used to indicate that the user should be prompted to kill a broker, in order to test
+ * failover, immediately before committing a transaction.
+ */
+ protected boolean _failBeforeCommit = false;
+
+ /**
+ * This flag is used to indicate that the user should be prompted to a kill a broker, in order to test
+ * failover, immediate after committing a transaction.
+ */
+ protected boolean _failAfterCommit = false;
+
+ /**
+ * Creates a PingPongBouncer on the specified producer and consumer sessions.
+ *
+ * @param brokerDetails The addresses of the brokers to connect to.
+ * @param username The broker username.
+ * @param password The broker password.
+ * @param virtualpath The virtual host name within the broker.
+ * @param destinationName The name of the queue to receive pings on
+ * (or root of the queue name where many queues are generated).
+ * @param persistent A flag to indicate that persistent message should be used.
+ * @param transacted A flag to indicate that pings should be sent within transactions.
+ * @param selector A message selector to filter received pings with.
+ * @param verbose A flag to indicate that message timings should be sent to the console.
+ *
+ * @throws Exception All underlying exceptions allowed to fall through. This is only test code...
+ */
+ public PingPongBouncer(String brokerDetails, String username, String password, String virtualpath,
+ String destinationName, boolean persistent, boolean transacted, String selector, boolean verbose,
+ boolean pubsub) throws Exception
+ {
+ // Create a client id to uniquely identify this client.
+ InetAddress address = InetAddress.getLocalHost();
+ String clientId = address.getHostName() + System.currentTimeMillis();
+ _verbose = verbose;
+ _persistent = persistent;
+ setPubSub(pubsub);
+ // Connect to the broker.
+ setConnection(new AMQConnection(brokerDetails, username, password, clientId, virtualpath));
+ _logger.info("Connected with URL:" + getConnection().toURL());
+
+ // Set up the failover notifier.
+ getConnection().setConnectionListener(new FailoverNotifier());
+
+ // Create a controlSession to listen for messages on and one to send replies on, transactional depending on the
+ // command line option.
+ _consumerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE);
+ _producerSession = (Session) getConnection().createSession(transacted, Session.AUTO_ACKNOWLEDGE);
+
+ // Create the queue to listen for message on.
+ createConsumerDestination(destinationName);
+ MessageConsumer consumer =
+ _consumerSession.createConsumer(_consumerDestination, PREFETCH, NO_LOCAL, EXCLUSIVE, selector);
+
+ // Create a producer for the replies, without a default destination.
+ _replyProducer = _producerSession.createProducer(null);
+ _replyProducer.setDisableMessageTimestamp(true);
+ _replyProducer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
+
+ // Set this up to listen for messages on the queue.
+ consumer.setMessageListener(this);
+ }
+
+ /**
+ * Starts a stand alone ping-pong client running in verbose mode.
+ *
+ * @param args
+ */
+ public static void main(String[] args) throws Exception
+ {
+ System.out.println("Starting...");
+
+ // Display help on the command line.
+ if (args.length == 0)
+ {
+ _logger.info("Running test with default values...");
+ //usage();
+ //System.exit(0);
+ }
+
+ // Extract all command line parameters.
+ Config config = new Config();
+ config.setOptions(args);
+ String brokerDetails = config.getHost() + ":" + config.getPort();
+ String virtualpath = "test";
+ String destinationName = config.getDestination();
+ if (destinationName == null)
+ {
+ destinationName = DEFAULT_DESTINATION_NAME;
+ }
+
+ String selector = config.getSelector();
+ boolean transacted = config.isTransacted();
+ boolean persistent = config.usePersistentMessages();
+ boolean pubsub = config.isPubSub();
+ boolean verbose = true;
+
+ //String selector = null;
+
+ // Instantiate the ping pong client with the command line options and start it running.
+ PingPongBouncer pingBouncer =
+ new PingPongBouncer(brokerDetails, "guest", "guest", virtualpath, destinationName, persistent, transacted,
+ selector, verbose, pubsub);
+ pingBouncer.getConnection().start();
+
+ System.out.println("Waiting...");
+ }
+
+ private static void usage()
+ {
+ System.err.println("Usage: PingPongBouncer \n" + "-host : broker host\n" + "-port : broker port\n"
+ + "-destinationname : queue/topic name\n" + "-transacted : (true/false). Default is false\n"
+ + "-persistent : (true/false). Default is false\n"
+ + "-pubsub : (true/false). Default is false\n" + "-selector : selector string\n");
+ }
+
+ /**
+ * This is a callback method that is notified of all messages for which this has been registered as a message
+ * listener on a message consumer. It sends a reply (pong) to all messages it receieves on the reply to
+ * destination of the message.
+ *
+ * @param message The message that triggered this callback.
+ */
+ public void onMessage(Message message)
+ {
+ try
+ {
+ String messageCorrelationId = message.getJMSCorrelationID();
+ if (_verbose)
+ {
+ _logger.info(timestampFormatter.format(new Date()) + ": Got ping with correlation id, "
+ + messageCorrelationId);
+ }
+
+ // Get the reply to destination from the message and check it is set.
+ Destination responseDest = message.getJMSReplyTo();
+
+ if (responseDest == null)
+ {
+ _logger.debug("Cannot send reply because reply-to destination is null.");
+
+ return;
+ }
+
+ // Spew out some timing information if verbose mode is on.
+ if (_verbose)
+ {
+ Long timestamp = message.getLongProperty("timestamp");
+
+ if (timestamp != null)
+ {
+ long diff = System.currentTimeMillis() - timestamp;
+ _logger.info("Time to bounce point: " + diff);
+ }
+ }
+
+ // Correlate the reply to the original.
+ message.setJMSCorrelationID(messageCorrelationId);
+
+ // Send the receieved message as the pong reply.
+ _replyProducer.send(responseDest, message);
+
+ if (_verbose)
+ {
+ _logger.info(timestampFormatter.format(new Date()) + ": Sent reply with correlation id, "
+ + messageCorrelationId);
+ }
+
+ // Commit the transaction if running in transactional mode.
+ commitTx(_producerSession);
+ }
+ catch (JMSException e)
+ {
+ _logger.debug("There was a JMSException: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Gets the underlying connection that this ping client is running on.
+ *
+ * @return The underlying connection that this ping client is running on.
+ */
+ public AMQConnection getConnection()
+ {
+ return _connection;
+ }
+
+ /**
+ * Sets the connection that this ping client is using.
+ *
+ * @param connection The ping connection.
+ */
+ public void setConnection(AMQConnection connection)
+ {
+ this._connection = connection;
+ }
+
+ /**
+ * Sets or clears the pub/sub flag to indiciate whether this client is pinging a queue or a topic.
+ *
+ * @param pubsub <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue.
+ */
+ public void setPubSub(boolean pubsub)
+ {
+ _isPubSub = pubsub;
+ }
+
+ /**
+ * Checks whether this client is a p2p or pub/sub ping client.
+ *
+ * @return <tt>true</tt> if this client is pinging a topic, <tt>false</tt> if it is pinging a queue.
+ */
+ public boolean isPubSub()
+ {
+ return _isPubSub;
+ }
+
+ /**
+ * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not
+ * a transactional controlSession, this method does nothing.
+ *
+ * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the
+ * commit is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker
+ * after the commit is applied.
+ *
+ * @throws javax.jms.JMSException If the commit fails and then the rollback fails.
+ */
+ protected void commitTx(Session session) throws JMSException
+ {
+ if (session.getTransacted())
+ {
+ try
+ {
+ if (_failBeforeCommit)
+ {
+ _logger.trace("Failing Before Commit");
+ doFailover();
+ }
+
+ session.commit();
+
+ if (_failAfterCommit)
+ {
+ _logger.trace("Failing After Commit");
+ doFailover();
+ }
+
+ _logger.trace("Session Commited.");
+ }
+ catch (JMSException e)
+ {
+ _logger.trace("JMSException on commit:" + e.getMessage(), e);
+
+ try
+ {
+ session.rollback();
+ _logger.debug("Message rolled back.");
+ }
+ catch (JMSException jmse)
+ {
+ _logger.trace("JMSE on rollback:" + jmse.getMessage(), jmse);
+
+ // Both commit and rollback failed. Throw the rollback exception.
+ throw jmse;
+ }
+ }
+ }
+ }
+
+ /**
+ * Prompts the user to terminate the named broker, in order to test failover functionality. This method will block
+ * until the user supplied some input on the terminal.
+ *
+ * @param broker The name of the broker to terminate.
+ */
+ protected void doFailover(String broker)
+ {
+ System.out.println("Kill Broker " + broker + " now.");
+ try
+ {
+ System.in.read();
+ }
+ catch (IOException e)
+ { }
+
+ System.out.println("Continuing.");
+ }
+
+ /**
+ * Prompts the user to terminate the broker, in order to test failover functionality. This method will block
+ * until the user supplied some input on the terminal.
+ */
+ protected void doFailover()
+ {
+ System.out.println("Kill Broker now.");
+ try
+ {
+ System.in.read();
+ }
+ catch (IOException e)
+ { }
+
+ System.out.println("Continuing.");
+
+ }
+
+ private void createConsumerDestination(String name)
+ {
+ if (isPubSub())
+ {
+ _consumerDestination = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, name);
+ }
+ else
+ {
+ _consumerDestination = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, name);
+ }
+ }
+
+ /**
+ * A connection listener that logs out any failover complete events. Could do more interesting things with this
+ * at some point...
+ */
+ public static class FailoverNotifier implements ConnectionListener
+ {
+ public void bytesSent(long count)
+ { }
+
+ public void bytesReceived(long count)
+ { }
+
+ public boolean preFailover(boolean redirect)
+ {
+ return true;
+ }
+
+ public boolean preResubscribe()
+ {
+ return true;
+ }
+
+ public void failoverComplete()
+ {
+ _logger.info("App got failover complete callback.");
+ }
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java b/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java
new file mode 100644
index 0000000000..99ed9f8367
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java
@@ -0,0 +1,1688 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.requestreply;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.*;
+import org.apache.qpid.client.message.AMQMessage;
+import org.apache.qpid.client.message.TestMessageFactory;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.jms.MessageProducer;
+import org.apache.qpid.jms.Session;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.util.CommandLineParser;
+
+import uk.co.thebadgerset.junit.extensions.BatchedThrottle;
+import uk.co.thebadgerset.junit.extensions.Throttle;
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+
+import javax.jms.*;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * PingPongProducer is a client that sends test messages, and waits for replies to these messages. The replies may
+ * either be generated by another client (see {@link PingPongBouncer}, or an extension of it may be used that listens
+ * to its own messages and does not send replies (see {@link org.apache.qpid.ping.PingClient}). The intention of ping
+ * pong producer is that it is a swiss-army knife test client that makes almost every aspect of its behaviour
+ * configurable.
+ *
+ * <p/>The pings are sent with a reply-to field set to a single temporary queue, which is the same for all pings. This
+ * means that this class has to do some work to correlate pings with pongs; it expectes the original message correlation
+ * id in the ping to be bounced back in the reply correlation id.
+ *
+ * <p/>This ping tool accepts a vast number of configuration options, all of which are passed in to the constructor. It
+ * can ping topics or queues; ping multiple destinations; do persistent pings; send messages of any size; do pings within
+ * transactions; control the number of pings to send in each transaction; limit its sending rate; and perform failover
+ * testing. A complete list of accepted parameters, default values and comments on their usage is provided here:
+ *
+ * <p/><table><caption>Parameters</caption>
+ * <tr><th> Parameter <th> Default <th> Comments
+ * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers.
+ * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping.
+ * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used.
+ * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions.
+ * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to.
+ * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over.
+ * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit.
+ * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message.
+ * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default.
+ * <tr><td> failAfterCommit <td> false <td> Whether to prompt user to kill broker after a commit batch.
+ * <tr><td> failBeforeCommit <td> false <td> Whether to prompt user to kill broker before a commit batch.
+ * <tr><td> failAfterSend <td> false <td> Whether to prompt user to kill broker after a send.
+ * <tr><td> failBeforeSend <td> false <td> Whether to prompt user to kill broker before a send.
+ * <tr><td> failOnce <td> true <td> Whether to prompt for failover only once.
+ * <tr><td> username <td> guest <td> The username to access the broker with.
+ * <tr><td> password <td> guest <td> The password to access the broker with.
+ * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with.
+ * <tr><td> destinationCount <td> 1 <td> The number of destinations to send pings to.
+ * <tr><td> numConsumers <td> 1 <td> The number of consumers on each destination.
+ * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies.
+ * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode.
+ * <tr><td> uniqueDests <td> true <td> Whether each receivers only listens to one ping destination or all.
+ * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used.
+ * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are:
+ * 0 - SESSION_TRANSACTED
+ * 1 - AUTO_ACKNOWLEDGE
+ * 2 - CLIENT_ACKNOWLEDGE
+ * 3 - DUPS_OK_ACKNOWLEDGE
+ * 257 - NO_ACKNOWLEDGE
+ * 258 - PRE_ACKNOWLEDGE
+ * <tr><td> consTransacted <td> false <td> Whether or not consumers use transactions. Defaults to the same value
+ * as the 'transacted' option if not seperately defined.
+ * <tr><td> consAckMode <td> AUTO_ACK <td> The message acknowledgement mode for consumers. Defaults to the same
+ * value as 'ackMode' if not seperately defined.
+ * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received.
+ * Limits the volume of messages currently buffered on the client
+ * or broker. Can help scale test clients by limiting amount of buffered
+ * data to avoid out of memory errors.
+ * </table>
+ *
+ * <p/>This implements the Runnable interface with a run method that implements an infinite ping loop. The ping loop
+ * does all its work through helper methods, so that code wishing to run a ping-pong cycle is not forced to do so by
+ * starting a new thread. The command line invocation does take advantage of this ping loop. A shutdown hook is also
+ * registered to terminate the ping-pong loop cleanly.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a ping and wait for all responses cycle.
+ * <tr><td> Provide command line invocation to loop the ping cycle on a configurable broker url.
+ * </table>
+ *
+ * @todo Use read/write lock in the onmessage, not for reading writing but to make use of a shared and exlcusive lock pair.
+ * Obtain read lock on all messages, before decrementing the message count. At the end of the on message method add a
+ * block that obtains the write lock for the very last message, releases any waiting producer. Means that the last
+ * message waits until all other messages have been handled before releasing producers but allows messages to be
+ * processed concurrently, unlike the current synchronized block.
+ */
+public class PingPongProducer implements Runnable /*, MessageListener*/, ExceptionListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(PingPongProducer.class);
+
+ /** Holds the name of the property to get the test message size from. */
+ public static final String MESSAGE_SIZE_PROPNAME = "messageSize";
+
+ /** Used to set up a default message size. */
+ public static final int MESSAGE_SIZE_DEAFULT = 0;
+
+ /** Holds the name of the property to get the ping queue name from. */
+ public static final String PING_QUEUE_NAME_PROPNAME = "destinationName";
+
+ /** Holds the name of the default destination to send pings on. */
+ public static final String PING_QUEUE_NAME_DEFAULT = "ping";
+
+ /** Holds the name of the property to get the test delivery mode from. */
+ public static final String PERSISTENT_MODE_PROPNAME = "persistent";
+
+ /** Holds the message delivery mode to use for the test. */
+ public static final boolean PERSISTENT_MODE_DEFAULT = false;
+
+ /** Holds the name of the property to get the test transactional mode from. */
+ public static final String TRANSACTED_PROPNAME = "transacted";
+
+ /** Holds the transactional mode to use for the test. */
+ public static final boolean TRANSACTED_DEFAULT = false;
+
+ /** Holds the name of the property to get the test consumer transacted mode from. */
+ public static final String CONSUMER_TRANSACTED_PROPNAME = "consTransacted";
+
+ /** Holds the consumer transactional mode default setting. */
+ public static final boolean CONSUMER_TRANSACTED_DEFAULT = false;
+
+ /** Holds the name of the property to get the test broker url from. */
+ public static final String BROKER_PROPNAME = "broker";
+
+ /** Holds the default broker url for the test. */
+ public static final String BROKER_DEFAULT = "tcp://localhost:5672";
+
+ /** Holds the name of the property to get the test broker virtual path. */
+ public static final String VIRTUAL_HOST_PROPNAME = "virtualHost";
+
+ /** Holds the default virtual path for the test. */
+ public static final String VIRTUAL_HOST_DEFAULT = "";
+
+ /** Holds the name of the property to get the message rate from. */
+ public static final String RATE_PROPNAME = "rate";
+
+ /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */
+ public static final int RATE_DEFAULT = 0;
+
+ /** Holds the name of the property to get the verbose mode proeprty from. */
+ public static final String VERBOSE_PROPNAME = "verbose";
+
+ /** Holds the default verbose mode. */
+ public static final boolean VERBOSE_DEFAULT = false;
+
+ /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */
+ public static final String PUBSUB_PROPNAME = "pubsub";
+
+ /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */
+ public static final boolean PUBSUB_DEFAULT = false;
+
+ /** Holds the name of the property to get the fail after commit flag from. */
+ public static final String FAIL_AFTER_COMMIT_PROPNAME = "failAfterCommit";
+
+ /** Holds the default failover after commit test flag. */
+ public static final boolean FAIL_AFTER_COMMIT_DEFAULT = false;
+
+ /** Holds the name of the proeprty to get the fail before commit flag from. */
+ public static final String FAIL_BEFORE_COMMIT_PROPNAME = "failBeforeCommit";
+
+ /** Holds the default failover before commit test flag. */
+ public static final boolean FAIL_BEFORE_COMMIT_DEFAULT = false;
+
+ /** Holds the name of the proeprty to get the fail after send flag from. */
+ public static final String FAIL_AFTER_SEND_PROPNAME = "failAfterSend";
+
+ /** Holds the default failover after send test flag. */
+ public static final boolean FAIL_AFTER_SEND_DEFAULT = false;
+
+ /** Holds the name of the property to get the fail before send flag from. */
+ public static final String FAIL_BEFORE_SEND_PROPNAME = "failBeforeSend";
+
+ /** Holds the default failover before send test flag. */
+ public static final boolean FAIL_BEFORE_SEND_DEFAULT = false;
+
+ /** Holds the name of the property to get the fail once flag from. */
+ public static final String FAIL_ONCE_PROPNAME = "failOnce";
+
+ /** The default failover once flag, true means only do one failover, false means failover on every commit cycle. */
+ public static final boolean FAIL_ONCE_DEFAULT = true;
+
+ /** Holds the name of the property to get the broker access username from. */
+ public static final String USERNAME_PROPNAME = "username";
+
+ /** Holds the default broker log on username. */
+ public static final String USERNAME_DEFAULT = "guest";
+
+ /** Holds the name of the property to get the broker access password from. */
+ public static final String PASSWORD_PROPNAME = "password";
+
+ /** Holds the default broker log on password. */
+ public static final String PASSWORD_DEFAULT = "guest";
+
+ /** Holds the name of the proeprty to get the. */
+ public static final String SELECTOR_PROPNAME = "selector";
+
+ /** Holds the default message selector. */
+ public static final String SELECTOR_DEFAULT = "";
+
+ /** Holds the name of the property to get the destination count from. */
+ public static final String DESTINATION_COUNT_PROPNAME = "destinationCount";
+
+ /** Defines the default number of destinations to ping. */
+ public static final int DESTINATION_COUNT_DEFAULT = 1;
+
+ /** Holds the name of the property to get the number of consumers per destination from. */
+ public static final String NUM_CONSUMERS_PROPNAME = "numConsumers";
+
+ /** Defines the default number consumers per destination. */
+ public static final int NUM_CONSUMERS_DEFAULT = 1;
+
+ /** Holds the name of the property to get the waiting timeout for response messages. */
+ public static final String TIMEOUT_PROPNAME = "timeout";
+
+ /** Default time to wait before assuming that a ping has timed out. */
+ public static final long TIMEOUT_DEFAULT = 30000;
+
+ /** Holds the name of the property to get the commit batch size from. */
+ public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize";
+
+ /** Defines the default number of pings to send in each transaction when running transactionally. */
+ public static final int TX_BATCH_SIZE_DEFAULT = 1;
+
+ /** Holds the name of the property to get the unique destinations flag from. */
+ public static final String UNIQUE_DESTS_PROPNAME = "uniqueDests";
+
+ /** Defines the default value for the unique destinations property. */
+ public static final boolean UNIQUE_DESTS_DEFAULT = true;
+
+ /** Holds the name of the property to get the durable destinations flag from. */
+ public static final String DURABLE_DESTS_PROPNAME = "durableDests";
+
+ /** Defines the default value of the durable destinations flag. */
+ public static final boolean DURABLE_DESTS_DEFAULT = false;
+
+ /** Holds the name of the proeprty to get the message acknowledgement mode from. */
+ public static final String ACK_MODE_PROPNAME = "ackMode";
+
+ /** Defines the default message acknowledgement mode. */
+ public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE;
+
+ /** Holds the name of the property to get the consumers message acknowledgement mode from. */
+ public static final String CONSUMER_ACK_MODE_PROPNAME = "consAckMode";
+
+ /** Defines the default consumers message acknowledgement mode. */
+ public static final int CONSUMER_ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE;
+
+ /** Holds the name of the property to get the maximum pending message size setting from. */
+ public static final String MAX_PENDING_PROPNAME = "maxPending";
+
+ /** Defines the default value for the maximum pending message size setting. 0 means no limit. */
+ public static final int MAX_PENDING_DEFAULT = 0;
+
+ /** Defines the default prefetch size to use when consuming messages. */
+ public static final int PREFETCH_DEFAULT = 100;
+
+ /** Defines the default value of the no local flag to use when consuming messages. */
+ public static final boolean NO_LOCAL_DEFAULT = false;
+
+ /** Defines the default value of the exclusive flag to use when consuming messages. */
+ public static final boolean EXCLUSIVE_DEFAULT = false;
+
+ /** Holds the name of the property to store nanosecond timestamps in ping messages with. */
+ public static final String MESSAGE_TIMESTAMP_PROPNAME = "timestamp";
+
+ /** Holds the default configuration properties. */
+ public static ParsedProperties defaults = new ParsedProperties();
+
+ static
+ {
+ defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT);
+ defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT);
+ defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT);
+ defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT);
+ defaults.setPropertyIfNull(PING_QUEUE_NAME_PROPNAME, PING_QUEUE_NAME_DEFAULT);
+ defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT);
+ defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT);
+ defaults.setPropertyIfNull(CONSUMER_TRANSACTED_PROPNAME, CONSUMER_TRANSACTED_DEFAULT);
+ defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT);
+ defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT);
+ defaults.setPropertyIfNull(CONSUMER_ACK_MODE_PROPNAME, CONSUMER_ACK_MODE_DEFAULT);
+ defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT);
+ defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT);
+ defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT);
+ defaults.setPropertyIfNull(UNIQUE_DESTS_PROPNAME, UNIQUE_DESTS_DEFAULT);
+ defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT);
+ defaults.setPropertyIfNull(FAIL_BEFORE_COMMIT_PROPNAME, FAIL_BEFORE_COMMIT_DEFAULT);
+ defaults.setPropertyIfNull(FAIL_AFTER_COMMIT_PROPNAME, FAIL_AFTER_COMMIT_DEFAULT);
+ defaults.setPropertyIfNull(FAIL_BEFORE_SEND_PROPNAME, FAIL_BEFORE_SEND_DEFAULT);
+ defaults.setPropertyIfNull(FAIL_AFTER_SEND_PROPNAME, FAIL_AFTER_SEND_DEFAULT);
+ defaults.setPropertyIfNull(FAIL_ONCE_PROPNAME, FAIL_ONCE_DEFAULT);
+ defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT);
+ defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT);
+ defaults.setPropertyIfNull(NUM_CONSUMERS_PROPNAME, NUM_CONSUMERS_DEFAULT);
+ defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT);
+ defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT);
+ defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT);
+ }
+
+ /** Holds the broker url. */
+ protected String _brokerDetails;
+
+ /** Holds the username to access the broker with. */
+ protected String _username;
+
+ /** Holds the password to access the broker with. */
+ protected String _password;
+
+ /** Holds the virtual host on the broker to run the tests through. */
+ protected String _virtualpath;
+
+ /** Holds the root name from which to generate test destination names. */
+ protected String _destinationName;
+
+ /** Holds the message selector to filter the pings with. */
+ protected String _selector;
+
+ /** Holds the producers transactional mode flag. */
+ protected boolean _transacted;
+
+ /** Holds the consumers transactional mode flag. */
+ protected boolean _consTransacted;
+
+ /** Determines whether this producer sends persistent messages. */
+ protected boolean _persistent;
+
+ /** Holds the acknowledgement mode used for the producers. */
+ protected int _ackMode;
+
+ /** Holds the acknowledgement mode setting for the consumers. */
+ protected int _consAckMode;
+
+ /** Determines what size of messages this producer sends. */
+ protected int _messageSize;
+
+ /** Used to indicate that the ping loop should print out whenever it pings. */
+ protected boolean _verbose;
+
+ /** Flag used to indicate if this is a point to point or pub/sub ping client. */
+ protected boolean _isPubSub;
+
+ /** Flag used to indicate if the destinations should be unique client. */
+ protected boolean _isUnique;
+
+ /** Flag used to indicate that durable destination should be used. */
+ protected boolean _isDurable;
+
+ /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a commit. */
+ protected boolean _failBeforeCommit;
+
+ /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a commit. */
+ protected boolean _failAfterCommit;
+
+ /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover before a send. */
+ protected boolean _failBeforeSend;
+
+ /** Flag used to indicate that the user should be prompted to terminate a broker, to test failover after a send. */
+ protected boolean _failAfterSend;
+
+ /** Flag used to indicate that failover prompting should only be done on the first commit, not on every commit. */
+ protected boolean _failOnce;
+
+ /** Holds the number of sends that should be performed in every transaction when using transactions. */
+ protected int _txBatchSize;
+
+ /** Holds the number of destinations to ping. */
+ protected int _noOfDestinations;
+
+ /** Holds the number of consumers per destination. */
+ protected int _noOfConsumers;
+
+ /** Holds the maximum send rate in herz. */
+ protected int _rate;
+
+ /**
+ * Holds the size of the maximum amount of pending data that the client should buffer, sending is suspended
+ * if this limit is breached.
+ */
+ protected int _maxPendingSize;
+
+ /** A source for providing sequential unique correlation ids. These will be unique within the same JVM. */
+ private static AtomicLong _correlationIdGenerator = new AtomicLong(0L);
+
+ /** A source for providing sequential unqiue ids for instances of this class to be identifed with. */
+ private static AtomicInteger _instanceIdGenerator = new AtomicInteger(0);
+
+ /** Holds this instances unique id. */
+ private int instanceId;
+
+ /**
+ * Holds a map from message ids to latches on which threads wait for replies. This map is shared accross multiple
+ * ping producers on the same JVM.
+ */
+ private static Map<String, PerCorrelationId> perCorrelationIds =
+ Collections.synchronizedMap(new HashMap<String, PerCorrelationId>());
+
+ /** A convenient formatter to use when time stamping output. */
+ protected static final DateFormat timestampFormatter = new SimpleDateFormat("hh:mm:ss:SS");
+
+ /** Holds the connection for the message producer. */
+ protected Connection _connection;
+
+ /** Holds the consumer connections. */
+ protected Connection[] _consumerConnection;
+
+ /** Holds the controlSession on which ping replies are received. */
+ protected Session[] _consumerSession;
+
+ /** Holds the producer controlSession, needed to create ping messages. */
+ protected Session _producerSession;
+
+ /** Holds the destination where the response messages will arrive. */
+ protected Destination _replyDestination;
+
+ /** Holds the set of destinations that this ping producer pings. */
+ protected List<Destination> _pingDestinations;
+
+ /** Used to restrict the sending rate to a specified limit. */
+ protected Throttle _rateLimiter;
+
+ /** Holds a message listener that this message listener chains all its messages to. */
+ protected ChainedMessageListener _chainedMessageListener = null;
+
+ /**
+ * This id generator is used to generate ids to append to the queue name to ensure that queues can be unique when
+ * creating multiple ping producers in the same JVM.
+ */
+ protected static AtomicInteger _queueJVMSequenceID = new AtomicInteger();
+
+ /**
+ * This id generator is used to generates ids that are only unique within this pinger. Creating multiple pingers
+ * on the same JVM using this id generator will allow them to ping on the same queues.
+ */
+ protected AtomicInteger _queueSharedID = new AtomicInteger();
+
+ /** Used to tell the ping loop when to terminate, it only runs while this is true. */
+ protected boolean _publish = true;
+
+ /** Holds the message producer to send the pings through. */
+ protected MessageProducer _producer;
+
+ /** Holds the message consumer to receive the ping replies through. */
+ protected MessageConsumer[] _consumer;
+
+ /** The prompt to display when asking the user to kill the broker for failover testing. */
+ private static final String KILL_BROKER_PROMPT = "Kill broker now, then press Return.";
+
+ /** Holds the name for this test client to be identified to the broker with. */
+ private String _clientID;
+
+ /** Keeps count of the total messages sent purely for debugging purposes. */
+ private static AtomicInteger numSent = new AtomicInteger();
+
+ /**
+ * Holds a monitor which is used to synchronize sender and receivers threads, where the sender has elected
+ * to wait until the number of unreceived message is reduced before continuing to send. This monitor is a
+ * fair SynchronousQueue becuase that provides fair scheduling, to ensure that all producer threads get an
+ * equal chance to produce messages.
+ */
+ static final SynchronousQueue _sendPauseMonitor = new SynchronousQueue(true);
+
+ /** Keeps a count of the number of message currently sent but not received. */
+ static AtomicInteger _unreceived = new AtomicInteger(0);
+
+ /**
+ * Creates a ping producer with the specified parameters, of which there are many. See the class level comments
+ * for details. This constructor creates a connection to the broker and creates producer and consumer sessions on
+ * it, to send and recieve its pings and replies on.
+ *
+ * @param overrides Properties containing any desired overrides to the defaults.
+ *
+ * @throws Exception Any exceptions are allowed to fall through.
+ */
+ public PingPongProducer(Properties overrides) throws Exception
+ {
+ // log.debug("public PingPongProducer(Properties overrides = " + overrides + "): called");
+ instanceId = _instanceIdGenerator.getAndIncrement();
+
+ // Create a set of parsed properties from the defaults overriden by the passed in values.
+ ParsedProperties properties = new ParsedProperties(defaults);
+ properties.putAll(overrides);
+
+ // Extract the configuration properties to set the pinger up with.
+ _brokerDetails = properties.getProperty(BROKER_PROPNAME);
+ _username = properties.getProperty(USERNAME_PROPNAME);
+ _password = properties.getProperty(PASSWORD_PROPNAME);
+ _virtualpath = properties.getProperty(VIRTUAL_HOST_PROPNAME);
+ _destinationName = properties.getProperty(PING_QUEUE_NAME_PROPNAME);
+ _selector = properties.getProperty(SELECTOR_PROPNAME);
+ _transacted = properties.getPropertyAsBoolean(TRANSACTED_PROPNAME);
+ _consTransacted = properties.getPropertyAsBoolean(CONSUMER_TRANSACTED_PROPNAME);
+ _persistent = properties.getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME);
+ _messageSize = properties.getPropertyAsInteger(MESSAGE_SIZE_PROPNAME);
+ _verbose = properties.getPropertyAsBoolean(VERBOSE_PROPNAME);
+ _failAfterCommit = properties.getPropertyAsBoolean(FAIL_AFTER_COMMIT_PROPNAME);
+ _failBeforeCommit = properties.getPropertyAsBoolean(FAIL_BEFORE_COMMIT_PROPNAME);
+ _failAfterSend = properties.getPropertyAsBoolean(FAIL_AFTER_SEND_PROPNAME);
+ _failBeforeSend = properties.getPropertyAsBoolean(FAIL_BEFORE_SEND_PROPNAME);
+ _failOnce = properties.getPropertyAsBoolean(FAIL_ONCE_PROPNAME);
+ _txBatchSize = properties.getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME);
+ _noOfDestinations = properties.getPropertyAsInteger(DESTINATION_COUNT_PROPNAME);
+ _noOfConsumers = properties.getPropertyAsInteger(NUM_CONSUMERS_PROPNAME);
+ _rate = properties.getPropertyAsInteger(RATE_PROPNAME);
+ _isPubSub = properties.getPropertyAsBoolean(PUBSUB_PROPNAME);
+ _isUnique = properties.getPropertyAsBoolean(UNIQUE_DESTS_PROPNAME);
+ _isDurable = properties.getPropertyAsBoolean(DURABLE_DESTS_PROPNAME);
+ _ackMode = _transacted ? 0 : properties.getPropertyAsInteger(ACK_MODE_PROPNAME);
+ _consAckMode = _consTransacted ? 0 : properties.getPropertyAsInteger(CONSUMER_ACK_MODE_PROPNAME);
+ _maxPendingSize = properties.getPropertyAsInteger(MAX_PENDING_PROPNAME);
+
+ // Check that one or more destinations were specified.
+ if (_noOfDestinations < 1)
+ {
+ throw new IllegalArgumentException("There must be at least one destination.");
+ }
+
+ // Set up a throttle to control the send rate, if a rate > 0 is specified.
+ if (_rate > 0)
+ {
+ _rateLimiter = new BatchedThrottle();
+ _rateLimiter.setRate(_rate);
+ }
+
+ // Create the connection and message producers/consumers.
+ // establishConnection(true, true);
+ }
+
+ /**
+ * Establishes a connection to the broker and creates message consumers and producers based on the parameters
+ * that this ping client was created with.
+ *
+ * @param producer Flag to indicate whether or not the producer should be set up.
+ * @param consumer Flag to indicate whether or not the consumers should be set up.
+ *
+ * @throws Exception Any exceptions are allowed to fall through.
+ */
+ public void establishConnection(boolean producer, boolean consumer) throws Exception
+ {
+ // log.debug("public void establishConnection(): called");
+
+ // Generate a unique identifying name for this client, based on it ip address and the current time.
+ InetAddress address = InetAddress.getLocalHost();
+ _clientID = address.getHostName() + System.currentTimeMillis();
+
+ // Create a connection to the broker.
+ createConnection(_clientID);
+
+ // Create transactional or non-transactional sessions, based on the command line arguments.
+ _producerSession = (Session) _connection.createSession(_transacted, _ackMode);
+
+ _consumerSession = new Session[_noOfConsumers];
+
+ for (int i = 0; i < _noOfConsumers; i++)
+ {
+ _consumerSession[i] = (Session) _consumerConnection[i].createSession(_consTransacted, _consAckMode);
+ }
+
+ // Create the destinations to send pings to and receive replies from.
+ _replyDestination = _consumerSession[0].createTemporaryQueue();
+ createPingDestinations(_noOfDestinations, _selector, _destinationName, _isUnique, _isDurable);
+
+ // Create the message producer only if instructed to.
+ if (producer)
+ {
+ createProducer();
+ }
+
+ // Create the message consumer only if instructed to.
+ if (consumer)
+ {
+ createReplyConsumers(getReplyDestinations(), _selector);
+ }
+ }
+
+ /**
+ * Establishes a connection to the broker, based on the configuration parameters that this ping client was
+ * created with.
+ *
+ * @param clientID The clients identifier.
+ *
+ * @throws AMQException Any underlying exceptions are allowed to fall through.
+ * @throws URLSyntaxException Any underlying exceptions are allowed to fall through.
+ */
+ protected void createConnection(String clientID) throws AMQException, URLSyntaxException
+ {
+ // log.debug("protected void createConnection(String clientID = " + clientID + "): called");
+
+ // log.debug("Creating a connection for the message producer.");
+
+ _connection = new AMQConnection(_brokerDetails, _username, _password, clientID, _virtualpath);
+
+ // log.debug("Creating " + _noOfConsumers + " connections for the consumers.");
+
+ _consumerConnection = new Connection[_noOfConsumers];
+
+ for (int i = 0; i < _noOfConsumers; i++)
+ {
+ _consumerConnection[i] = new AMQConnection(_brokerDetails, _username, _password, clientID, _virtualpath);
+ }
+ }
+
+ /**
+ * Starts a ping-pong loop running from the command line. The bounce back client {@link PingPongBouncer} also needs
+ * to be started to bounce the pings back again.
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ Properties options =
+ CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties());
+
+ // Create a ping producer overriding its defaults with all options passed on the command line.
+ PingPongProducer pingProducer = new PingPongProducer(options);
+ pingProducer.establishConnection(true, true);
+
+ // Start the ping producers dispatch thread running.
+ pingProducer._connection.start();
+
+ // Create a shutdown hook to terminate the ping-pong producer.
+ Runtime.getRuntime().addShutdownHook(pingProducer.getShutdownHook());
+
+ // Ensure that the ping pong producer is registered to listen for exceptions on the connection too.
+ pingProducer._connection.setExceptionListener(pingProducer);
+
+ // Create the ping loop thread and run it until it is terminated by the shutdown hook or exception.
+ Thread pingThread = new Thread(pingProducer);
+ pingThread.run();
+ pingThread.join();
+ }
+ catch (Exception e)
+ {
+ System.err.println(e.getMessage());
+ log.error("Top level handler caught execption.", e);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Convenience method for a short pause.
+ *
+ * @param sleepTime The time in milliseconds to pause for.
+ */
+ public static void pause(long sleepTime)
+ {
+ if (sleepTime > 0)
+ {
+ try
+ {
+ Thread.sleep(sleepTime);
+ }
+ catch (InterruptedException ie)
+ { }
+ }
+ }
+
+ /**
+ * Gets all the reply destinations (to listen for replies on). In this case this will just be the single reply to
+ * destination of this pinger.
+ *
+ * @return The single reply to destination of this pinger, wrapped in a list.
+ */
+ public List<Destination> getReplyDestinations()
+ {
+ // log.debug("public List<Destination> getReplyDestinations(): called");
+
+ List<Destination> replyDestinations = new ArrayList<Destination>();
+ replyDestinations.add(_replyDestination);
+
+ // log.debug("replyDestinations = " + replyDestinations);
+
+ return replyDestinations;
+ }
+
+ /**
+ * Creates the producer to send the pings on. This is created without a default destination. Its persistent delivery
+ * flag is set accoring the ping producer creation options.
+ *
+ * @throws JMSException Any JMSExceptions are allowed to fall through.
+ */
+ public void createProducer() throws JMSException
+ {
+ // log.debug("public void createProducer(): called");
+
+ _producer = (MessageProducer) _producerSession.createProducer(null);
+ _producer.setDeliveryMode(_persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
+
+ // log.debug("Created producer for " + (_persistent ? "persistent" : "non-persistent") + " messages.");
+ }
+
+ /**
+ * Creates consumers for the specified number of destinations. The destinations themselves are also created by this
+ * method.
+ *
+ * @param noOfDestinations The number of destinations to create consumers for.
+ * @param selector The message selector to filter the consumers with.
+ * @param rootName The root of the name, or actual name if only one is being created.
+ * @param unique <tt>true</tt> to make the destinations unique to this pinger, <tt>false</tt> to share the
+ * numbering with all pingers on the same JVM.
+ * @param durable If the destinations are durable topics.
+ *
+ * @throws JMSException Any JMSExceptions are allowed to fall through.
+ * @throws AMQException Any AMQExceptions are allowed to fall through.
+ */
+ public void createPingDestinations(int noOfDestinations, String selector, String rootName, boolean unique,
+ boolean durable) throws JMSException, AMQException
+ {
+ /*log.debug("public void createPingDestinations(int noOfDestinations = " + noOfDestinations + ", String selector = "
+ + selector + ", String rootName = " + rootName + ", boolean unique = " + unique + ", boolean durable = "
+ + durable + "): called");*/
+
+ _pingDestinations = new ArrayList<Destination>();
+
+ // Create the desired number of ping destinations and consumers for them.
+ // log.debug("Creating " + noOfDestinations + " destinations to ping.");
+
+ for (int i = 0; i < noOfDestinations; i++)
+ {
+ AMQDestination destination;
+
+ String id;
+
+ // Generate an id, unique within this pinger or to the whole JVM depending on the unique flag.
+ if (unique)
+ {
+ // log.debug("Creating unique destinations.");
+ id = "_" + _queueJVMSequenceID.incrementAndGet() + "_" + _connection.getClientID();
+ }
+ else
+ {
+ // log.debug("Creating shared destinations.");
+ id = "_" + _queueSharedID.incrementAndGet();
+ }
+
+ // Check if this is a pub/sub pinger, in which case create topics.
+ if (_isPubSub)
+ {
+ if (!durable)
+ {
+ destination = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, rootName + id);
+ // log.debug("Created non-durable topic " + destination);
+ }
+ else
+ {
+ destination =
+ AMQTopic.createDurableTopic(new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, rootName + id),
+ _clientID, (AMQConnection) _connection);
+ // log.debug("Created durable topic " + destination);
+ }
+ }
+ // Otherwise this is a p2p pinger, in which case create queues.
+ else
+ {
+ AMQShortString destinationName = new AMQShortString(rootName + id);
+ destination =
+ new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, destinationName, destinationName, false, false,
+ _isDurable);
+ ((AMQSession) _producerSession).createQueue(destinationName, false, _isDurable, false);
+ ((AMQSession) _producerSession).bindQueue(destinationName, destinationName, null,
+ ExchangeDefaults.DIRECT_EXCHANGE_NAME);
+
+ // log.debug("Created queue " + destination);
+ }
+
+ // Keep the destination.
+ _pingDestinations.add(destination);
+ }
+ }
+
+ /**
+ * Creates consumers for the specified destinations and registers this pinger to listen to their messages.
+ *
+ * @param destinations The destinations to listen to.
+ * @param selector A selector to filter the messages with.
+ *
+ * @throws javax.jms.JMSException Any JMSExceptions are allowed to fall through.
+ */
+ public void createReplyConsumers(Collection<Destination> destinations, String selector) throws JMSException
+ {
+ /*log.debug("public void createReplyConsumers(Collection<Destination> destinations = " + destinations
+ + ", String selector = " + selector + "): called");*/
+
+ log.debug("There are " + destinations.size() + " destinations.");
+ log.debug("Creating " + _noOfConsumers + " consumers on each destination.");
+ log.debug("Total number of consumers is: " + (destinations.size() * _noOfConsumers));
+
+ for (Destination destination : destinations)
+ {
+ _consumer = new MessageConsumer[_noOfConsumers];
+
+ for (int i = 0; i < _noOfConsumers; i++)
+ {
+ // Create a consumer for the destination and set this pinger to listen to its messages.
+ _consumer[i] =
+ _consumerSession[i].createConsumer(destination, PREFETCH_DEFAULT, NO_LOCAL_DEFAULT, EXCLUSIVE_DEFAULT,
+ selector);
+
+ final int consumerNo = i;
+
+ _consumer[i].setMessageListener(new MessageListener()
+ {
+ public void onMessage(Message message)
+ {
+ onMessageWithConsumerNo(message, consumerNo);
+ }
+ });
+
+ log.debug("Set consumer " + i + " to listen to replies sent to destination: " + destination);
+ }
+ }
+ }
+
+ /**
+ * Stores the received message in the replies map, then resets the boolean latch that a thread waiting for a
+ * correlating reply may be waiting on. This is only done if the reply has a correlation id that is expected in the
+ * replies map.
+ *
+ * @param message The received message.
+ * @param consumerNo The consumer number within this test pinger instance.
+ */
+ public void onMessageWithConsumerNo(Message message, int consumerNo)
+ {
+ // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo = " + consumerNo + "): called");
+ try
+ {
+ long now = System.nanoTime();
+ long timestamp = getTimestamp(message);
+ long pingTime = now - timestamp;
+
+ // NDC.push("id" + instanceId + "/cons" + consumerNo);
+
+ // Extract the messages correlation id.
+ String correlationID = message.getJMSCorrelationID();
+ // log.debug("correlationID = " + correlationID);
+
+ // int num = message.getIntProperty("MSG_NUM");
+ // log.info("Message " + num + " received.");
+
+ boolean isRedelivered = message.getJMSRedelivered();
+ // log.debug("isRedelivered = " + isRedelivered);
+
+ if (!isRedelivered)
+ {
+ // Countdown on the traffic light if there is one for the matching correlation id.
+ PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID);
+
+ if (perCorrelationId != null)
+ {
+ CountDownLatch trafficLight = perCorrelationId.trafficLight;
+
+ // Restart the timeout timer on every message.
+ perCorrelationId.timeOutStart = System.nanoTime();
+
+ // log.debug("Reply was expected, decrementing the latch for the id, " + correlationID);
+
+ // Release waiting senders if there are some and using maxPending limit.
+ if ((_maxPendingSize > 0))
+ {
+ // Decrement the count of sent but not yet received messages.
+ int unreceived = _unreceived.decrementAndGet();
+ int unreceivedSize =
+ (unreceived * ((_messageSize == 0) ? 1 : _messageSize))
+ / (_isPubSub ? getConsumersPerDestination() : 1);
+
+ // log.debug("unreceived = " + unreceived);
+ // log.debug("unreceivedSize = " + unreceivedSize);
+
+ // synchronized (_sendPauseMonitor)
+ // {
+ if (unreceivedSize < _maxPendingSize)
+ {
+ _sendPauseMonitor.poll();
+ }
+ // }
+ }
+
+ // Decrement the countdown latch. Before this point, it is possible that two threads might enter this
+ // method simultanesouly with the same correlation id. Decrementing the latch in a synchronized block
+ // ensures that each thread will get a unique value for the remaining messages.
+ long trueCount;
+ long remainingCount;
+
+ synchronized (trafficLight)
+ {
+ trafficLight.countDown();
+
+ trueCount = trafficLight.getCount();
+ remainingCount = trueCount - 1;
+
+ // NDC.push("/rem" + remainingCount);
+
+ // log.debug("remainingCount = " + remainingCount);
+ // log.debug("trueCount = " + trueCount);
+
+ // Commit on transaction batch size boundaries. At this point in time the waiting producer
+ // remains blocked, even on the last message.
+ // Commit count is divided by noOfConsumers in p2p mode, so that each consumer only commits on
+ // each batch boundary. For pub/sub each consumer gets every message so no division is done.
+ // When running in client ack mode, an ack is done instead of a commit, on the commit batch
+ // size boundaries.
+ long commitCount = _isPubSub ? remainingCount : (remainingCount / _noOfConsumers);
+ // log.debug("commitCount = " + commitCount);
+
+ if ((commitCount % _txBatchSize) == 0)
+ {
+ if (_consAckMode == 2)
+ {
+ // log.debug("Doing client ack for consumer " + consumerNo + ".");
+ message.acknowledge();
+ }
+ else
+ {
+ // log.debug("Trying commit for consumer " + consumerNo + ".");
+ commitTx(_consumerSession[consumerNo]);
+ // log.info("Tx committed on consumer " + consumerNo);
+ }
+ }
+
+ // Forward the message and remaining count to any interested chained message listener.
+ if (_chainedMessageListener != null)
+ {
+ _chainedMessageListener.onMessage(message, (int) remainingCount, pingTime);
+ }
+
+ // Check if this is the last message, in which case release any waiting producers. This is done
+ // after the transaction has been committed and any listeners notified.
+ if (trueCount == 1)
+ {
+ trafficLight.countDown();
+ }
+ }
+ }
+ else
+ {
+ log.warn("Got unexpected message with correlationId: " + correlationID);
+ }
+ }
+ else
+ {
+ log.warn("Got redelivered message, ignoring.");
+ }
+ }
+ catch (JMSException e)
+ {
+ log.warn("There was a JMSException: " + e.getMessage(), e);
+ }
+ finally
+ {
+ // log.debug("public void onMessageWithConsumerNo(Message message, int consumerNo): ending");
+ // NDC.clear();
+ }
+ }
+
+ /**
+ * Sends the specified number of ping message and then waits for all correlating replies. If the wait times out
+ * before a reply arrives, then a null reply is returned from this method. This method allows the caller to specify
+ * the correlation id.
+ *
+ * @param message The message to send. If this is null, one is generated.
+ * @param numPings The number of ping messages to send.
+ * @param timeout The timeout in milliseconds.
+ * @param messageCorrelationId The message correlation id. If this is null, one is generated.
+ *
+ * @return The number of replies received. This may be less than the number sent if the timeout terminated the wait
+ * for all prematurely.
+ *
+ * @throws JMSException All underlying JMSExceptions are allowed to fall through.
+ * @throws InterruptedException When interrupted by a timeout
+ */
+ public int pingAndWaitForReply(Message message, int numPings, long timeout, String messageCorrelationId)
+ throws JMSException, InterruptedException
+ {
+ /*log.debug("public int pingAndWaitForReply(Message message, int numPings = " + numPings + ", long timeout = "
+ + timeout + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/
+
+ // Generate a unique correlation id to put on the messages before sending them, if one was not specified.
+ if (messageCorrelationId == null)
+ {
+ messageCorrelationId = Long.toString(_correlationIdGenerator.incrementAndGet());
+ }
+
+ try
+ {
+ // NDC.push("prod");
+
+ // Create a count down latch to count the number of replies with. This is created before the messages are
+ // sent so that the replies cannot be received before the count down is created.
+ // One is added to this, so that the last reply becomes a special case. The special case is that the
+ // chained message listener must be called before this sender can be unblocked, but that decrementing the
+ // countdown needs to be done before the chained listener can be called.
+ PerCorrelationId perCorrelationId = new PerCorrelationId();
+
+ perCorrelationId.trafficLight = new CountDownLatch(getExpectedNumPings(numPings) + 1);
+ perCorrelationIds.put(messageCorrelationId, perCorrelationId);
+
+ // Set up the current time as the start time for pinging on the correlation id. This is used to determine
+ // timeouts.
+ perCorrelationId.timeOutStart = System.nanoTime();
+
+ // Send the specifed number of messages.
+ pingNoWaitForReply(message, numPings, messageCorrelationId);
+
+ boolean timedOut;
+ boolean allMessagesReceived;
+ int numReplies;
+
+ do
+ {
+ // Block the current thread until replies to all the messages are received, or it times out.
+ perCorrelationId.trafficLight.await(timeout, TimeUnit.MILLISECONDS);
+
+ // Work out how many replies were receieved.
+ numReplies = getExpectedNumPings(numPings) - (int) perCorrelationId.trafficLight.getCount();
+
+ allMessagesReceived = numReplies == getExpectedNumPings(numPings);
+
+ // log.debug("numReplies = " + numReplies);
+ // log.debug("allMessagesReceived = " + allMessagesReceived);
+
+ // Recheck the timeout condition.
+ long now = System.nanoTime();
+ long lastMessageReceievedAt = perCorrelationId.timeOutStart;
+ timedOut = (now - lastMessageReceievedAt) > (timeout * 1000000);
+
+ // log.debug("now = " + now);
+ // log.debug("lastMessageReceievedAt = " + lastMessageReceievedAt);
+ }
+ while (!timedOut && !allMessagesReceived);
+
+ if ((numReplies < getExpectedNumPings(numPings)) && _verbose)
+ {
+ log.info("Timed out (" + timeout + " ms) before all replies received on id, " + messageCorrelationId);
+ }
+ else if (_verbose)
+ {
+ log.info("Got all replies on id, " + messageCorrelationId);
+ }
+
+ // commitTx(_consumerSession);
+
+ // log.debug("public int pingAndWaitForReply(Message message, int numPings, long timeout): ending");
+
+ return numReplies;
+ }
+ // Ensure that the message countdown latch is always removed from the reply map. The reply map is long lived,
+ // so will be a memory leak if this is not done.
+ finally
+ {
+ // NDC.pop();
+ perCorrelationIds.remove(messageCorrelationId);
+ }
+ }
+
+ /**
+ * Sends the specified number of ping messages and does not wait for correlating replies.
+ *
+ * @param message The message to send.
+ * @param numPings The number of pings to send.
+ * @param messageCorrelationId A correlation id to place on all messages sent.
+ *
+ * @throws JMSException All underlying JMSExceptions are allowed to fall through.
+ */
+ public void pingNoWaitForReply(Message message, int numPings, String messageCorrelationId) throws JMSException
+ {
+ /*log.debug("public void pingNoWaitForReply(Message message, int numPings = " + numPings
+ + ", String messageCorrelationId = " + messageCorrelationId + "): called");*/
+
+ if (message == null)
+ {
+ message = getTestMessage(getReplyDestinations().get(0), _messageSize, _persistent);
+ }
+
+ message.setJMSCorrelationID(messageCorrelationId);
+
+ // Set up a committed flag to detect uncommitted messages at the end of the send loop. This may occurr if the
+ // transaction batch size is not a factor of the number of pings. In which case an extra commit at the end is
+ // needed.
+ boolean committed = false;
+
+ // Send all of the ping messages.
+ for (int i = 0; i < numPings; i++)
+ {
+ // Re-timestamp the message.
+ // message.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime());
+
+ // Send the message, passing in the message count.
+ committed = sendMessage(i, message);
+
+ // Spew out per message timings on every message sonly in verbose mode.
+ /*if (_verbose)
+ {
+ log.info(timestampFormatter.format(new Date()) + ": Pinged at with correlation id, " + messageCorrelationId);
+ }*/
+ }
+
+ // Call commit if the send loop finished before reaching a batch size boundary so there may still be uncommitted messages.
+ if (!committed)
+ {
+ commitTx(_producerSession);
+ }
+ }
+
+ /**
+ * Sends the sepcified message, applies rate limiting and possibly commits the current transaction. The count of
+ * messages sent so far must be specified and is used to round robin the ping destinations (where there are more
+ * than one), and to determine if the transaction batch size has been reached and the sent messages should be
+ * committed.
+ *
+ * @param i The count of messages sent so far in a loop of multiple calls to this send method.
+ * @param message The message to send.
+ *
+ * @return <tt>true</tt> if the messages were committed, <tt>false</tt> otherwise.
+ *
+ * @throws JMSException All underlyiung JMSExceptions are allowed to fall through.
+ */
+ protected boolean sendMessage(int i, Message message) throws JMSException
+ {
+ try
+ {
+ NDC.push("id" + instanceId + "/prod");
+
+ // log.debug("protected boolean sendMessage(int i = " + i + ", Message message): called");
+ // log.debug("_txBatchSize = " + _txBatchSize);
+
+ // Round robin the destinations as the messages are sent.
+ Destination destination = _pingDestinations.get(i % _pingDestinations.size());
+
+ // Prompt the user to kill the broker when doing failover testing.
+ _failBeforeSend = waitForUserToPromptOnFailure(_failBeforeSend);
+
+ // Get the test setup for the correlation id.
+ String correlationID = message.getJMSCorrelationID();
+ PerCorrelationId perCorrelationId = perCorrelationIds.get(correlationID);
+
+ // If necessary, wait until the max pending message size comes within its limit.
+ if (_maxPendingSize > 0)
+ {
+ synchronized (_sendPauseMonitor)
+ {
+ // Used to keep track of the number of times that send has to wait.
+ int numWaits = 0;
+
+ // The maximum number of waits before the test gives up and fails. This has been chosen to correspond with
+ // the test timeout.
+ int waitLimit = (int) (TIMEOUT_DEFAULT / 10000);
+
+ while (true)
+ {
+ // Get the size estimate of sent but not yet received messages.
+ int unreceived = _unreceived.get();
+ int unreceivedSize =
+ (unreceived * ((_messageSize == 0) ? 1 : _messageSize))
+ / (_isPubSub ? getConsumersPerDestination() : 1);
+
+ // log.debug("unreceived = " + unreceived);
+ // log.debug("unreceivedSize = " + unreceivedSize);
+ // log.debug("_maxPendingSize = " + _maxPendingSize);
+
+ if (unreceivedSize > _maxPendingSize)
+ {
+ // log.debug("unreceived size estimate over limit = " + unreceivedSize);
+
+ // Fail the test if the send has had to wait more than the maximum allowed number of times.
+ if (numWaits > waitLimit)
+ {
+ String errorMessage =
+ "Send has had to wait for the unreceivedSize (" + unreceivedSize
+ + ") to come below the maxPendingSize (" + _maxPendingSize + ") more that " + waitLimit
+ + " times.";
+ log.warn(errorMessage);
+ throw new RuntimeException(errorMessage);
+ }
+
+ // Wait on the send pause barrier for the limit to be re-established.
+ try
+ {
+ long start = System.nanoTime();
+ // _sendPauseMonitor.wait(10000);
+ _sendPauseMonitor.offer(new Object(), 10000, TimeUnit.MILLISECONDS);
+ long end = System.nanoTime();
+
+ // Count the wait only if it was for > 99% of the requested wait time.
+ if (((float) (end - start) / (float) (10000 * 1000000L)) > 0.99)
+ {
+ numWaits++;
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the interrupted status
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ // Send the message either to its round robin destination, or its default destination.
+ // int num = numSent.incrementAndGet();
+ // message.setIntProperty("MSG_NUM", num);
+ setTimestamp(message);
+
+ if (destination == null)
+ {
+ _producer.send(message);
+ }
+ else
+ {
+ _producer.send(destination, message);
+ }
+
+ // Increase the unreceived size, this may actually happen after the message is received.
+ // The unreceived size is incremented by the number of consumers that will get a copy of the message,
+ // in pub/sub mode.
+ if (_maxPendingSize > 0)
+ {
+ int newUnreceivedCount = _unreceived.addAndGet(_isPubSub ? getConsumersPerDestination() : 1);
+ // log.debug("newUnreceivedCount = " + newUnreceivedCount);
+ }
+
+ // Apply message rate throttling if a rate limit has been set up.
+ if (_rateLimiter != null)
+ {
+ _rateLimiter.throttle();
+ }
+
+ // Call commit every time the commit batch size is reached.
+ boolean committed = false;
+
+ // Commit on every transaction batch size boundary. Here i + 1 is the count of actual messages sent.
+ if (((i + 1) % _txBatchSize) == 0)
+ {
+ // log.debug("Trying commit on producer session.");
+ committed = commitTx(_producerSession);
+ }
+
+ return committed;
+ }
+ finally
+ {
+ NDC.clear();
+ }
+ }
+
+ /**
+ * If the specified fail flag is set, this method waits for the user to cause a failure and then indicate to the
+ * test that the failure has occurred, before the method returns.
+ *
+ * @param failFlag The fail flag to test.
+ *
+ * @return The new value for the fail flag. If the {@link #_failOnce} flag is set, then each fail flag is only
+ * used once, then reset.
+ */
+ private boolean waitForUserToPromptOnFailure(boolean failFlag)
+ {
+ if (failFlag)
+ {
+ if (_failOnce)
+ {
+ failFlag = false;
+ }
+
+ // log.debug("Failing Before Send");
+ waitForUser(KILL_BROKER_PROMPT);
+ }
+
+ return failFlag;
+ }
+
+ /**
+ * Implements a single iteration of the ping loop. This sends the number of pings specified by the transaction batch
+ * size property, and waits for replies to all of them. Any errors cause the publish flag to be cleared, which will
+ * terminate the pinger.
+ */
+ public void pingLoop()
+ {
+ try
+ {
+ // Generate a sample message and time stamp it.
+ Message msg = getTestMessage(_replyDestination, _messageSize, _persistent);
+ // setTimestamp(msg);
+
+ // Send the message and wait for a reply.
+ pingAndWaitForReply(msg, TX_BATCH_SIZE_DEFAULT, TIMEOUT_DEFAULT, null);
+ }
+ catch (JMSException e)
+ {
+ _publish = false;
+ // log.debug("There was a JMSException: " + e.getMessage(), e);
+ }
+ catch (InterruptedException e)
+ {
+ _publish = false;
+ // log.debug("There was an interruption: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Sets a chained message listener. The message listener on this pinger, chains all its messages to the one set
+ * here.
+ *
+ * @param messageListener The chained message listener.
+ */
+ public void setChainedMessageListener(ChainedMessageListener messageListener)
+ {
+ _chainedMessageListener = messageListener;
+ }
+
+ /** Removes any chained message listeners from this pinger. */
+ public void removeChainedMessageListener()
+ {
+ _chainedMessageListener = null;
+ }
+
+ /**
+ * Generates a test message of the specified size, with the specified reply-to destination and persistence flag.
+ *
+ * @param replyQueue The reply-to destination for the message.
+ * @param messageSize The desired size of the message in bytes.
+ * @param persistent <tt>true</tt> if the message should use persistent delivery, <tt>false</tt> otherwise.
+ *
+ * @return A freshly generated test message.
+ *
+ * @throws javax.jms.JMSException All underlying JMSException are allowed to fall through.
+ */
+ public Message getTestMessage(Destination replyQueue, int messageSize, boolean persistent) throws JMSException
+ {
+ return TestMessageFactory.newObjectMessage(_producerSession, replyQueue, messageSize, persistent);
+ }
+
+ /**
+ * Sets the current time in nanoseconds as the timestamp on the message.
+ *
+ * @param msg The message to timestamp.
+ *
+ * @throws JMSException Any JMSExceptions are allowed to fall through.
+ */
+ protected void setTimestamp(Message msg) throws JMSException
+ {
+ if (((AMQSession) _producerSession).isStrictAMQP())
+ {
+ ((AMQMessage) msg).setTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME), System.nanoTime());
+ }
+ else
+ {
+ msg.setLongProperty(MESSAGE_TIMESTAMP_PROPNAME, System.nanoTime());
+ }
+ }
+
+ /**
+ * Extracts the nanosecond timestamp from a message.
+ *
+ * @param msg The message to extract the time stamp from.
+ *
+ * @return The timestamp in nanos.
+ *
+ * @throws JMSException Any JMSExceptions are allowed to fall through.
+ */
+ protected long getTimestamp(Message msg) throws JMSException
+ {
+ if (((AMQSession) _producerSession).isStrictAMQP())
+ {
+ Long value = ((AMQMessage) msg).getTimestampProperty(new AMQShortString(MESSAGE_TIMESTAMP_PROPNAME));
+
+ return (value == null) ? 0L : value;
+ }
+ else
+ {
+ return msg.getLongProperty(PingPongProducer.MESSAGE_TIMESTAMP_PROPNAME);
+ }
+ }
+
+ /**
+ * Stops the ping loop by clearing the publish flag. The current loop will complete when it notices that this flag
+ * has been cleared.
+ */
+ public void stop()
+ {
+ _publish = false;
+ }
+
+ /**
+ * Starts the producer and consumer connections.
+ *
+ * @throws JMSException Any JMSExceptions are allowed to fall through.
+ */
+ public void start() throws JMSException
+ {
+ // log.debug("public void start(): called");
+
+ _connection.start();
+ // log.debug("Producer started.");
+
+ for (int i = 0; i < _noOfConsumers; i++)
+ {
+ _consumerConnection[i].start();
+ // log.debug("Consumer " + i + " started.");
+ }
+ }
+
+ /** Implements a ping loop that repeatedly pings until the publish flag becomes false. */
+ public void run()
+ {
+ // Keep running until the publish flag is cleared.
+ while (_publish)
+ {
+ pingLoop();
+ }
+ }
+
+ /**
+ * Callback method, implementing ExceptionListener. This should be registered to listen for exceptions on the
+ * connection, this clears the publish flag which in turn will halt the ping loop.
+ *
+ * @param e The exception that triggered this callback method.
+ */
+ public void onException(JMSException e)
+ {
+ // log.debug("public void onException(JMSException e = " + e + "): called", e);
+ _publish = false;
+ }
+
+ /**
+ * Gets a shutdown hook that will cleanly shut this down when it is running the ping loop. This can be registered
+ * with the runtime system as a shutdown hook.
+ *
+ * @return A shutdown hook for the ping loop.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ stop();
+ }
+ });
+ }
+
+ /**
+ * Closes all of the producer and consumer connections.
+ *
+ * @throws JMSException All JMSException are allowed to fall through.
+ */
+ public void close() throws JMSException
+ {
+ // log.debug("public void close(): called");
+
+ try
+ {
+ if (_connection != null)
+ {
+ // log.debug("Before close producer connection.");
+ _connection.close();
+ // log.debug("Closed producer connection.");
+ }
+
+ for (int i = 0; i < _noOfConsumers; i++)
+ {
+ if (_consumerConnection[i] != null)
+ {
+ // log.debug("Before close consumer connection " + i + ".");
+ _consumerConnection[i].close();
+ // log.debug("Closed consumer connection " + i + ".");
+ }
+ }
+ }
+ finally
+ {
+ _connection = null;
+ _producerSession = null;
+ _consumerSession = null;
+ _consumerConnection = null;
+ _producer = null;
+ _consumer = null;
+ _pingDestinations = null;
+ _replyDestination = null;
+ }
+ }
+
+ /**
+ * Convenience method to commit the transaction on the specified controlSession. If the controlSession to commit on is not a
+ * transactional controlSession, this method does nothing (unless the failover after send flag is set).
+ *
+ * <p/>If the {@link #_failAfterSend} flag is set, this will prompt the user to kill the broker before the commit is
+ * applied. This flag applies whether the pinger is transactional or not.
+ *
+ * <p/>If the {@link #_failBeforeCommit} flag is set, this will prompt the user to kill the broker before the commit
+ * is applied. If the {@link #_failAfterCommit} flag is set, this will prompt the user to kill the broker after the
+ * commit is applied. These flags will only apply if using a transactional pinger.
+ *
+ * @param session The controlSession to commit
+ *
+ * @return <tt>true</tt> if the controlSession was committed, <tt>false</tt> if it was not.
+ *
+ * @throws javax.jms.JMSException If the commit fails and then the rollback fails.
+ *
+ * @todo Consider moving the fail after send logic into the send method. It is confusing to have it in this commit
+ * method, because commits only apply to transactional pingers, but fail after send applied to transactional and
+ * non-transactional alike.
+ */
+ protected boolean commitTx(Session session) throws JMSException
+ {
+ // log.debug("protected void commitTx(Session session): called");
+
+ boolean committed = false;
+
+ _failAfterSend = waitForUserToPromptOnFailure(_failAfterSend);
+
+ if (session.getTransacted())
+ {
+ // log.debug("Session is transacted.");
+
+ try
+ {
+ _failBeforeCommit = waitForUserToPromptOnFailure(_failBeforeCommit);
+
+ long start = System.nanoTime();
+ session.commit();
+ committed = true;
+ // log.debug("Time taken to commit :" + ((System.nanoTime() - start) / 1000000f) + " ms");
+
+ _failAfterCommit = waitForUserToPromptOnFailure(_failAfterCommit);
+
+ // log.debug("Session Commited.");
+ }
+ catch (JMSException e)
+ {
+ // log.debug("JMSException on commit:" + e.getMessage(), e);
+
+ // Warn that the bounce back client is not available.
+ if (e.getLinkedException() instanceof AMQNoConsumersException)
+ {
+ // log.debug("No consumers on queue.");
+ }
+
+ try
+ {
+ session.rollback();
+ // log.debug("Message rolled back.");
+ }
+ catch (JMSException jmse)
+ {
+ // log.debug("JMSE on rollback:" + jmse.getMessage(), jmse);
+
+ // Both commit and rollback failed. Throw the rollback exception.
+ throw jmse;
+ }
+ }
+ }
+
+ return committed;
+ }
+
+ /**
+ * Outputs a prompt to the console and waits for the user to press return.
+ *
+ * @param prompt The prompt to display on the console.
+ */
+ public void waitForUser(String prompt)
+ {
+ System.out.println(prompt);
+
+ try
+ {
+ System.in.read();
+ }
+ catch (IOException e)
+ {
+ // Ignored.
+ }
+
+ System.out.println("Continuing.");
+ }
+
+ /**
+ * Gets the number of consumers that are listening to each destination in the test.
+ *
+ * @return int The number of consumers subscribing to each topic.
+ */
+ public int getConsumersPerDestination()
+ {
+ return _noOfConsumers;
+ }
+
+ /**
+ * Calculates how many pings are expected to be received for the given number sent.
+ *
+ * @param numpings The number of pings that will be sent.
+ *
+ * @return The number that should be received, for the test to pass.
+ */
+ public int getExpectedNumPings(int numpings)
+ {
+ // log.debug("public int getExpectedNumPings(int numpings = " + numpings + "): called");
+
+ // log.debug("Each ping will be received by " + (_isPubSub ? getConsumersPerDestination() : 1) + " consumers.");
+
+ return numpings * (_isPubSub ? getConsumersPerDestination() : 1);
+ }
+
+ /**
+ * Defines a chained message listener interface that can be attached to this pinger. Whenever this pinger's {@link
+ * PingPongProducer#onMessageWithConsumerNo} method is called, the chained listener set through the {@link
+ * PingPongProducer#setChainedMessageListener} method is passed the message, and the remaining expected count of
+ * messages with that correlation id.
+ *
+ * <p/>Provided only one pinger is producing messages with that correlation id, the chained listener will always be
+ * given unique message counts. It will always be called while the producer waiting for all messages to arrive is
+ * still blocked.
+ */
+ public static interface ChainedMessageListener
+ {
+ /**
+ * Notifies interested listeners about message arrival and important test stats, the number of messages
+ * remaining in the test, and the messages send timestamp.
+ *
+ * @param message The newly arrived message.
+ * @param remainingCount The number of messages left to complete the test.
+ * @param latency The nanosecond latency of the message.
+ *
+ * @throws JMSException Any JMS exceptions is allowed to fall through.
+ */
+ public void onMessage(Message message, int remainingCount, long latency) throws JMSException;
+ }
+
+ /**
+ * Holds information on each correlation id. The countdown latch, the current timeout timer... More stuff to be
+ * added to this: read/write lock to make onMessage more concurrent as described in class header comment.
+ */
+ protected static class PerCorrelationId
+ {
+ /** Holds a countdown on number of expected messages. */
+ CountDownLatch trafficLight;
+
+ /** Holds the last timestamp that the timeout was reset to. */
+ Long timeOutStart;
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java b/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java
new file mode 100644
index 0000000000..e52deeecf1
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java
@@ -0,0 +1,251 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.requestreply;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import uk.co.thebadgerset.junit.extensions.AsymptoticTestCase;
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+
+import javax.jms.*;
+
+/**
+ * PingPongTestPerf is a full round trip ping test, that has been written with the intention of being scaled up to run
+ * many times simultaneously to simluate many clients/producer/connections. A full round trip ping sends a message from
+ * a producer to a conumer, then the consumer replies to the message on a temporary queue.
+ *
+ * <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
+ * 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
+ * temporary queue for replies. This setup is only established once for all the test repeats, but each test threads
+ * gets its own connection/producer/consumer, this is only re-established if the connection is lost.
+ *
+ * <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that
+ * is the name of the temporary queue, fires off many messages on the original queue and waits for them all to come
+ * back on the temporary queue.
+ *
+ * <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * </table>
+ */
+public class PingPongTestPerf extends AsymptoticTestCase
+{
+ private static Logger _logger = Logger.getLogger(PingPongTestPerf.class);
+
+ /** Thread local to hold the per-thread test setup fields. */
+ ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
+
+ // Set up a property reader to extract the test parameters from. Once ContextualProperties is available in
+ // the project dependencies, use it to get property overrides for configurable tests and to notify the test runner
+ // of the test parameters to log with the results. It also providers some basic type parsing convenience methods.
+ // private Properties testParameters = System.getProperties();
+ private ParsedProperties testParameters =
+ TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/);
+
+ public PingPongTestPerf(String name)
+ {
+ super(name);
+
+ _logger.debug(testParameters);
+
+ // Sets up the test parameters with defaults.
+ /*testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME,
+ Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.MESSAGE_SIZE_PROPNAME,
+ Integer.toString(PingPongProducer.MESSAGE_SIZE_DEAFULT));
+ testParameters.setPropertyIfNull(PingPongProducer.PING_QUEUE_NAME_PROPNAME,
+ PingPongProducer.PING_QUEUE_NAME_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.PERSISTENT_MODE_PROPNAME,
+ Boolean.toString(PingPongProducer.PERSISTENT_MODE_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.TRANSACTED_PROPNAME,
+ Boolean.toString(PingPongProducer.TRANSACTED_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.BROKER_PROPNAME, PingPongProducer.BROKER_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.USERNAME_PROPNAME, PingPongProducer.USERNAME_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.PASSWORD_PROPNAME, PingPongProducer.PASSWORD_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.VIRTUAL_HOST_PROPNAME, PingPongProducer.VIRTUAL_HOST_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.VERBOSE_PROPNAME,
+ Boolean.toString(PingPongProducer.VERBOSE_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.RATE_PROPNAME, Integer.toString(PingPongProducer.RATE_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.PUBSUB_PROPNAME,
+ Boolean.toString(PingPongProducer.PUBSUB_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME,
+ Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.TIMEOUT_PROPNAME, Long.toString(PingPongProducer.TIMEOUT_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.DESTINATION_COUNT_PROPNAME,
+ Integer.toString(PingPongProducer.DESTINATION_COUNT_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_COMMIT_PROPNAME,
+ PingPongProducer.FAIL_AFTER_COMMIT_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_COMMIT_PROPNAME,
+ PingPongProducer.FAIL_BEFORE_COMMIT_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_SEND_PROPNAME,
+ PingPongProducer.FAIL_AFTER_SEND_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_SEND_PROPNAME,
+ PingPongProducer.FAIL_BEFORE_SEND_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.FAIL_ONCE_PROPNAME, PingPongProducer.FAIL_ONCE_DEFAULT);
+ testParameters.setPropertyIfNull(PingPongProducer.UNIQUE_DESTS_PROPNAME,
+ Boolean.toString(PingPongProducer.UNIQUE_DESTS_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.ACK_MODE_PROPNAME,
+ Integer.toString(PingPongProducer.ACK_MODE_DEFAULT));
+ testParameters.setPropertyIfNull(PingPongProducer.PAUSE_AFTER_BATCH_PROPNAME,
+ PingPongProducer.PAUSE_AFTER_BATCH_DEFAULT);*/
+ }
+
+ /**
+ * Compile all the tests into a test suite.
+ */
+ public static Test suite()
+ {
+ // Build a new test suite
+ TestSuite suite = new TestSuite("Ping-Pong Performance Tests");
+
+ // Run performance tests in read committed mode.
+ suite.addTest(new PingPongTestPerf("testPingPongOk"));
+
+ return suite;
+ }
+
+ private static void setSystemPropertyIfNull(String propName, String propValue)
+ {
+ if (System.getProperty(propName) == null)
+ {
+ System.setProperty(propName, propValue);
+ }
+ }
+
+ public void testPingPongOk(int numPings) throws Exception
+ {
+ // Get the per thread test setup to run the test through.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+
+ // Generate a sample message. This message is already time stamped and has its reply-to destination set.
+ Message msg =
+ perThreadSetup._testPingProducer.getTestMessage(perThreadSetup._testPingProducer.getReplyDestinations().get(0),
+ testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
+ testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
+
+ // Send the message and wait for a reply.
+ int numReplies =
+ perThreadSetup._testPingProducer.pingAndWaitForReply(msg, numPings, PingPongProducer.TIMEOUT_DEFAULT, null);
+
+ // Fail the test if the timeout was exceeded.
+ if (numReplies != numPings)
+ {
+ Assert.fail("The ping timed out, got " + numReplies + " out of " + numPings);
+ }
+ }
+
+ /**
+ * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
+ */
+ public void threadSetUp()
+ {
+ try
+ {
+ PerThreadSetup perThreadSetup = new PerThreadSetup();
+
+ // Extract the test set up paramaeters.
+ String brokerDetails = testParameters.getProperty(PingPongProducer.BROKER_PROPNAME);
+ String username = testParameters.getProperty(PingPongProducer.USERNAME_PROPNAME);
+ String password = testParameters.getProperty(PingPongProducer.PASSWORD_PROPNAME);
+ String virtualPath = testParameters.getProperty(PingPongProducer.VIRTUAL_HOST_PROPNAME);
+ String destinationName = testParameters.getProperty(PingPongProducer.PING_QUEUE_NAME_PROPNAME);
+ boolean persistent = testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME);
+ boolean transacted = testParameters.getPropertyAsBoolean(PingPongProducer.TRANSACTED_PROPNAME);
+ String selector = testParameters.getProperty(PingPongProducer.SELECTOR_PROPNAME);
+ boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME);
+ boolean pubsub = testParameters.getPropertyAsBoolean(PingPongProducer.PUBSUB_PROPNAME);
+
+ synchronized (this)
+ {
+ // Establish a bounce back client on the ping queue to bounce back the pings.
+ perThreadSetup._testPingBouncer =
+ new PingPongBouncer(brokerDetails, username, password, virtualPath, destinationName, persistent,
+ transacted, selector, verbose, pubsub);
+
+ // Start the connections for client and producer running.
+ perThreadSetup._testPingBouncer.getConnection().start();
+
+ // Establish a ping-pong client on the ping queue to send the pings and receive replies with.
+ perThreadSetup._testPingProducer = new PingPongProducer(testParameters);
+ perThreadSetup._testPingProducer.establishConnection(true, true);
+ perThreadSetup._testPingProducer.start();
+ }
+
+ // Attach the per-thread set to the thread.
+ threadSetup.set(perThreadSetup);
+ }
+ catch (Exception e)
+ {
+ _logger.warn("There was an exception during per thread setup.", e);
+ }
+ }
+
+ /**
+ * Performs test fixture clean
+ */
+ public void threadTearDown()
+ {
+ _logger.debug("public void threadTearDown(): called");
+
+ try
+ {
+ // Get the per thread test fixture.
+ PerThreadSetup perThreadSetup = threadSetup.get();
+
+ // Close the pingers so that it cleans up its connection cleanly.
+ synchronized (this)
+ {
+ perThreadSetup._testPingProducer.close();
+ // perThreadSetup._testPingBouncer.close();
+ }
+
+ // Ensure the per thread fixture is reclaimed.
+ threadSetup.remove();
+ }
+ catch (JMSException e)
+ {
+ _logger.warn("There was an exception during per thread tear down.");
+ }
+ }
+
+ protected static class PerThreadSetup
+ {
+ /**
+ * Holds the test ping-pong producer.
+ */
+ private PingPongProducer _testPingProducer;
+
+ /**
+ * Holds the test ping client.
+ */
+ private PingPongBouncer _testPingBouncer;
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/topic/Config.java b/Final/java/perftests/src/main/java/org/apache/qpid/topic/Config.java
new file mode 100644
index 0000000000..d5c0979399
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/topic/Config.java
@@ -0,0 +1,326 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.topic;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.config.ConnectorConfig;
+import org.apache.qpid.config.Connector;
+import org.apache.qpid.config.AbstractConfig;
+
+import javax.jms.Connection;
+
+public class Config extends AbstractConfig implements ConnectorConfig
+{
+
+ private String host = "localhost";
+ private int port = 5672;
+ private String factory = null;
+
+ private int payload = 256;
+ private int messages = 1000;
+ private int clients = 1;
+ private int batch = 1;
+ private long delay = 1;
+ private int warmup;
+ private int ackMode= AMQSession.NO_ACKNOWLEDGE;
+ private String clientId;
+ private String subscriptionId;
+ private String selector;
+ private String destinationName;
+ private boolean persistent;
+ private boolean transacted;
+ private int destinationsCount;
+ private int batchSize;
+ private int rate;
+ private boolean ispubsub;
+ private long timeout;
+
+ public Config()
+ {
+ }
+
+ public int getAckMode()
+ {
+ return ackMode;
+ }
+
+ public void setPayload(int payload)
+ {
+ this.payload = payload;
+ }
+
+ public int getPayload()
+ {
+ return payload;
+ }
+
+ void setClients(int clients)
+ {
+ this.clients = clients;
+ }
+
+ int getClients()
+ {
+ return clients;
+ }
+
+ void setMessages(int messages)
+ {
+ this.messages = messages;
+ }
+
+ public int getMessages()
+ {
+ return messages;
+ }
+
+ public int getBatchSize()
+ {
+ return batchSize;
+ }
+
+ public int getRate()
+ {
+ return rate;
+ }
+
+ public int getDestinationsCount()
+ {
+ return destinationsCount;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public void setHost(String host)
+ {
+ this.host = host;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ public String getFactory()
+ {
+ return factory;
+ }
+
+ public void setPort(int port)
+ {
+ this.port = port;
+ }
+
+ int getBatch()
+ {
+ return batch;
+ }
+
+ void setBatch(int batch)
+ {
+ this.batch = batch;
+ }
+
+ int getWarmup()
+ {
+ return warmup;
+ }
+
+ void setWarmup(int warmup)
+ {
+ this.warmup = warmup;
+ }
+
+ public long getDelay()
+ {
+ return delay;
+ }
+
+ public void setDelay(long delay)
+ {
+ this.delay = delay;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ public void setTimeout(long time)
+ {
+ this.timeout = time;
+ }
+
+ public String getClientId()
+ {
+ return clientId;
+ }
+
+ public String getSubscriptionId()
+ {
+ return subscriptionId;
+ }
+
+ public String getSelector()
+ {
+ return selector;
+ }
+
+ public String getDestination()
+ {
+ return destinationName;
+ }
+
+ public boolean usePersistentMessages()
+ {
+ return persistent;
+ }
+
+ public boolean isTransacted()
+ {
+ return transacted;
+ }
+
+ public boolean isPubSub()
+ {
+ return ispubsub;
+ }
+
+ public void setOption(String key, String value)
+ {
+ if("-host".equalsIgnoreCase(key))
+ {
+ setHost(value);
+ }
+ else if("-port".equalsIgnoreCase(key))
+ {
+ try
+ {
+ setPort(Integer.parseInt(value));
+ }
+ catch(NumberFormatException e)
+ {
+ throw new RuntimeException("Bad port number: " + value, e);
+ }
+ }
+ else if("-payload".equalsIgnoreCase(key))
+ {
+ setPayload(parseInt("Bad payload size", value));
+ }
+ else if("-messages".equalsIgnoreCase(key))
+ {
+ setMessages(parseInt("Bad message count", value));
+ }
+ else if("-clients".equalsIgnoreCase(key))
+ {
+ setClients(parseInt("Bad client count", value));
+ }
+ else if("-batch".equalsIgnoreCase(key))
+ {
+ setBatch(parseInt("Bad batch count", value));
+ }
+ else if("-delay".equalsIgnoreCase(key))
+ {
+ setDelay(parseLong("Bad batch delay", value));
+ }
+ else if("-warmup".equalsIgnoreCase(key))
+ {
+ setWarmup(parseInt("Bad warmup count", value));
+ }
+ else if("-ack".equalsIgnoreCase(key))
+ {
+ ackMode = parseInt("Bad ack mode", value);
+ }
+ else if("-factory".equalsIgnoreCase(key))
+ {
+ factory = value;
+ }
+ else if("-clientId".equalsIgnoreCase(key))
+ {
+ clientId = value;
+ }
+ else if("-subscriptionId".equalsIgnoreCase(key))
+ {
+ subscriptionId = value;
+ }
+ else if("-persistent".equalsIgnoreCase(key))
+ {
+ persistent = "true".equalsIgnoreCase(value);
+ }
+ else if("-transacted".equalsIgnoreCase(key))
+ {
+ transacted = "true".equalsIgnoreCase(value);
+ }
+ else if ("-destinationscount".equalsIgnoreCase(key))
+ {
+ destinationsCount = parseInt("Bad destinations count", value);
+ }
+ else if ("-batchsize".equalsIgnoreCase(key))
+ {
+ batchSize = parseInt("Bad batch size", value);
+ }
+ else if ("-rate".equalsIgnoreCase(key))
+ {
+ rate = parseInt("MEssage rate", value);
+ }
+ else if("-pubsub".equalsIgnoreCase(key))
+ {
+ ispubsub = "true".equalsIgnoreCase(value);
+ }
+ else if("-selector".equalsIgnoreCase(key))
+ {
+ selector = value;
+ }
+ else if("-destinationname".equalsIgnoreCase(key))
+ {
+ destinationName = value;
+ }
+ else if("-timeout".equalsIgnoreCase(key))
+ {
+ setTimeout(parseLong("Bad timeout data", value));
+ }
+ else
+ {
+ System.out.println("Ignoring unrecognised option: " + key);
+ }
+ }
+
+ static String getAckModeDescription(int ackMode)
+ {
+ switch(ackMode)
+ {
+ case AMQSession.NO_ACKNOWLEDGE: return "NO_ACKNOWLEDGE";
+ case AMQSession.AUTO_ACKNOWLEDGE: return "AUTO_ACKNOWLEDGE";
+ case AMQSession.CLIENT_ACKNOWLEDGE: return "CLIENT_ACKNOWLEDGE";
+ case AMQSession.DUPS_OK_ACKNOWLEDGE: return "DUPS_OK_ACKNOWELDGE";
+ case AMQSession.PRE_ACKNOWLEDGE: return "PRE_ACKNOWLEDGE";
+ }
+ return "AckMode=" + ackMode;
+ }
+
+ public Connection createConnection() throws Exception
+ {
+ return new Connector().createConnection(this);
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java b/Final/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java
new file mode 100644
index 0000000000..6dcea42bfe
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/topic/Listener.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.topic;
+
+import java.util.Random;
+
+import javax.jms.*;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+
+/**
+ * This class has not kept up to date with the topic_listener in the cpp tests. It should provide identical behaviour for
+ * cross testing the java and cpp clients.
+ *
+ * <p/>How the cpp topic_publisher operates:
+ * It publishes text messages to the default topic exchange, on virtual host "/test", on the topic "topic_control", for
+ * the specified number of test messages to be sent.
+ * It publishes a report request message (on same topic), with the header text field "TYPE", value "REPORT_REQUEST",
+ * optionally within a transaction, and waits for the specified number of consumers to reply to this request. The
+ * listeners should reply to this message on a queue named "response", on virtual host "/test", with some sort of message
+ * about the number of messages received and how long it took, although the publisher never looks at the message content.
+ * The publisher then send a message (on the same topic), with the header text field "TYPE", value "TERMINATION_REQUEST",
+ * which the listener should close its connection and terminate upon receipt of.
+ *
+ * @todo I've added lots of field table types in the report message, just to check if the other end can decode them
+ * correctly. Not really the right place to test this, so remove them from
+ * {@link #createReportResponseMessage(String)} once a better test exists.
+ */
+public class Listener implements MessageListener
+{
+ private static Logger log = Logger.getLogger(Listener.class);
+
+ public static final String CONTROL_TOPIC = "topic_control";
+ public static final String RESPONSE_QUEUE = "response";
+
+ private final Topic _topic;
+ //private final Topic _control;
+
+ private final Queue _response;
+
+ /** Holds the connection to listen on. */
+ private final Connection _connection;
+
+ /** Holds the producer to send control messages on. */
+ private final MessageProducer _controller;
+
+ /** Holds the JMS session. */
+ private final javax.jms.Session _session;
+
+ /** Holds a flag to indicate that a timer has begun on the first message. Reset when report is sent. */
+ private boolean init;
+
+ /** Holds the count of messages received by this listener. */
+ private int count;
+
+ /** Used to hold the start time of the first message. */
+ private long start;
+ private static String clientId;
+
+ Listener(Connection connection, int ackMode, String name) throws Exception
+ {
+ log.debug("Listener(Connection connection = " + connection + ", int ackMode = " + ackMode + ", String name = " + name
+ + "): called");
+
+ _connection = connection;
+ _session = connection.createSession(false, ackMode);
+
+ if (_session instanceof AMQSession)
+ {
+ _topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, CONTROL_TOPIC);
+ //_control = new AMQTopic(CONTROL_TOPIC);
+ _response = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, RESPONSE_QUEUE);
+ }
+ else
+ {
+ _topic = _session.createTopic(CONTROL_TOPIC);
+ //_control = _session.createTopic(CONTROL_TOPIC);
+ _response = _session.createQueue(RESPONSE_QUEUE);
+ }
+
+ //register for events
+ if (name == null)
+ {
+ log.debug("Calling _factory.createTopicConsumer().setMessageListener(this)");
+ createTopicConsumer().setMessageListener(this);
+ }
+ else
+ {
+ log.debug("Calling createDurableTopicConsumer(name).setMessageListener(this)");
+ createDurableTopicConsumer(name).setMessageListener(this);
+ }
+
+ _connection.start();
+
+ _controller = createControlPublisher();
+ System.out.println("Waiting for messages " + Config.getAckModeDescription(ackMode)
+ +
+ ((name == null)
+ ? "" : (" (subscribed with name " + name + " and client id " + connection.getClientID() + ")"))
+ + "...");
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ clientId = "Listener-" + System.currentTimeMillis();
+
+ NDC.push(clientId);
+
+ Config config = new Config();
+ config.setOptions(argv);
+
+ //Connection con = config.createConnection();
+ Connection con =
+ new AMQConnection("amqp://guest:guest@testid/test?brokerlist='" + config.getHost() + ":" + config.getPort()
+ + "'");
+
+ if (config.getClientId() != null)
+ {
+ con.setClientID(config.getClientId());
+ }
+
+ new Listener(con, config.getAckMode(), config.getSubscriptionId());
+
+ NDC.pop();
+ NDC.remove();
+ }
+
+ /**
+ * Checks whether or not a text field on a message has the specified value.
+ *
+ * @param m The message to check.
+ * @param fieldName The name of the field to check.
+ * @param value The expected value of the field to compare with.
+ *
+ * @return <tt>true</tt>If the specified field has the specified value, <tt>fals</tt> otherwise.
+ *
+ * @throws JMSException Any JMSExceptions are allowed to fall through.
+ */
+ private static boolean checkTextField(Message m, String fieldName, String value) throws JMSException
+ {
+ log.debug("private static boolean checkTextField(Message m = " + m + ", String fieldName = " + fieldName
+ + ", String value = " + value + "): called");
+
+ String comp = m.getStringProperty(fieldName);
+ log.debug("comp = " + comp);
+
+ boolean result = (comp != null) && comp.equals(value);
+ log.debug("result = " + result);
+
+ return result;
+ }
+
+ public void onMessage(Message message)
+ {
+ NDC.push(clientId);
+
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ if (!init)
+ {
+ start = System.nanoTime() / 1000000;
+ count = 0;
+ init = true;
+ }
+
+ try
+ {
+ if (isShutdown(message))
+ {
+ log.debug("Got a shutdown message.");
+ shutdown();
+ }
+ else if (isReport(message))
+ {
+ log.debug("Got a report request message.");
+
+ // Send the report.
+ report();
+ init = false;
+ }
+ }
+ catch (JMSException e)
+ {
+ log.warn("There was a JMSException during onMessage.", e);
+ }
+ finally
+ {
+ NDC.pop();
+ }
+ }
+
+ Message createReportResponseMessage(String msg) throws JMSException
+ {
+ Message message = _session.createTextMessage(msg);
+
+ // Shove some more field table type in the message just to see if the other end can handle it.
+ message.setBooleanProperty("BOOLEAN", true);
+ message.setByteProperty("BYTE", (byte) 5);
+ message.setDoubleProperty("DOUBLE", Math.PI);
+ message.setFloatProperty("FLOAT", 1.0f);
+ message.setIntProperty("INT", 1);
+ message.setShortProperty("SHORT", (short) 1);
+ message.setLongProperty("LONG", (long) 1827361278);
+ message.setStringProperty("STRING", "hello");
+
+ return message;
+ }
+
+ boolean isShutdown(Message m) throws JMSException
+ {
+ boolean result = checkTextField(m, "TYPE", "TERMINATION_REQUEST");
+
+ //log.debug("isShutdown = " + result);
+
+ return result;
+ }
+
+ boolean isReport(Message m) throws JMSException
+ {
+ boolean result = checkTextField(m, "TYPE", "REPORT_REQUEST");
+
+ //log.debug("isReport = " + result);
+
+ return result;
+ }
+
+ MessageConsumer createTopicConsumer() throws Exception
+ {
+ return _session.createConsumer(_topic);
+ }
+
+ MessageConsumer createDurableTopicConsumer(String name) throws Exception
+ {
+ return _session.createDurableSubscriber(_topic, name);
+ }
+
+ MessageProducer createControlPublisher() throws Exception
+ {
+ return _session.createProducer(_response);
+ }
+
+ private void shutdown()
+ {
+ try
+ {
+ _session.close();
+ _connection.stop();
+ _connection.close();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private void report()
+ {
+ log.debug("private void report(): called");
+
+ try
+ {
+ String msg = getReport();
+ _controller.send(createReportResponseMessage(msg));
+ log.debug("Sent report: " + msg);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ private String getReport()
+ {
+ long time = ((System.nanoTime() / 1000000) - start);
+
+ return "Received " + count + " in " + time + "ms";
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java b/Final/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java
new file mode 100644
index 0000000000..4efdc1cb56
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.topic;
+
+import javax.jms.*;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQTopic;
+import org.apache.qpid.exchange.ExchangeDefaults;
+
+/**
+ */
+class MessageFactory
+{
+ private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray();
+
+ private final Session _session;
+ private final Topic _topic;
+ private final Topic _control;
+ private final byte[] _payload;
+
+ MessageFactory(Session session) throws JMSException
+ {
+ this(session, 256);
+ }
+
+ MessageFactory(Session session, int size) throws JMSException
+ {
+ _session = session;
+ if (session instanceof AMQSession)
+ {
+ _topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, "topic_control");
+ _control = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, "topictest.control");
+ }
+ else
+ {
+ _topic = session.createTopic("topic_control");
+ _control = session.createTopic("topictest.control");
+ }
+
+ _payload = new byte[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ _payload[i] = (byte) DATA[i % DATA.length];
+ }
+ }
+
+ private static boolean checkText(Message m, String s)
+ {
+ try
+ {
+ return (m instanceof TextMessage) && ((TextMessage) m).getText().equals(s);
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+
+ return false;
+ }
+ }
+
+ Topic getTopic()
+ {
+ return _topic;
+ }
+
+ Message createEventMessage() throws JMSException
+ {
+ BytesMessage msg = _session.createBytesMessage();
+ msg.writeBytes(_payload);
+
+ return msg;
+ }
+
+ Message createShutdownMessage() throws JMSException
+ {
+ return _session.createTextMessage("SHUTDOWN");
+ }
+
+ Message createReportRequestMessage() throws JMSException
+ {
+ return _session.createTextMessage("REPORT");
+ }
+
+ Message createReportResponseMessage(String msg) throws JMSException
+ {
+ return _session.createTextMessage(msg);
+ }
+
+ boolean isShutdown(Message m)
+ {
+ return checkText(m, "SHUTDOWN");
+ }
+
+ boolean isReport(Message m)
+ {
+ return checkText(m, "REPORT");
+ }
+
+ Object getReport(Message m)
+ {
+ try
+ {
+ return ((TextMessage) m).getText();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(System.out);
+
+ return e.toString();
+ }
+ }
+
+ MessageConsumer createTopicConsumer() throws Exception
+ {
+ return _session.createConsumer(_topic);
+ }
+
+ MessageConsumer createDurableTopicConsumer(String name) throws Exception
+ {
+ return _session.createDurableSubscriber(_topic, name);
+ }
+
+ MessageConsumer createControlConsumer() throws Exception
+ {
+ return _session.createConsumer(_control);
+ }
+
+ MessageProducer createTopicPublisher() throws Exception
+ {
+ return _session.createProducer(_topic);
+ }
+
+ MessageProducer createControlPublisher() throws Exception
+ {
+ return _session.createProducer(_control);
+ }
+}
diff --git a/Final/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java b/Final/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java
new file mode 100644
index 0000000000..c3b19b558a
--- /dev/null
+++ b/Final/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java
@@ -0,0 +1,186 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.topic;
+
+import javax.jms.*;
+
+public class Publisher implements MessageListener
+{
+ private final Object _lock = new Object();
+ private final Connection _connection;
+ private final Session _session;
+ private final MessageFactory _factory;
+ private final MessageProducer _publisher;
+ private int _count;
+
+ Publisher(Connection connection, int size, int ackMode, boolean persistent) throws Exception
+ {
+ _connection = connection;
+ _session = _connection.createSession(false, ackMode);
+ _factory = new MessageFactory(_session, size);
+ _publisher = _factory.createTopicPublisher();
+ _publisher.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
+ System.out.println("Publishing " + (persistent ? "persistent" : "non-persistent") + " messages of " + size + " bytes, " + Config.getAckModeDescription(ackMode) + ".");
+ }
+
+ private void test(Config config) throws Exception
+ {
+ test(config.getBatch(), config.getDelay(), config.getMessages(), config.getClients(), config.getWarmup());
+ }
+
+ private void test(int batches, long delay, int msgCount, int consumerCount, int warmup) throws Exception
+ {
+ _factory.createControlConsumer().setMessageListener(this);
+ _connection.start();
+
+ if (warmup > 0)
+ {
+ System.out.println("Runing warmup (" + warmup + " msgs)");
+ long time = batch(warmup, consumerCount);
+ System.out.println("Warmup completed in " + time + "ms");
+ }
+
+ long[] times = new long[batches];
+ for (int i = 0; i < batches; i++)
+ {
+ if (i > 0)
+ {
+ Thread.sleep(delay * 1000);
+ }
+ times[i] = batch(msgCount, consumerCount);
+ System.out.println("Batch " + (i + 1) + " of " + batches + " completed in " + times[i] + " ms.");
+ }
+
+ long min = min(times);
+ long max = max(times);
+ System.out.println("min: " + min + ", max: " + max + " avg: " + avg(times, min, max));
+
+ //request shutdown
+ _publisher.send(_factory.createShutdownMessage());
+
+ _connection.stop();
+ _connection.close();
+ }
+
+ private long batch(int msgCount, int consumerCount) throws Exception
+ {
+ _count = consumerCount;
+ long start = System.currentTimeMillis();
+ publish(msgCount);
+ waitForCompletion(consumerCount);
+ return System.currentTimeMillis() - start;
+ }
+
+ private void publish(int count) throws Exception
+ {
+
+ //send events
+ for (int i = 0; i < count; i++)
+ {
+ _publisher.send(_factory.createEventMessage());
+ if ((i + 1) % 100 == 0)
+ {
+ System.out.println("Sent " + (i + 1) + " messages");
+ }
+ }
+
+ //request report
+ _publisher.send(_factory.createReportRequestMessage());
+ }
+
+ private void waitForCompletion(int consumers) throws Exception
+ {
+ System.out.println("Waiting for completion...");
+ synchronized (_lock)
+ {
+ while (_count > 0)
+ {
+ _lock.wait();
+ }
+ }
+ }
+
+
+ public void onMessage(Message message)
+ {
+ System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining");
+ if (_count == 0)
+ {
+ synchronized (_lock)
+ {
+ _lock.notify();
+ }
+ }
+ }
+
+ static long min(long[] times)
+ {
+ long min = times.length > 0 ? times[0] : 0;
+ for (int i = 0; i < times.length; i++)
+ {
+ min = Math.min(min, times[i]);
+ }
+ return min;
+ }
+
+ static long max(long[] times)
+ {
+ long max = times.length > 0 ? times[0] : 0;
+ for (int i = 0; i < times.length; i++)
+ {
+ max = Math.max(max, times[i]);
+ }
+ return max;
+ }
+
+ static long avg(long[] times, long min, long max)
+ {
+ long sum = 0;
+ for (int i = 0; i < times.length; i++)
+ {
+ sum += times[i];
+ }
+
+ int adjustment = 0;
+
+ // Remove min and max if we have run enough batches.
+ if (times.length > 2)
+ {
+ sum -= min;
+ sum -= max;
+ adjustment = 2;
+ }
+
+ return (sum / (times.length - adjustment));
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Config config = new Config();
+ config.setOptions(argv);
+
+ Connection con = config.createConnection();
+ int size = config.getPayload();
+ int ackMode = config.getAckMode();
+ boolean persistent = config.usePersistentMessages();
+ new Publisher(con, size, ackMode, persistent).test(config);
+ }
+}
diff --git a/Final/java/perftests/src/main/java/perftests.log4j b/Final/java/perftests/src/main/java/perftests.log4j
new file mode 100644
index 0000000000..3bd8c201f8
--- /dev/null
+++ b/Final/java/perftests/src/main/java/perftests.log4j
@@ -0,0 +1,45 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootLogger=${root.logging.level}
+
+
+log4j.logger.org.apache.qpid=${amqj.logging.level}, console
+log4j.additivity.org.apache.qpid=false
+
+log4j.logger.org.apache.qpid.requestreply=${amqj.test.logging.level}
+log4j.logger.org.apache.qpid.pingpong=${amqj.test.logging.level}
+log4j.logger.org.apache.qpid.ping=${amqj.test.logging.level}
+log4j.logger.org.apache.qpid.topic=${amqj.test.logging.level}
+
+
+log4j.logger.uk.co.thebadgerset.junit.extensions=${badger.level}, console
+log4j.additivity.uk.co.thebadgerset.junit.extensions=false
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+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
+#log4j.appender.console.layout.ConversionPattern=%t %p [%c] %m%n
+
+log4j.appender.fileApp=org.apache.log4j.FileAppender
+log4j.appender.fileApp.file=${log.dir}/perftests.volumetest.log
+log4j.appender.fileApp.Threshold=info
+log4j.appender.fileApp.append=false
+log4j.appender.fileApp.layout=org.apache.log4j.PatternLayout
diff --git a/Final/java/pom.xml b/Final/java/pom.xml
new file mode 100644
index 0000000000..28db0467d2
--- /dev/null
+++ b/Final/java/pom.xml
@@ -0,0 +1,773 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT 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</artifactId>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <packaging>pom</packaging>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/incubator/qpid/branches/M2/java</connection>
+ <developerConnection>scm:svn:http://svn.apache.org/repos/asf/incubator/qpid/branches/M2/java</developerConnection>
+ <url>http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java</url>
+ </scm>
+
+ <prerequisites>
+ <maven>2.0.4</maven>
+ </prerequisites>
+
+ <distributionManagement>
+
+ <snapshotRepository>
+ <id>apache.snapshots</id>
+ <name>Apache SNAPSHOT Repository</name>
+ <url>scp://people.apache.org/www/people.apache.org/repo/m2-snapshot-repository</url>
+ </snapshotRepository>
+
+ <repository>
+ <id>apache.incubating</id>
+ <name>Apache Incubating Repository</name>
+ <url>scp://people.apache.org/www/people.apache.org/repo/m2-incubating-repository</url>
+ </repository>
+
+ <!--
+ <snapshotRepository>
+ <uniqueVersion>true</uniqueVersion>
+ <id>snapshot-repo</id>
+ <name>Local Snapshot Repository</name>
+ <url>file://${basedir}/${topDirectoryLocation}/snapshots</url>
+ <layout>default</layout>
+ </snapshotRepository>
+ -->
+
+ <!-- Qpid has a Wiki site, maven generated site not used. This is just so that it can be created locally for viewing the reports. -->
+ <site>
+ <id>Qpid_Site</id>
+ <name>Qpid Site</name>
+ <url>file:/temp</url>
+ </site>
+
+ </distributionManagement>
+
+ <inceptionYear>2006</inceptionYear>
+ <mailingLists>
+ <mailingList>
+ <name>Qpid Developer List</name>
+ <subscribe>qpid-dev-subscribe@incubator.apache.org</subscribe>
+ <unsubscribe>qpid-dev-unsubscribe@incubator.apache.org</unsubscribe>
+ <post>qpid-dev@incubator.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/incubator-qpid-dev</archive>
+ </mailingList>
+ <mailingList>
+ <name>Qpid Commits List</name>
+ <subscribe>qpid-commits-subscribe@incubator.apache.org</subscribe>
+ <unsubscribe>qpid-commits-unsubscribe@incubator.apache.org</unsubscribe>
+ <post>qpid-commits@incubator.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/incubator-qpid-commits</archive>
+ </mailingList>
+ </mailingLists>
+
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <organization>
+ <name>Apache Software Foundation</name>
+ <url>http://www.apache.org/</url>
+ </organization>
+
+ <properties>
+ <topDirectoryLocation>.</topDirectoryLocation>
+
+ <surefire.fork.mode>never</surefire.fork.mode>
+ <surefire.format>brief</surefire.format>
+ <surefire.usefile>true</surefire.usefile>
+ <compile.forked>false</compile.forked>
+ <java.source.version>1.5</java.source.version>
+ <compile.flags>-Xlint:fallthrough,finally</compile.flags>
+
+ <!--
+ This should always point to a default minimal log4j configuration that all developers are happy with as a useable default. To use your own
+ log4j preferences set up an alternative in your settings.xml and avoid corrupting the default with private preferences.
+ -->
+ <!--<log4j.configuration>file:/${topDirectoryLocation}/etc/log4j.xml</log4j.configuration>-->
+ <amqj.logging.level>warn</amqj.logging.level> <!-- This is referenced in the default log4j.xml -->
+
+ <!--Versions for various plugins and features -->
+ <antrun.version>1.2-SNAPSHOT</antrun.version>
+ <!--<assembly.version>2.2-SNAPSHOT</assembly.version>-->
+ <assembly.version>2.1</assembly.version>
+ <cobertura.version>2.0</cobertura.version>
+ <compiler.version>2.0.1</compiler.version>
+ <dependency.plugin.version>1.0</dependency.plugin.version>
+ <eclipse.plugin.version>2.2</eclipse.plugin.version>
+ <jar.version>2.0</jar.version>
+ <javadoc.version>2.0</javadoc.version>
+ <junit.version>3.8.1</junit.version>
+ <jxr.version>2.0</jxr.version>
+ <mprojectinfo.version>2.0</mprojectinfo.version>
+ <resources.version>2.2</resources.version>
+ <site.version>2.0-beta-5</site.version>
+ <surefire-report.version>2.1-SNAPSHOT</surefire-report.version>
+ <surefire.version>2.2</surefire.version>
+ <retrotranslator.plugin.version>1.0-alpha-2-SNAPSHOT</retrotranslator.plugin.version>
+ <build-helper.plugin.version>1.0</build-helper.plugin.version>
+ <eclipse.workspace.dir>${basedir}/${topDirectoryLocation}/../workspace</eclipse.workspace.dir>
+ <clover.license.pathname>/set/clover/license/path/here</clover.license.pathname>
+
+ <!-- Override these in local settings.xml to perform verification. Cannot make assumptions about 1.4 Jdk location to turn this on by default. -->
+ <retrotranslator.verify>false</retrotranslator.verify>
+ <retrotranslator.1.4-rt-path>pathto/rt.jar</retrotranslator.1.4-rt-path>
+ <retrotranslator.1.4-jce-path>pathto/jce.jar</retrotranslator.1.4-jce-path>
+ <retrotranslator.1.4-jsse-path>pathto/jsse.jar</retrotranslator.1.4-jsse-path>
+ <retrotranslator.1.4-sasl-path>pathto/sasl.jar</retrotranslator.1.4-sasl-path>
+
+ </properties>
+
+ <modules>
+ <module>common</module>
+ <module>broker</module>
+ <module>client</module>
+ <module>cluster</module>
+ <module>systests</module>
+ <module>perftests</module>
+ <module>integrationtests</module>
+ <module>management/eclipse-plugin</module>
+ <module>client/example</module>
+ <module>client-java14</module>
+
+ </modules>
+
+
+ <build>
+ <resources>
+
+ <resource>
+ <targetPath>META-INF/</targetPath>
+ <filtering>false</filtering>
+ <directory>../resources</directory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ </includes>
+ </resource>
+
+ <resource>
+ <directory>src/main/java</directory>
+ <excludes>
+ <exclude>**/*.java</exclude>
+ <exclude>**/log4j.properties</exclude>
+ </excludes>
+ </resource>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ <resource>
+ <directory>src/main/resources-filtered</directory>
+ <filtering>true</filtering>
+ </resource>
+ <resource>
+ <directory>target/generated/src/main/resources</directory>
+ </resource>
+ </resources>
+
+ <testResources>
+ <testResource>
+ <targetPath>META-INF/</targetPath>
+ <filtering>false</filtering>
+ <directory>../resources</directory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ </includes>
+ </testResource>
+
+ <testResource>
+ <directory>src/test/java</directory>
+ <excludes>
+ <exclude>**/*.java</exclude>
+ </excludes>
+ </testResource>
+ <testResource>
+ <directory>src/test/resources</directory>
+ </testResource>
+ <testResource>
+ <directory>src/test/java</directory>
+ <excludes>
+ <exclude>**/*.xml</exclude>
+ </excludes>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
+
+ <pluginManagement>
+ <plugins>
+
+ <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>
+
+ <!-- This Ant task writes the module name, version and the Subversion version information out to a properties file.
+ The svnversion command must be available to run from the command line for this to work. The build will not fail if
+ svnversion cannot be run though.
+ This is done during the 'compile' phase to reflect the version of the currently compiled code and to ensure that
+ these properties are up to date when running from a file system classpath. Consider moving this to, or running a second
+ time, during the 'package' phase to capture the version of any resources added to jar files.
+ This svnversion command is always run in the top directory to accurately reflect the svnversion range accross all modules
+ at the time of the build.
+ The properties are placed into a file 'qpidversion.properties' in the target/classes directory of any child module
+ that runs this plugin.
+ The 'qpidversion.properties' file is loaded by the org.apache.qpid.common.QpidProperties class.
+ Be carefull of the possibility that the 'common' module may run this antrun plugin and recieve its own set of
+ qpidversion.properties and then the client or broker being built against an older version of the common library ending
+ up with the wrong version information. This is unlikely to happen because the client or broker should pick up its own
+ properties from the classpath first. If this happens it will be obvious because the productName property will be
+ 'Qpid Common Utilities'. If this is a problem then push this ant task down into the client and broker poms and remove it
+ from here.
+ -->
+ <execution>
+ <id>version_properties</id>
+ <phase>compile</phase>
+ <configuration>
+ <tasks>
+
+ <exec executable="svnversion" spawn="false" failifexecutionfails="false"
+ dir="${topDirectoryLocation}" outputproperty="svnversion">
+ <arg line="."/>
+ </exec>
+
+ <!-- Write the version.properties out. -->
+ <propertyfile file="target/classes/qpidversion.properties">
+ <entry key="qpid.svnversion" value="${svnversion}"/>
+ <entry key="qpid.name" value="${project.name}"/>
+ <entry key="qpid.version" value="${project.version}"/>
+ </propertyfile>
+
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>retrotranslator-maven-plugin</artifactId>
+ <version>${retrotranslator.plugin.version}</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>${build-helper.plugin.version}</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>${jar.version}</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>${resources.version}</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${compiler.version}</version>
+ <configuration>
+ <source>${java.source.version}</source>
+ <target>${java.source.version}</target>
+ <fork>${compile.forked}</fork>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire.version}</version>
+ <configuration>
+ <excludes>
+ <exclude>**/*$*</exclude>
+ </excludes>
+ <reportFormat>${surefire.format}</reportFormat>
+ <useFile>${surefire.usefile}</useFile>
+ <forkMode>${surefire.fork.mode}</forkMode>
+ <childDelegation>false</childDelegation>
+ <argLine>-ea</argLine>
+ <systemProperties>
+ <property>
+ <name>amqj.logging.level</name>
+ <value>${amqj.logging.level}</value>
+ </property>
+ <property>
+ <name>log4j.configuration</name>
+ <value>${log4j.configuration}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>${site.version}</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-eclipse-plugin</artifactId>
+ <version>${eclipse.plugin.version}</version>
+ <configuration>
+ <!--downloadSources>true</downloadSources-->
+ <buildcommands>
+ <java.lang.String>org.eclipse.jdt.core.javabuilder</java.lang.String>
+ </buildcommands>
+ <projectnatures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </projectnatures>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-idea-plugin</artifactId>
+ <configuration>
+ <!--downloadSources>true</downloadSources-->
+ <downloadJavadocs>true</downloadJavadocs>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-clover-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <licenseLocation>${clover.license.pathname}</licenseLocation>
+ <jdk>${java.source.version}</jdk>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>instrument</goal>
+ <goal>aggregate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>uk.co.thebadgerset</groupId>
+ <artifactId>junit-toolkit-maven-plugin</artifactId>
+ <version>0.6.1</version>
+ </plugin>
+
+
+
+
+ </plugins>
+ </pluginManagement>
+
+
+ <plugins>
+
+ <!-- Disabled as plugin crashes on the systest module.
+ Also, the resulting NOTICE file doesn't include all license info due to missing data in dependant poms.
+
+ <plugin>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <version>1.0-alpha-5</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <resourceBundles>
+ <resourceBundle>org.apache:apache-incubator-disclaimer-resource-bundle:1.1</resourceBundle>
+ <resourceBundle>org.apache:apache-jar-resource-bundle:1.2</resourceBundle>
+ </resourceBundles>
+ <properties>
+ <addLicense>true</addLicense>
+ <projectName>Apache Qpid</projectName>
+ </properties>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin-->
+ </plugins>
+
+ <defaultGoal>install</defaultGoal>
+
+ </build>
+
+ <dependencyManagement>
+ <dependencies>
+
+ <!-- Comile time only dependencies. -->
+ <dependency>
+ <groupId>net.sf.retrotranslator</groupId>
+ <artifactId>retrotranslator-runtime</artifactId>
+ <version>1.2.1</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Compilation and run time dependecies. -->
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.3</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-configuration</groupId>
+ <artifactId>commons-configuration</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.0.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jms_1.1_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>xml-resolver</groupId>
+ <artifactId>xml-resolver</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sf.saxon</groupId>
+ <artifactId>saxon</artifactId>
+ <version>8.7</version>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.12</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.4.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-filter-ssl</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-java5</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>backport-util-concurrent</groupId>
+ <artifactId>backport-util-concurrent</artifactId>
+ <version>2.2</version>
+ </dependency>
+
+ <!-- Test Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <version>2.2</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>uk.co.thebadgerset</groupId>
+ <artifactId>junit-toolkit</artifactId>
+ <version>0.6.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Qpid Version Dependencies -->
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-perftests</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-systests</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-mgmt-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-mgmt-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-cluster</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <reporting>
+ <plugins>
+ <!--
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ <version>${cobertura.version}</version>
+ </plugin>
+ -->
+
+ <!-- Run the javadoc report. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <tags>
+ <tag>
+ <name>todo</name>
+ <placement>a</placement>
+ <head>To do:</head>
+ </tag>
+ </tags>
+ </configuration>
+ </plugin>
+
+ <!-- Generate the clover coverage report. -->
+ <!--
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-clover-plugin</artifactId>
+ </plugin>
+ -->
+
+ <!-- Standard Maven project info reports. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <version>${mprojectinfo.version}</version>
+ </plugin>
+
+ <!-- Generate the surefire test report. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>${surefire-report.version}</version>
+ </plugin>
+
+ <!-- Generate the TODO lists. -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>taglist-maven-plugin</artifactId>
+ </plugin>
+
+ <!-- Generate the source code cross reference. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ </plugin>
+
+ <!-- Minimal checkstyle rules. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <configLocation>${basedir}/${topDirectoryLocation}/etc/coding_standards.xml</configLocation>
+ <headerLocation>${basedir}/${topDirectoryLocation}/etc/license_header.txt</headerLocation>
+ </configuration>
+ </plugin>
+
+ <!-- Generate report on changed files. -->
+ <!--
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changelog-plugin</artifactId>
+ </plugin>
+ -->
+
+ </plugins>
+ </reporting>
+
+ <repositories>
+ <!-- not picking up any snapshots at the moment
+ <repository>
+ <id>apache.snapshots</id>
+ <name>Apache SNAPSHOT Repository</name>
+ <url>http://people.apache.org/repo/m2-snapshot-repository</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ -->
+ <repository>
+ <id>Codehaus Snapshots</id>
+ <url>http://snapshots.repository.codehaus.org/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ </repository>
+ </repositories>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>apache.snapshots</id>
+ <name>Apache SNAPSHOT Repository</name>
+ <url>http://people.apache.org/repo/m2-snapshot-repository</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+
+ <pluginRepository>
+ <id>codehaus.snapshots</id>
+ <name>Codehaus SNAPSHOT Repository</name>
+ <url>http://snapshots.repository.codehaus.org</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+
+ </pluginRepositories>
+
+ <profiles>
+ <profile>
+ <id>fastinstall</id>
+ <properties>
+ <maven.test.skip>true</maven.test.skip>
+ <skip.python.tests>true</skip.python.tests>
+ </properties>
+ </profile>
+
+ <profile>
+ <id>nochecks</id>
+ </profile>
+
+ <profile>
+ <id>setup.eclipse</id>
+ <build>
+ <defaultGoal>process-test-sources</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-eclipse-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>setup.eclipse.project</id>
+ <phase>process-test-sources</phase>
+ <goals>
+ <goal>eclipse</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>setup.eclipse.workspace</id>
+ <phase>process-test-sources</phase>
+ <goals>
+ <goal>add-maven-repo</goal>
+ </goals>
+ <configuration>
+ <workspace>${eclipse.workspace.dir}</workspace>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/Final/java/release-docs/RELEASE_NOTES.txt b/Final/java/release-docs/RELEASE_NOTES.txt
new file mode 100644
index 0000000000..ed93f3e450
--- /dev/null
+++ b/Final/java/release-docs/RELEASE_NOTES.txt
@@ -0,0 +1,160 @@
+Apache Incubator Qpid Java M2 Release Notes
+-------------------------------------------
+
+The Qpid M2 release contains support the for AMQP 0-8 specification.
+You can access the 0-8 specification using the following link.
+http://www.amqp.org/tikiwiki/tiki-index.php?page=Download
+
+For full details of Qpid capabilities, as they currently stand, see our
+detailed project documentation at:
+
+http://cwiki.apache.org/confluence/display/qpid/Qpid+Java+Documentation
+
+From the link above you can access our Getting Started Guide, FAQ, Build How To
+and detailed developer documentation.
+
+
+Known Issues/Outstanding Work
+-----------------------------
+
+You can view the outstanding task list for Qpid by visiting our JIRA:
+http://issues.apache.org/jira/browse/QPID
+
+Please note that most of the issues are fixed, but are kept open until the
+merged with trunk, at which point they will be closed
+
+Here is a filtered list for your convinience
+---------------------------------------
+D-560 Add DiagnosticExchange exchange
+QPID-543 Add ability to register custom exchange types
+QPID-274 add connection configuratble timeout on
+waituntilStateHasChanged
+QPID-156 Implement persistent store with Apache compliant licence
+QPID-155 Add ability to configure (on/off) queue creation on demand
+QPID-43 Multiple-AMQP version support in the broker
+QPID-28 Allow user to select policy for undeliverable message handling
+QPID-27 Introduce user configurable redlivery delay
+QPID-22 Provide run scripts for clustered broker
+QPID-592 Limit number of bytes in write buffer queue
+QPID-583 Reinstate the old topic performance test
+QPID-582 Ensure Java codebase builds with Java 6
+QPID-567 Add basic multiversion support to Qpid for interop between M1,
+M2 and 0-8 and 0-9 AMQP implementations
+QPID-564 Reapply MINA performance patches
+QPID-430 Message Age Alerting should not depend upon queue activity
+QPID-19 Add protocol logging capability to client and broker
+QPID-11 Move protocol literals from code to AMQConstant
+QPID-659 Messages sent to a queue that uses selectors may cause high
+CPU load.
+QPID-654 ThreadPoolExecutor.getTask() can be seen WAITING causing test
+cases to hang
+QPID-653 Timeouts in tests causing spurious failures on slower machines
+QPID-647 Delivery Manager can stop starving consumers.
+QPID-645 Exception thrown while preparing TxnOp not logged or
+propogated
+QPID-643 CSDM causes duplicate message delivery.
+QPID-637 BasicConsumer.recieve can return null before the timeout
+expires due to InterruptedException
+QPID-632 ImmediateMessageTest fails occasionally because
+AMQNoConsumersException was not received.
+QPID-626 AMQSession deadlocks
+QPID-623 If all consumers of a queue use selectors then the broker will
+leak memory
+QPID-611 Queue Total size calculations can cause NPE on broker.
+QPID-610 Usages of queue.dequeue need to be checked to ensure memory
+loss doesn't occur
+QPID-609 Dispatcher threads are not always killed
+QPID-608 ConnectionTest fails because VM broker is not created
+QPID-607 Dispatcher threads do not die
+QPID-606 NPE from several systests occasionally
+QPID-604 Potential NullPointerException in FieldTable trace logging
+QPID-599 maven runs tests twice in client module
+QPID-594 Java client does not throw exceptions for failures during
+connection establishment
+QPID-590 Client can deadlock when session is accessed from two threads
+QPID-586 Client state manager is not interupted when unrecoverable
+error occurs on connection
+QPID-585 Client protocol state is maintiained between connections when
+the connection fails during initial connection
+QPID-584 Client allows RuntimeExceptions to be silently swallowed.
+QPID-579 Qpid Broker process fails to terminate on configuration errors
+QPID-578 AMQChannel.queueDeletion causes unacked msgs to be discarded.
+QPID-577 Setting a MessageListener after the connection has been
+started may result in apparent message loss.
+QPID-573 race condition between rollback() and the dispatcher thread in
+the java client
+QPID-572 broker delivers messages out of order
+QPID-558 AMQShortString should autoexpand when adding content to it
+QPID-549 AMQConnection.start() is not threadsafe
+QPID-545 Using a private queue with selector will result in non
+matching messages being left on the broker queue as they will not be consumed.
+QPID-539 HeadersExchange doesnot correctly implement isBound
+QPID-531 [Memory Leak] Broker retains messages that are consumed with
+NO_ACK
+QPID-469 Redelivered information is currently recorded per message it
+should be per message per queue.
+QPID-463 Java client doesn't close connection gracefully when faced
+with broker with unsuported protocol version
+QPID-462 Exclusive queues and with subscription that 'filtersMessages'
+will build up messages it doesn't hasInterest() in.
+QPID-397 Client closeure can be processed before final message ack.
+QPID-396 Broker OutOfMemory Error handling
+QPID-377 NumberFormatException thrown by broker when running one
+performance test
+QPID-293 setting MessageListener after connection has started can cause
+messages to be "lost" on a internal delivery queue.
+QPID-185 Amend Java Broker handling of ifUnused & ifEmpty flags
+QPID-168 qpid-server.bat needs updated to support same
+arguments/features as qpid-server (via qpid-run) bash script
+Bug QPID-168 qpid-server.bat needs updated to support same arguments/features as qpid-server (via qpid-run) bash script
+
+
+M2 Tasks Completed
+-------------------
+
+The set of JIRA tasks completed as part of the M2 effort is available at:
+https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12310520&styleName=Html&version=12312116
+
+
+Here is a filtered (by Java components) version of the JIRA items
+
+QPID-190 refactoring the java broker mbean classes
+QPID-125 Moving eclipse plugin for broker management to Maven
+QPID-93 Delete the old management modules (trunk\qpid\java\management)
+QPID-418 Add ability to save user preferences to Java Management Console
+QPID-482 [Java] Small performance tweaks
+QPID-466 Create STRICT_AMQP System property to disable JMS extensions in Java client
+QPID-453 AMQShortString should implement Comparable
+QPID-421 Provide enumerated description for static constants including delivery mode
+QPID-420 Add client id to information displayed about connections on management console
+QPID-419 Introduce read-only and modify authorisation for all objects in a virtual host
+QPID-129 improving Broker MBeans
+QPID-616 Underflow in calculating message pending size in perftests.
+QPID-614 Race condition on queue browser close
+QPID-612 Duplicate temporary queue names when running unit tests
+QPID-600 Deadlocks on Connection.close
+QPID-540 Transient Broker throws NullPointerException and locks up.
+QPID-538 [Memory Leak] Connecting lots of consumers causes the broker memory to leak
+QPID-537 Make AMQMessage.incrementReference public
+QPID-527 encoding issue
+QPID-508 [Memory Leak] Broker does not return mandatory messages sent outside of a
+transaction.
+QPID-476 AMQProtocolSession channelId2SessionMap does not have sessions removed
+QPID-472 Creation of TemporaryQueues will not guarantee unqiue queue names if created
+rapidly.
+QPID-471 UserManagement panel lists all users but only after a View Users has been
+executed and is not updated on Create/Delete User
+QPID-467 Complete Interop Testing
+QPID-465 Incorrect Exception thrown from send() method.
+QPID-459 Broker doesn't correctly handle noLocal consumers when messages are
+pre-exisiting on queues.
+QPID-458 Queue Browsing Broken
+QPID-454 Message 'taken' notion is per message. But should be per message per queue
+QPID-443 Abruptly disconnecting client on transaction publish causes error
+QPID-440 Can create dangling transactions on unroutable messages.
+QPID-414 Authentication requires plain text passwords in password file
+QPID-408 Queue Depth data incorrect
+QPID-200 set/get Destination not implemented in JMSMessage impl
+QPID-166 Check for pre conditions to satisfy JMS spec requirments
+QPID-159 The following Interface implementations do not throw Exceptions as required by
+the spec
diff --git a/Final/java/resources/DISCLAIMER b/Final/java/resources/DISCLAIMER
new file mode 100644
index 0000000000..c321113c9e
--- /dev/null
+++ b/Final/java/resources/DISCLAIMER
@@ -0,0 +1,5 @@
+Apache Qpid is an effort undergoing incubation at the Apache Software Foundation (ASF), sponsored by the Apache Incubator PMC.
+
+Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects.
+
+While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.
diff --git a/Final/java/resources/LICENSE b/Final/java/resources/LICENSE
new file mode 100644
index 0000000000..82ace4848e
--- /dev/null
+++ b/Final/java/resources/LICENSE
@@ -0,0 +1,341 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+=========================================================================
+== SL4Fj License ==
+=========================================================================
+
+SLF4J License
+
+SLF4J source code and binaries are distributed under the following license.
+Copyright (c) 2004-2007 QOS.ch All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+=========================================================================
+== Public Domain License for Backport of JSR 166 ==
+=========================================================================
+
+Copyright-Only Dedication (based on United States law) or Public Domain Certification
+
+The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below.
+
+A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain.
+
+Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work.
+
+Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived.
+
+=========================================================================
+== Eclipse Public License ==
+=========================================================================
+
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
+
+
+
+=========================================================================
+== ICU License ==
+=========================================================================
+ICU License - ICU 1.8.1 and later
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2006 International Business Machines Corporation and others
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the property of their respective owners.
+
+
+
diff --git a/Final/java/resources/META-INF/DISCLAIMER b/Final/java/resources/META-INF/DISCLAIMER
new file mode 100644
index 0000000000..1ca63e46e2
--- /dev/null
+++ b/Final/java/resources/META-INF/DISCLAIMER
@@ -0,0 +1,10 @@
+Apache Qpid is an effort undergoing incubation at the Apache Software
+Foundation (ASF), sponsored by the Apache Incubator PMC.
+
+Incubation is required of all newly accepted projects until a further review
+indicates that the infrastructure, communications, and decision making process
+have stabilized in a manner consistent with other successful ASF projects.
+
+While incubation status is not necessarily a reflection of the completeness
+or stability of the code, it does indicate that the project has yet to be
+fully endorsed by the ASF.
diff --git a/Final/java/resources/META-INF/LICENSE b/Final/java/resources/META-INF/LICENSE
new file mode 100644
index 0000000000..6b0b1270ff
--- /dev/null
+++ b/Final/java/resources/META-INF/LICENSE
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/Final/java/resources/META-INF/NOTICE b/Final/java/resources/META-INF/NOTICE
new file mode 100644
index 0000000000..f62ec14896
--- /dev/null
+++ b/Final/java/resources/META-INF/NOTICE
@@ -0,0 +1,105 @@
+// ------------------------------------------------------------------
+// NOTICE file corresponding to the section 4d of The Apache License,
+// Version 2.0, in this case for Qpid Common Utilities
+// ------------------------------------------------------------------
+
+Apache Qpid
+Copyright 2006-2007 Apache Software Foundation
+
+This product includes software developed at
+Apache Software Foundation (http://www.apache.org/)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Unnamed - relaxngDatatype:relaxngDatatype:jar:20020414 (http://sourceforge.net/projects/relaxng)
+License: BSD License (http://www.opensource.org/licenses/bsd-license.php)
+
+This product includes/uses software, Apache MINA Core API (http://directory.apache.org/projects/mina/)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Unnamed - isorelax:isorelax:jar:20020414
+License: MIT license (http://www.opensource.org/licenses/mit-license.html)
+
+This product includes/uses software, SLF4J API Module (http://www.slf4j.org),
+developed by QOS.ch (http://www.qos.ch)
+License: MIT License (http://www.slf4j.org/license.html)
+
+This product includes/uses software, Commons Collections - commons-collections:commons-collections:jar:3.1,
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Digester - commons-digester:commons-digester:jar:1.6
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons CLI - commons-cli:commons-cli:jar:1.0
+Ideveloped by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Unnamed - msv:msv:jar:20020414
+developed by (https://msv.dev.java.net/)
+License:
+
+This product includes/uses software, Codec (http://jakarta.apache.org/commons/codec/),
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Logging - commons-logging:commons-logging:jar:1.0
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Backport of JSR 166 (http://www.mathcs.emory.edu/dcl/util/backport-util-concurrent/),
+developed by Dawid Kurzyniec (http://www.mathcs.emory.edu/~dawidk/)
+License: Public Domain (http://creativecommons.org/licenses/publicdomain)
+
+This product includes/uses software, Commons Lang - commons-lang:commons-lang:jar:2.1
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Apache MINA SSL Filter (http://directory.apache.org/subprojects/mina/mina-filter-ssl)
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Unnamed - xerces:xercesImpl:jar:2.2.1
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, - javax.servlet:servlet-api:jar:2.3
+
+This product includes/uses software, Xalan - xalan:xalan:jar:2.7.0
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Configuration (http://jakarta.apache.org/commons/),
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Apache MINA Java5 Extensions (http://directory.apache.org/subprojects/mina/mina-java5)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Jaxen - jaxen:jaxen:jar:1.0-FCS
+License: Apache License (http://jaxen.org/faq.html)
+
+This product includes/uses software, BeanUtils (http://jakarta.apache.org/commons/beanutils/)
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, XML Commons External Components XML APIs (http://xml.apache.org/commons/#external),
+developed by Apache Software Foundation (http://www.apache.org/)
+License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+This product includes/uses software, Commons Beanutils Core - commons-beanutils:commons-beanutils-core:jar:1.7.0
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Logging API - commons-logging:commons-logging-api:jar:1.0.4
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Dom4j - dom4j:dom4j:jar:1.4
+developed by MetaStuff, Ltd. (http://www.dom4j.org/)
+License: BSD License (http://www.dom4j.org/license.html)
+
+This product includes/uses software, Saxon - saxpath:saxpath:jar:1.0-FCS
+developed by Michael Kay (http://saxon.sourceforge.net/)
+License: Mozilla Public License v1.0, (http://www.opensource.org/licenses/mozilla1.0.php)
+
diff --git a/Final/java/resources/NOTICE b/Final/java/resources/NOTICE
new file mode 100644
index 0000000000..8ec21adde2
--- /dev/null
+++ b/Final/java/resources/NOTICE
@@ -0,0 +1,79 @@
+// ------------------------------------------------------------------
+// NOTICE file corresponding to the section 4d of The Apache License,
+// Version 2.0, in this case for Qpid Common Utilities
+// ------------------------------------------------------------------
+
+Apache Qpid
+Copyright 2006-2007 Apache Software Foundation
+
+This product includes software developed at
+Apache Software Foundation (http://www.apache.org/)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, SLF4J API Module (http://www.slf4j.org),
+developed by QOS.ch (http://www.qos.ch)
+Source included with binary builds at : http://slf4j.org/dist
+License: MIT License (http://www.slf4j.org/license.html)
+
+This product includes/uses software, Backport of JSR 166 (http://www.mathcs.emory.edu/dcl/util/backport-util-concurrent/),
+developed by Dawid Kurzyniec (http://www.mathcs.emory.edu/~dawidk/)
+License: Public Domain (http://creativecommons.org/licenses/publicdomain)
+
+This product includes/uses software, JUnit Toolkit (http://sourceforge.net/projects/junit-toolkit/)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Apache Log4j (http://logging.apache.org/log4j/1.2)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Apache MINA Core API (http://directory.apache.org/projects/mina/)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Apache MINA SSL Filter (http://directory.apache.org/subprojects/mina/mina-filter-ssl)
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Apache MINA Java5 Extensions (http://directory.apache.org/subprojects/mina/mina-java5)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Configuration (http://jakarta.apache.org/commons/),
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Logging API - commons-logging:commons-logging-api:jar:1.0.4
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Collections - commons-collections:commons-collections:jar:3.1,
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons CLI - commons-cli:commons-cli:jar:1.0
+Ideveloped by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Codec (http://jakarta.apache.org/commons/codec/),
+developed by The Apache Software Foundation (http://jakarta.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Commons Lang - commons-lang:commons-lang:jar:2.1
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product includes/uses software, Xalan - xalan:xalan:jar:2.7.0
+developed by Apache Software Foundation (http://www.apache.org)
+License: Apache 2.0 License (http://www.apache.org/licenses/LICENSE-2.0)
+
+This product uses/references the following software from Eclipse.
+License: Eclipse Public License - v 1.0 (http://www.eclipse.org/legal/epl-v10.html)
+(Note: the versions are ommited and only the name is specified.)
+org.eclipse.jface
+org.eclipse.core.*
+org.eclipse.equinox.*
+org.eclipse.osgi
+org.eclipse.swt
+Source available from : http://archive.eclipse.org/eclipse/downloads/drops/R-3.2-200606291905/index.php
+
+This product includes/uses software, ICU4J - ICU4J 3.4.4
+developed by the ICU Project (http://icu-project.org/)
+License: http://source.icu-project.org/repos/icu/icu/trunk/license.html
+
diff --git a/Final/java/resources/README b/Final/java/resources/README
new file mode 100644
index 0000000000..1d52d487fb
--- /dev/null
+++ b/Final/java/resources/README
@@ -0,0 +1,40 @@
+
+Documentation
+--------------
+All of our user documentation for the Qpid Java components can be accessed on our wiki at:
+
+http://cwiki.apache.org/confluence/display/qpid/Qpid+Java+Documentation
+
+This includes a Getting Started Guide and FAQ as well as detailed developer documentation.
+However, here's a VERY quick guide to running the installed Qpid broker, once you have installed it somewhere !
+
+
+Running the Broker
+------------------
+
+To run the broker, set the QPID_HOME environment variable to
+distribution directory and add $QPID_HOME/bin to your PATH. Then run
+the qpid-server shell script or qpid-server.bat batch file to start
+the broker. By default, the broker will use $QPID_HOME/etc to find
+the configuration files. You can supply a custom configuration using
+the -c argument.
+
+For example:
+
+qpid-server -c ~/etc/config.xml
+
+You can get a list of all command line arguments by using the -h argument.
+
+
+Developing
+----------
+
+In order to build Qpid you need Ant 1.6.5. Use ant -p to list the
+available targets. The default ant target, build, creates a working
+development-mode distribution in the build directory. To run the
+scripts in build/bin set QPID_HOME to the build directory and put
+${QPID_HOME}/bin on your PATH. The scripts in that directory include
+the standard ones in the distribution and a number of testing scripts.
+
+
+
diff --git a/Final/java/systests/distribution/pom.xml b/Final/java/systests/distribution/pom.xml
new file mode 100644
index 0000000000..172a59d3a1
--- /dev/null
+++ b/Final/java/systests/distribution/pom.xml
@@ -0,0 +1,112 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<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-systests-distribution</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid System Tests Distribution</name>
+ <url>http://cwiki.apache.org/confluence/display/qpid</url>
+
+ <parent>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid</artifactId>
+ <version>1.0-incubating-M2</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ <java.source.version>1.5</java.source.version>
+ <qpid.version>${pom.version}</qpid.version>
+ <qpid.targetDir>${project.build.directory}</qpid.targetDir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-systests</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <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>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>${assembly.version}</version>
+ <configuration>
+ <finalName>qpid-${pom.version}</finalName>
+ <outputDirectory>${qpid.targetDir}</outputDirectory>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <finalName>qpid-systests</finalName>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/systests.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ </build>
+
+</project>
diff --git a/Final/java/systests/distribution/src/main/assembly/systests.xml b/Final/java/systests/distribution/src/main/assembly/systests.xml
new file mode 100644
index 0000000000..2d6a6d8572
--- /dev/null
+++ b/Final/java/systests/distribution/src/main/assembly/systests.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<assembly>
+ <id>system-test-java</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+<fileSets>
+ <!-- Apache Licensing Details-->
+ <fileSet>
+ <directory>../../resources</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>DISCLAIMER</include>
+ <include>LICENSE.txt</include>
+ <include>NOTICE.txt</include>
+ <include>README.txt</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>..</directory>
+ <outputDirectory>qpid-${qpid.version}</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>../../release-docs</directory>
+ <outputDirectory>qpid-${qpid.version}/docs</outputDirectory>
+ <includes>
+ <include>RELEASE_NOTES.txt</include>
+ </includes>
+ </fileSet>
+
+ <!-- Scripts to run the system tests-->
+ <fileSet>
+ <directory>../bin</directory>
+ <outputDirectory>qpid-${qpid.version}/bin</outputDirectory>
+ <includes>
+ <include>*</include>
+ </includes>
+ </fileSet>
+
+ <!-- Include source files in easy access form -->
+ <fileSet>
+ <directory>../src/main</directory>
+ <outputDirectory>qpid-${qpid.version}/src</outputDirectory>
+ <includes>
+ <include>**/*.java</include>
+ <include>**/*.log4j</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <includes>
+ <include>qpid-systests.jar</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>qpid-${qpid.version}/lib</outputDirectory>
+ <unpack>false</unpack>
+ <excludes>
+ <exclude>org.apache.qpid:qpid-systests-distribution</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/Final/java/systests/etc/bin/testclients.sh b/Final/java/systests/etc/bin/testclients.sh
new file mode 100644
index 0000000000..6aaa7c408e
--- /dev/null
+++ b/Final/java/systests/etc/bin/testclients.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+for x in `seq 1 $1`;
+do
+ java -cp qpid-integrationtests-1.0-incubating-M2-SNAPSHOT-all-test-deps.jar -Dlog4j.configuration=file:/home/rupert/qpid/trunk/qpid/java/etc/mylog4j.xml org.apache.qpid.test.framework.distributedtesting.TestClient -n java$x &
+done
diff --git a/Final/java/systests/pom.xml b/Final/java/systests/pom.xml
new file mode 100644
index 0000000000..b18bfc93de
--- /dev/null
+++ b/Final/java/systests/pom.xml
@@ -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.
+-->
+<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-systests</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-incubating-M2</version>
+ <name>Qpid System 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</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-client</artifactId>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker</artifactId>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>uk.co.thebadgerset</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>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <testSourceDirectory>${basedir}/src/main</testSourceDirectory>
+ <testClassesDirectory>target/classes</testClassesDirectory>
+ <includes>
+ <include>**/*Test.class</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ </plugins>
+
+ <!-- Include source files in built jar -->
+ <resources>
+ <resource>
+ <targetPath>src/</targetPath>
+ <filtering>false</filtering>
+ <directory>src/main/java</directory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </resource>
+ <resource>
+ <targetPath>src/</targetPath>
+ <filtering>false</filtering>
+ <directory>src/main/java</directory>
+ <includes>
+ <include>systests.log4j</include>
+ </includes>
+ </resource>
+ </resources>
+ </build>
+</project>
+
diff --git a/Final/java/systests/src/main/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java b/Final/java/systests/src/main/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java
new file mode 100644
index 0000000000..5323ad28bf
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.mina.transport.vmpipe.support;
+
+import org.apache.mina.common.IdleStatus;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This file is a patch to override MINA, because of the IdentityHashMap bug. Workaround to be supplied in MINA 1.0.7.
+ * This patched file will be removed once upgraded onto a newer MINA.
+ *
+ * Dectects idle sessions and fires <tt>sessionIdle</tt> events to them.
+ *
+ * @author The Apache Directory Project (mina-dev@directory.apache.org)
+ */
+public class VmPipeIdleStatusChecker
+{
+ private static final VmPipeIdleStatusChecker INSTANCE = new VmPipeIdleStatusChecker();
+
+ public static VmPipeIdleStatusChecker getInstance()
+ {
+ return INSTANCE;
+ }
+
+ private final Map sessions = new HashMap(); // will use as a set
+
+ private final Worker worker = new Worker();
+
+ private VmPipeIdleStatusChecker()
+ {
+ worker.start();
+ }
+
+ public void addSession(VmPipeSessionImpl session)
+ {
+ synchronized (sessions)
+ {
+ sessions.put(session, session);
+ }
+ }
+
+ private class Worker extends Thread
+ {
+ private Worker()
+ {
+ super("VmPipeIdleStatusChecker");
+ setDaemon(true);
+ }
+
+ public void run()
+ {
+ for (;;)
+ {
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ { }
+
+ long currentTime = System.currentTimeMillis();
+
+ synchronized (sessions)
+ {
+ Iterator it = sessions.keySet().iterator();
+ while (it.hasNext())
+ {
+ VmPipeSessionImpl session = (VmPipeSessionImpl) it.next();
+ if (!session.isConnected())
+ {
+ it.remove();
+ }
+ else
+ {
+ notifyIdleSession(session, currentTime);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void notifyIdleSession(VmPipeSessionImpl session, long currentTime)
+ {
+ notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.BOTH_IDLE), IdleStatus.BOTH_IDLE,
+ Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE)));
+ notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.READER_IDLE), IdleStatus.READER_IDLE,
+ Math.max(session.getLastReadTime(), session.getLastIdleTime(IdleStatus.READER_IDLE)));
+ notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.WRITER_IDLE), IdleStatus.WRITER_IDLE,
+ Math.max(session.getLastWriteTime(), session.getLastIdleTime(IdleStatus.WRITER_IDLE)));
+ }
+
+ private void notifyIdleSession0(VmPipeSessionImpl session, long currentTime, long idleTime, IdleStatus status,
+ long lastIoTime)
+ {
+ if ((idleTime > 0) && (lastIoTime != 0) && ((currentTime - lastIoTime) >= idleTime))
+ {
+ session.increaseIdleCount(status);
+ session.getFilterChain().fireSessionIdle(session, status);
+ }
+ }
+
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java
new file mode 100644
index 0000000000..370c2b43a7
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.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.server;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.management.ManagedBroker;
+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.virtualhost.VirtualHost;
+
+public class AMQBrokerManagerMBeanTest extends TestCase
+{
+ private QueueRegistry _queueRegistry;
+ private ExchangeRegistry _exchangeRegistry;
+
+ public void testExchangeOperations() throws Exception
+ {
+ String exchange1 = "testExchange1_" + System.currentTimeMillis();
+ String exchange2 = "testExchange2_" + System.currentTimeMillis();
+ String exchange3 = "testExchange3_" + System.currentTimeMillis();
+
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) == null);
+
+ VirtualHost vHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test");
+
+ ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHost.VirtualHostMBean) vHost.getManagedObject());
+ mbean.createNewExchange(exchange1, "direct", false);
+ mbean.createNewExchange(exchange2, "topic", false);
+ mbean.createNewExchange(exchange3, "headers", false);
+
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) != null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) != null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) != null);
+
+ mbean.unregisterExchange(exchange1);
+ mbean.unregisterExchange(exchange2);
+ mbean.unregisterExchange(exchange3);
+
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) == null);
+ assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) == null);
+ }
+
+ public void testQueueOperations() throws Exception
+ {
+ String queueName = "testQueue_" + System.currentTimeMillis();
+ VirtualHost vHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test");
+
+ ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHost.VirtualHostMBean) vHost.getManagedObject());
+
+ assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) == null);
+
+ mbean.createNewQueue(queueName, "test", false);
+ assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) != null);
+
+ mbean.deleteQueue(queueName);
+ assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) == null);
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ _queueRegistry = appRegistry.getVirtualHostRegistry().getVirtualHost("test").getQueueRegistry();
+ _exchangeRegistry = appRegistry.getVirtualHostRegistry().getVirtualHost("test").getExchangeRegistry();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
new file mode 100644
index 0000000000..3ee8277eba
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
@@ -0,0 +1,232 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.ack;
+
+import junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.queue.AMQMessage;
+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 java.util.*;
+
+public class TxAckTest extends TestCase
+{
+ private Scenario individual;
+ private Scenario multiple;
+ private Scenario combined;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ //ack only 5th msg
+ individual = new Scenario(10, Arrays.asList(5l), Arrays.asList(1l, 2l, 3l, 4l, 6l, 7l, 8l, 9l, 10l));
+ individual.update(5, false);
+
+ //ack all up to and including 5th msg
+ multiple = new Scenario(10, Arrays.asList(1l, 2l, 3l, 4l, 5l), Arrays.asList(6l, 7l, 8l, 9l, 10l));
+ multiple.update(5, true);
+
+ //leave only 8th and 9th unacked
+ combined = new Scenario(10, Arrays.asList(1l, 2l, 3l, 4l, 5l, 6l, 7l, 10l), Arrays.asList(8l, 9l));
+ combined.update(3, false);
+ combined.update(5, true);
+ combined.update(7, true);
+ combined.update(2, true);//should be ignored
+ combined.update(1, false);//should be ignored
+ combined.update(10, false);
+ }
+
+ public void testPrepare() throws AMQException
+ {
+ individual.prepare();
+ multiple.prepare();
+ combined.prepare();
+ }
+
+ public void testUndoPrepare() throws AMQException
+ {
+ individual.undoPrepare();
+ multiple.undoPrepare();
+ combined.undoPrepare();
+ }
+
+ public void testCommit() throws AMQException
+ {
+ individual.commit();
+ multiple.commit();
+ combined.commit();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TxAckTest.class);
+ }
+
+ private class Scenario
+ {
+ private final UnacknowledgedMessageMap _map = new UnacknowledgedMessageMapImpl(5000);
+ private final TxAck _op = new TxAck(_map);
+ private final List<Long> _acked;
+ private final List<Long> _unacked;
+ private StoreContext _storeContext = new StoreContext();
+
+ Scenario(int messageCount, List<Long> acked, List<Long> unacked)
+ {
+ TransactionalContext txnContext = new NonTransactionalContext(new TestableMemoryMessageStore(),
+ _storeContext, null,
+ new LinkedList<RequiredDeliveryException>(),
+ new HashSet<Long>());
+ for (int i = 0; i < messageCount; i++)
+ {
+ long deliveryTag = i + 1;
+
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ TestMessage message = new TestMessage(deliveryTag, i, info, txnContext);
+ _map.add(deliveryTag, new UnacknowledgedMessage(null, message, null, deliveryTag));
+ }
+ _acked = acked;
+ _unacked = unacked;
+ }
+
+ void update(long deliverytag, boolean multiple)
+ {
+ _op.update(deliverytag, multiple);
+ }
+
+ private void assertCount(List<Long> tags, int expected)
+ {
+ for (long tag : tags)
+ {
+ UnacknowledgedMessage u = _map.get(tag);
+ assertTrue("Message not found for tag " + tag, u != null);
+ ((TestMessage) u.message).assertCountEquals(expected);
+ }
+ }
+
+ void prepare() throws AMQException
+ {
+ _op.consolidate();
+ _op.prepare(_storeContext);
+
+ assertCount(_acked, -1);
+ assertCount(_unacked, 0);
+
+ }
+
+ void undoPrepare()
+ {
+ _op.consolidate();
+ _op.undoPrepare();
+
+ assertCount(_acked, 1);
+ assertCount(_unacked, 0);
+ }
+
+ void commit()
+ {
+ _op.consolidate();
+ _op.commit(_storeContext);
+
+ //check acked messages are removed from map
+ Set<Long> keys = new HashSet<Long>(_map.getDeliveryTags());
+ keys.retainAll(_acked);
+ assertTrue("Expected messages with following tags to have been removed from map: " + keys, keys.isEmpty());
+ //check unacked messages are still in map
+ keys = new HashSet<Long>(_unacked);
+ keys.removeAll(_map.getDeliveryTags());
+ assertTrue("Expected messages with following tags to still be in map: " + keys, keys.isEmpty());
+ }
+ }
+
+ private class TestMessage extends AMQMessage
+ {
+ private final long _tag;
+ private int _count;
+
+ TestMessage(long tag, long messageId, MessagePublishInfo publishBody, TransactionalContext txnContext)
+ {
+ super(messageId, publishBody, txnContext);
+ try
+ {
+ setContentHeaderBody(new ContentHeaderBody()
+ {
+ public int getSize()
+ {
+ return 1;
+ }
+ });
+ }
+ catch (AMQException e)
+ {
+ // won't happen
+ }
+ _tag = tag;
+ }
+
+ public void incrementReference()
+ {
+ _count++;
+ }
+
+ public void decrementReference(StoreContext context)
+ {
+ _count--;
+ }
+
+ void assertCountEquals(int expected)
+ {
+ assertEquals("Wrong count for message with tag " + _tag, expected, _count);
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
new file mode 100644
index 0000000000..ff5517bdd5
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
@@ -0,0 +1,336 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MessageHandleFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.SkeletonMessageStore;
+import org.apache.qpid.server.store.MemoryMessageStore;
+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.RequiredDeliveryException;
+import org.apache.log4j.Logger;
+
+import java.util.*;
+
+public class AbstractHeadersExchangeTestBase extends TestCase
+{
+ private static final Logger _log = Logger.getLogger(AbstractHeadersExchangeTestBase.class);
+
+ private final HeadersExchange exchange = new HeadersExchange();
+ protected final Set<TestQueue> queues = new HashSet<TestQueue>();
+
+ /**
+ * Not used in this test, just there to stub out the routing calls
+ */
+ private MessageStore _store = new MemoryMessageStore();
+
+ private StoreContext _storeContext = new StoreContext();
+
+ private MessageHandleFactory _handleFactory = new MessageHandleFactory();
+
+ private int count;
+
+ public void testDoNothing()
+ {
+ // this is here only to make junit under Eclipse happy
+ }
+
+ protected TestQueue bindDefault(String... bindings) throws AMQException
+ {
+ return bind("Queue" + (++count), bindings);
+ }
+
+ protected TestQueue bind(String queueName, String... bindings) throws AMQException
+ {
+ return bind(queueName, getHeaders(bindings));
+ }
+
+ protected TestQueue bind(String queue, FieldTable bindings) throws AMQException
+ {
+ return bind(new TestQueue(new AMQShortString(queue)), bindings);
+ }
+
+ protected TestQueue bind(TestQueue queue, String... bindings) throws AMQException
+ {
+ return bind(queue, getHeaders(bindings));
+ }
+
+ protected TestQueue bind(TestQueue queue, FieldTable bindings) throws AMQException
+ {
+ queues.add(queue);
+ exchange.registerQueue(null, queue, bindings);
+ return queue;
+ }
+
+
+ protected void route(Message m) throws AMQException
+ {
+ m.route(exchange);
+ m.routingComplete(_store, _storeContext, _handleFactory);
+ }
+
+ protected void routeAndTest(Message m, TestQueue... expected) throws AMQException
+ {
+ routeAndTest(m, false, Arrays.asList(expected));
+ }
+
+ protected void routeAndTest(Message m, boolean expectReturn, TestQueue... expected) throws AMQException
+ {
+ routeAndTest(m, expectReturn, Arrays.asList(expected));
+ }
+
+ protected void routeAndTest(Message m, List<TestQueue> expected) throws AMQException
+ {
+ routeAndTest(m, false, expected);
+ }
+
+ protected void routeAndTest(Message m, boolean expectReturn, List<TestQueue> expected) throws AMQException
+ {
+ try
+ {
+ route(m);
+ assertFalse("Expected "+m+" to be returned due to manadatory flag, and lack of routing",expectReturn);
+ for (TestQueue q : queues)
+ {
+ if (expected.contains(q))
+ {
+ assertTrue("Expected " + m + " to be delivered to " + q, m.isInQueue(q));
+ //assert m.isInQueue(q) : "Expected " + m + " to be delivered to " + q;
+ }
+ else
+ {
+ assertFalse("Did not expect " + m + " to be delivered to " + q, m.isInQueue(q));
+ //assert !m.isInQueue(q) : "Did not expect " + m + " to be delivered to " + q;
+ }
+ }
+ }
+
+ catch (NoRouteException ex)
+ {
+ assertTrue("Expected "+m+" not to be returned",expectReturn);
+ }
+
+ }
+
+ static FieldTable getHeaders(String... entries)
+ {
+ FieldTable headers = FieldTableFactory.newFieldTable();
+ for (String s : entries)
+ {
+ String[] parts = s.split("=", 2);
+ headers.setObject(parts[0], parts.length > 1 ? parts[1] : "");
+ }
+ return headers;
+ }
+
+
+ static final class MessagePublishInfoImpl implements MessagePublishInfo
+ {
+ private AMQShortString _exchange;
+ private boolean _immediate;
+ private boolean _mandatory;
+ private AMQShortString _routingKey;
+
+ public MessagePublishInfoImpl(AMQShortString routingKey)
+ {
+ _routingKey = routingKey;
+ }
+
+ public MessagePublishInfoImpl(AMQShortString exchange, boolean immediate, boolean mandatory, AMQShortString routingKey)
+ {
+ _exchange = exchange;
+ _immediate = immediate;
+ _mandatory = mandatory;
+ _routingKey = routingKey;
+ }
+
+ public AMQShortString getExchange()
+ {
+ return _exchange;
+ }
+
+ public boolean isImmediate()
+ {
+ return _immediate;
+
+ }
+
+ public boolean isMandatory()
+ {
+ return _mandatory;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return _routingKey;
+ }
+
+
+ public void setExchange(AMQShortString exchange)
+ {
+ _exchange = exchange;
+ }
+
+ public void setImmediate(boolean immediate)
+ {
+ _immediate = immediate;
+ }
+
+ public void setMandatory(boolean mandatory)
+ {
+ _mandatory = mandatory;
+ }
+
+ public void setRoutingKey(AMQShortString routingKey)
+ {
+ _routingKey = routingKey;
+ }
+ }
+
+ static MessagePublishInfo getPublishRequest(final String id)
+ {
+ return new MessagePublishInfoImpl(null, false, false, new AMQShortString(id));
+ }
+
+ static ContentHeaderBody getContentHeader(FieldTable headers)
+ {
+ ContentHeaderBody header = new ContentHeaderBody();
+ header.properties = getProperties(headers);
+ return header;
+ }
+
+ static BasicContentHeaderProperties getProperties(FieldTable headers)
+ {
+ BasicContentHeaderProperties properties = new BasicContentHeaderProperties();
+ properties.setHeaders(headers);
+ return properties;
+ }
+
+ static class TestQueue extends AMQQueue
+ {
+ final List<HeadersExchangeTest.Message> messages = new ArrayList<HeadersExchangeTest.Message>();
+
+ public TestQueue(AMQShortString name) throws AMQException
+ {
+ super(name, false, new AMQShortString("test"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"));
+ }
+
+ /**
+ * We override this method so that the default behaviour, which attempts to use a delivery manager, is
+ * not invoked. It is unnecessary since for this test we only care to know whether the message was
+ * sent to the queue; the queue processing logic is not being tested.
+ * @param msg
+ * @param deliverFirst
+ * @throws AMQException
+ */
+ public void process(StoreContext context, AMQMessage msg, boolean deliverFirst) throws AMQException
+ {
+ messages.add(new HeadersExchangeTest.Message(msg));
+ }
+ }
+
+ /**
+ * Just add some extra utility methods to AMQMessage to aid testing.
+ */
+ static class Message extends AMQMessage
+ {
+ private static MessageStore _messageStore = new SkeletonMessageStore();
+
+ private static StoreContext _storeContext = new StoreContext();
+
+ private static TransactionalContext _txnContext = new NonTransactionalContext(_messageStore, _storeContext,
+ null,
+ new LinkedList<RequiredDeliveryException>(),
+ new HashSet<Long>());
+
+ Message(String id, String... headers) throws AMQException
+ {
+ this(id, getHeaders(headers));
+ }
+
+ Message(String id, FieldTable headers) throws AMQException
+ {
+ this(getPublishRequest(id), getContentHeader(headers), null);
+ }
+
+ private Message(MessagePublishInfo publish, ContentHeaderBody header, List<ContentBody> bodies) throws AMQException
+ {
+ super(_messageStore.getNewMessageId(), publish, _txnContext, header);
+ }
+
+ private Message(AMQMessage msg) throws AMQException
+ {
+ super(msg);
+ }
+
+ void route(Exchange exchange) throws AMQException
+ {
+ exchange.route(this);
+ }
+
+ boolean isInQueue(TestQueue queue)
+ {
+ return queue.messages.contains(this);
+ }
+
+ public int hashCode()
+ {
+ return getKey().hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return o instanceof HeadersExchangeTest.Message && equals((HeadersExchangeTest.Message) o);
+ }
+
+ private boolean equals(HeadersExchangeTest.Message m)
+ {
+ return getKey().equals(m.getKey());
+ }
+
+ public String toString()
+ {
+ return getKey().toString();
+ }
+
+ private Object getKey()
+ {
+ try
+ {
+ return getMessagePublishInfo().getRoutingKey();
+ }
+ catch (AMQException e)
+ {
+ _log.error("Error getting routing key: " + e, e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java
new file mode 100644
index 0000000000..eca642b556
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import org.apache.qpid.framing.BasicPublishBody;
+
+public class HeadersExchangeTest extends AbstractHeadersExchangeTestBase
+{
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ ApplicationRegistry.initialise(new NullApplicationRegistry());
+ }
+
+ public void testSimple() throws AMQException
+ {
+ TestQueue q1 = bindDefault("F0000");
+ TestQueue q2 = bindDefault("F0000=Aardvark");
+ TestQueue q3 = bindDefault("F0001");
+ TestQueue q4 = bindDefault("F0001=Bear");
+ TestQueue q5 = bindDefault("F0000", "F0001");
+ TestQueue q6 = bindDefault("F0000=Aardvark", "F0001=Bear");
+ TestQueue q7 = bindDefault("F0000", "F0001=Bear");
+ TestQueue q8 = bindDefault("F0000=Aardvark", "F0001");
+
+ routeAndTest(new Message("Message1", "F0000"), q1);
+ routeAndTest(new Message("Message2", "F0000=Aardvark"), q1, q2);
+ routeAndTest(new Message("Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q5, q8);
+ routeAndTest(new Message("Message4", "F0000", "F0001=Bear"), q1, q3, q4, q5, q7);
+ routeAndTest(new Message("Message5", "F0000=Aardvark", "F0001=Bear"),
+ q1, q2, q3, q4, q5, q6, q7, q8);
+ routeAndTest(new Message("Message6", "F0002"));
+
+ Message m7 = new Message("Message7", "XXXXX");
+
+ MessagePublishInfoImpl pb7 = (MessagePublishInfoImpl) (m7.getMessagePublishInfo());
+ pb7.setMandatory(true);
+ routeAndTest(m7,true);
+
+ Message m8 = new Message("Message8", "F0000");
+ MessagePublishInfoImpl pb8 = (MessagePublishInfoImpl)(m8.getMessagePublishInfo());
+ pb8.setMandatory(true);
+ routeAndTest(m8,false,q1);
+
+
+ }
+
+ public void testAny() throws AMQException
+ {
+ TestQueue q1 = bindDefault("F0000", "F0001", "X-match=any");
+ TestQueue q2 = bindDefault("F0000=Aardvark", "F0001=Bear", "X-match=any");
+ TestQueue q3 = bindDefault("F0000", "F0001=Bear", "X-match=any");
+ TestQueue q4 = bindDefault("F0000=Aardvark", "F0001", "X-match=any");
+ TestQueue q6 = bindDefault("F0000=Apple", "F0001", "X-match=any");
+
+ routeAndTest(new Message("Message1", "F0000"), q1, q3);
+ routeAndTest(new Message("Message2", "F0000=Aardvark"), q1, q2, q3, q4);
+ routeAndTest(new Message("Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message("Message4", "F0000", "F0001=Bear"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message("Message5", "F0000=Aardvark", "F0001=Bear"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message("Message6", "F0002"));
+ }
+
+ public void testMandatory() throws AMQException
+ {
+ bindDefault("F0000");
+ Message m1 = new Message("Message1", "XXXXX");
+ Message m2 = new Message("Message2", "F0000");
+ MessagePublishInfoImpl pb1 = (MessagePublishInfoImpl) (m1.getMessagePublishInfo());
+ pb1.setMandatory(true);
+ MessagePublishInfoImpl pb2 = (MessagePublishInfoImpl) (m2.getMessagePublishInfo());
+ pb2.setMandatory(true);
+ routeAndTest(m1,true);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(HeadersExchangeTest.class);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java
new file mode 100644
index 0000000000..9b5c9c3d00
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java
@@ -0,0 +1,296 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.test.framework.Circuit;
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+import org.apache.qpid.test.framework.MessagingTestConfigProperties;
+import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
+import org.apache.qpid.test.framework.sequencers.CircuitFactory;
+
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+
+/**
+ * ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS
+ * feature. A message may be marked with an immediate delivery flag, which means that a consumer must be connected
+ * to receive the message, through a valid route, when it is sent, or when its transaction is committed in the case
+ * of transactional messaging. If this is not the case, the broker should return the message with a NO_CONSUMERS code.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is
+ * connected.
+ * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is
+ * connected.
+ * <tr><td> Check that an immediate message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an immediate message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that an immediate message results in no consumers code, not using transactions, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no consumers code, in a transaction, when a consumer is
+ * disconnected.
+ * <tr><td> Check that an immediate message results in no route code, not using transactions, when no outgoing route is
+ * connected.
+ * <tr><td> Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is
+ * connected.
+ * </table>
+ *
+ * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test
+ * circuits.
+ */
+public class ImmediateMessageTest extends FrameworkBaseCase
+{
+ /** Used to read the tests configurable properties through. */
+ ParsedProperties testProps;
+
+ /**
+ * Creates a new test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public ImmediateMessageTest(String name)
+ {
+ super(name);
+ }
+
+ /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */
+ public void test_QPID_517_ImmediateOkNoTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Run the default test sequence over the test circuit checking for no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */
+ public void test_QPID_517_ImmediateOkTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Send one message with no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */
+ public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message and get a linked no consumers exception.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion())));
+ }
+
+ /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */
+ public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message and get a linked no consumers exception.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion())));
+ }
+
+ /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */
+ public void test_QPID_517_ImmediateFailsNoRouteNoTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */
+ public void test_QPID_517_ImmediateFailsNoRouteTxP2P()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */
+ public void test_QPID_517_ImmediateOkNoTxPubSub()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Send one message with no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */
+ public void test_QPID_517_ImmediateOkTxPubSub()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Send one message with no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */
+ public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Use durable subscriptions, so that the route remains open with no subscribers.
+ testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message and get a linked no consumers exception.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion())));
+ }
+
+ /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */
+ public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Use durable subscriptions, so that the route remains open with no subscribers.
+ testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message and get a linked no consumers exception.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion())));
+ }
+
+ /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */
+ public void test_QPID_517_ImmediateFailsNoRouteNoTxPubSub()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */
+ public void test_QPID_517_ImmediateFailsNoRouteTxPubSub()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
+
+ /** All these tests should have the immediate flag on. */
+ testProps.setProperty(IMMEDIATE_PROPNAME, true);
+ testProps.setProperty(MANDATORY_PROPNAME, false);
+
+ /** Bind the receivers consumer by default. */
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true);
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java
new file mode 100644
index 0000000000..df99d044d2
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java
@@ -0,0 +1,308 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.test.framework.Circuit;
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+import org.apache.qpid.test.framework.MessagingTestConfigProperties;
+import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
+import org.apache.qpid.test.framework.sequencers.CircuitFactory;
+
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+
+/**
+ * MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS
+ * feature. A message may be marked with a mandatory delivery flag, which means that a valid route for the message
+ * must exist, when it is sent, or when its transaction is committed in the case of transactional messaging. If this
+ * is not the case, the broker should return the message with a NO_CONSUMERS code.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected
+ * but the route exists.
+ * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
+ * the route exists.
+ * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is
+ * connected.
+ * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is
+ * connected.
+ * <tr><td> Check that an mandatory message is sent succesfully not using transactions when a consumer is connected.
+ * <tr><td> Check that an mandatory message is committed succesfully in a transaction when a consumer is connected.
+ * <tr><td> Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected
+ * but the route exists.
+ * <tr><td> Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
+ * the route exists.
+ * <tr><td> Check that an mandatory message results in no route code, not using transactions, when no consumer is
+ * connected.
+ * <tr><td> Check that an mandatory message results in no route code, upon transaction commit, when a consumer is
+ * connected.
+ * </table>
+ *
+ * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test
+ * circuits.
+ */
+public class MandatoryMessageTest extends FrameworkBaseCase
+{
+ /** Used to read the tests configurable properties through. */
+ ParsedProperties testProps;
+
+ /**
+ * Creates a new test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public MandatoryMessageTest(String name)
+ {
+ super(name);
+ }
+
+ /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */
+ public void test_QPID_508_MandatoryOkNoTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Run the default test sequence over the test circuit checking for no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */
+ public void test_QPID_508_MandatoryOkTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Run the default test sequence over the test circuit checking for no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /**
+ * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but
+ * the route exists.
+ */
+ public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message with no errors.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /**
+ * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
+ * the route exists.
+ */
+ public void test_QPID_517_MandatoryOkConsumerDisconnectedTxP2P()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message with no errors.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */
+ public void test_QPID_508_MandatoryFailsNoRouteNoTxP2P()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */
+ public void test_QPID_508_MandatoryFailsNoRouteTxP2P()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, false);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */
+ public void test_QPID_508_MandatoryOkNoTxPubSub()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Run the default test sequence over the test circuit checking for no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */
+ public void test_QPID_508_MandatoryOkTxPubSub()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Run the default test sequence over the test circuit checking for no errors.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /**
+ * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but
+ * the route exists.
+ */
+ public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxPubSub()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Use durable subscriptions, so that the route remains open with no subscribers.
+ testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message with no errors.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /**
+ * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but
+ * the route exists.
+ */
+ public void test_QPID_517_MandatoryOkConsumerDisconnectedTxPubSub()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Use durable subscriptions, so that the route remains open with no subscribers.
+ testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true);
+
+ // Disconnect the consumer.
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false);
+
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ // Send one message with no errors.
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion())));
+ }
+
+ /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */
+ public void test_QPID_508_MandatoryFailsNoRouteNoTxPubSub()
+ {
+ // Ensure transactional sessions are off.
+ testProps.setProperty(TRANSACTED_PROPNAME, false);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */
+ public void test_QPID_508_MandatoryFailsNoRouteTxPubSub()
+ {
+ // Ensure transactional sessions are on.
+ testProps.setProperty(TRANSACTED_PROPNAME, true);
+ testProps.setProperty(PUBSUB_PROPNAME, true);
+
+ // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to
+ // collect its messages).
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false);
+
+ // Send one message and get a linked no route exception.
+ CircuitFactory circuitFactory = getCircuitFactory();
+ Circuit testCircuit = circuitFactory.createCircuit(testProps);
+
+ assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion())));
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
+
+ /** All these tests should have the mandatory flag on. */
+ testProps.setProperty(IMMEDIATE_PROPNAME, false);
+ testProps.setProperty(MANDATORY_PROPNAME, true);
+
+ /** Bind the receivers consumer by default. */
+ testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true);
+ testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java
new file mode 100644
index 0000000000..ebbffd8a71
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java
@@ -0,0 +1,305 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.exchange;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import org.apache.qpid.client.*;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.BindingURL;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.FieldTable;
+
+import javax.jms.*;
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;
+
+public class ReturnUnroutableMandatoryMessageTest extends TestCase implements ExceptionListener
+{
+ private static final Logger _logger = Logger.getLogger(ReturnUnroutableMandatoryMessageTest.class);
+
+ private final List<Message> _bouncedMessageList = Collections.synchronizedList(new ArrayList<Message>());
+ private static final String VIRTUALHOST = "test";
+ private static final String BROKER = "vm://:1";
+
+ static
+ {
+ String workdir = System.getProperty("QPID_WORK");
+ if (workdir == null || workdir.equals(""))
+ {
+ String tempdir = System.getProperty("java.io.tmpdir");
+ System.out.println("QPID_WORK not set using tmp directory: " + tempdir);
+ System.setProperty("QPID_WORK", tempdir);
+ }
+// DOMConfigurator.configure("../broker/etc/log4j.xml");
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ ApplicationRegistry.initialise(new NullApplicationRegistry(), 1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ /**
+ * Tests that mandatory message which are not routable are returned to the producer
+ *
+ * @throws Exception
+ */
+ public void testReturnUnroutableMandatoryMessage_HEADERS() throws Exception
+ {
+ _bouncedMessageList.clear();
+ Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST);
+
+
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ AMQHeadersExchange queue = new AMQHeadersExchange(new AMQBindingURL(ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'"));
+ FieldTable ft = new FieldTable();
+ ft.setString("F1000", "1");
+ MessageConsumer consumer = consumerSession.createConsumer(queue, AMQSession.DEFAULT_PREFETCH_LOW_MARK, AMQSession.DEFAULT_PREFETCH_HIGH_MARK, false, false, (String) null, ft);
+
+ //force synch to ensure the consumer has resulted in a bound queue
+ //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
+ // This is the default now
+
+ Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST);
+
+ con2.setExceptionListener(this);
+ AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ // Need to start the "producer" connection in order to receive bounced messages
+ _logger.info("Starting producer connection");
+ con2.start();
+
+
+ MessageProducer nonMandatoryProducer = producerSession.createProducer(queue, false, false);
+ MessageProducer mandatoryProducer = producerSession.createProducer(queue);
+
+ // First test - should neither be bounced nor routed
+ _logger.info("Sending non-routable non-mandatory message");
+ TextMessage msg1 = producerSession.createTextMessage("msg1");
+ nonMandatoryProducer.send(msg1);
+
+ // Second test - should be bounced
+ _logger.info("Sending non-routable mandatory message");
+ TextMessage msg2 = producerSession.createTextMessage("msg2");
+ mandatoryProducer.send(msg2);
+
+ // Third test - should be routed
+ _logger.info("Sending routable message");
+ TextMessage msg3 = producerSession.createTextMessage("msg3");
+ msg3.setStringProperty("F1000", "1");
+ mandatoryProducer.send(msg3);
+
+
+ _logger.info("Starting consumer connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive(1000L);
+
+ assertTrue("No message routed to receivers", tm != null);
+ assertTrue("Wrong message routed to receivers: " + tm.getText(), "msg3".equals(tm.getText()));
+
+ try
+ {
+ Thread.sleep(1000L);
+ }
+ catch (InterruptedException e)
+ {
+ ;
+ }
+
+ assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1);
+ Message m = _bouncedMessageList.get(0);
+ assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2"));
+
+
+ con.close();
+ con2.close();
+
+
+ }
+
+ public void testReturnUnroutableMandatoryMessage_QUEUE() throws Exception
+ {
+ _bouncedMessageList.clear();
+ Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST);
+
+
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ AMQQueue valid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE");
+ AMQQueue invalid_queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, "testReturnUnroutableMandatoryMessage_QUEUE_INVALID");
+ MessageConsumer consumer = consumerSession.createConsumer(valid_queue);
+
+ //force synch to ensure the consumer has resulted in a bound queue
+ //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
+ // This is the default now
+
+ Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST);
+
+ con2.setExceptionListener(this);
+ AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ // Need to start the "producer" connection in order to receive bounced messages
+ _logger.info("Starting producer connection");
+ con2.start();
+
+
+ MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_queue, false, false);
+ MessageProducer mandatoryProducer = producerSession.createProducer(invalid_queue);
+
+ // First test - should be routed
+ _logger.info("Sending non-mandatory message");
+ TextMessage msg1 = producerSession.createTextMessage("msg1");
+ nonMandatoryProducer.send(msg1);
+
+ // Second test - should be bounced
+ _logger.info("Sending non-routable mandatory message");
+ TextMessage msg2 = producerSession.createTextMessage("msg2");
+ mandatoryProducer.send(msg2);
+
+
+ _logger.info("Starting consumer connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive(1000L);
+
+ assertTrue("No message routed to receivers", tm != null);
+ assertTrue("Wrong message routed to receivers: " + tm.getText(), "msg1".equals(tm.getText()));
+
+ try
+ {
+ Thread.sleep(1000L);
+ }
+ catch (InterruptedException e)
+ {
+ ;
+ }
+
+ assertTrue("Wrong number of messages bounced (expect 1): " + _bouncedMessageList.size(), _bouncedMessageList.size() == 1);
+ Message m = _bouncedMessageList.get(0);
+ assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2"));
+
+
+ con.close();
+ con2.close();
+ }
+
+
+ public void testReturnUnroutableMandatoryMessage_TOPIC() throws Exception
+ {
+ _bouncedMessageList.clear();
+ Connection con = new AMQConnection(BROKER, "guest", "guest", "consumer1", VIRTUALHOST);
+
+
+ AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ AMQTopic valid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC");
+ AMQTopic invalid_topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, "test.Return.Unroutable.Mandatory.Message.TOPIC.invalid");
+ MessageConsumer consumer = consumerSession.createConsumer(valid_topic);
+
+ //force synch to ensure the consumer has resulted in a bound queue
+ //((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
+ // This is the default now
+
+ Connection con2 = new AMQConnection(BROKER, "guest", "guest", "producer1", VIRTUALHOST);
+
+ con2.setExceptionListener(this);
+ AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+
+ // Need to start the "producer" connection in order to receive bounced messages
+ _logger.info("Starting producer connection");
+ con2.start();
+
+
+ MessageProducer nonMandatoryProducer = producerSession.createProducer(valid_topic, false, false);
+ MessageProducer mandatoryProducer = producerSession.createProducer(invalid_topic);
+
+ // First test - should be routed
+ _logger.info("Sending non-mandatory message");
+ TextMessage msg1 = producerSession.createTextMessage("msg1");
+ nonMandatoryProducer.send(msg1);
+
+ // Second test - should be bounced
+ _logger.info("Sending non-routable mandatory message");
+ TextMessage msg2 = producerSession.createTextMessage("msg2");
+ mandatoryProducer.send(msg2);
+
+
+ _logger.info("Starting consumer connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive(1000L);
+
+ assertTrue("No message routed to receivers", tm != null);
+ assertTrue("Wrong message routed to receivers: " + tm.getText(), "msg1".equals(tm.getText()));
+
+ try
+ {
+ Thread.sleep(1000L);
+ }
+ catch (InterruptedException e)
+ {
+ ;
+ }
+
+ assertEquals("Wrong number of messages bounced: ", 1, _bouncedMessageList.size());
+ Message m = _bouncedMessageList.get(0);
+ assertTrue("Wrong message bounced: " + m.toString(), m.toString().contains("msg2"));
+
+
+ con.close();
+ con2.close();
+ }
+
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ReturnUnroutableMandatoryMessageTest.class);
+ }
+
+ public void onException(JMSException jmsException)
+ {
+
+ Exception linkedException = jmsException.getLinkedException();
+ if (linkedException instanceof AMQNoRouteException)
+ {
+ AMQNoRouteException noRoute = (AMQNoRouteException) linkedException;
+ Message bounced = (Message) noRoute.getUndeliveredMessage();
+ _bouncedMessageList.add(bounced);
+ _logger.info("Caught expected NoRouteException");
+ }
+ else
+ {
+ _logger.warn("Caught exception on producer: ", jmsException);
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/failure/DeadlockTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/failure/DeadlockTest.java
new file mode 100644
index 0000000000..a25af30008
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/failure/DeadlockTest.java
@@ -0,0 +1,211 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.failure;
+
+import junit.framework.TestCase;
+import org.apache.qpid.client.AMQConnectionFactory;
+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.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.jms.Topic;
+import java.util.Random;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * DeadlockTestCase:
+ * From a client requirement.
+ *
+ * The JMS Spec specifies that a Session has a single thread of control. And as such setting message listeners from a
+ * second thread is not allowed.
+ * Section 4.4.6 of the Spec states:
+ <quote>Another consequence is that a connection must be in stopped mode to set up a
+session with more than one message listener. The reason is that when a
+connection is actively delivering messages, once the first message listener for a
+session has been registered, the session is now controlled by the thread of
+control that delivers messages to it. At this point a client thread of control
+cannot be used to further configure the session.</quote>
+ *
+ * It, however, does not specified what we should do in the case. it only states:
+ <quote>Once a connection has been started, all its sessions with a registered message
+listener are dedicated to the thread of control that delivers messages to them. It
+is erroneous for client code to use such a session from another thread of
+control. The only exception to this is the use of the session or connection close
+method.</quote>
+ *
+ * While it may be erroneous the causing a Deadlock is not a very satisfactory solution. This test ensures that we do
+ * no do this. There is no technical reason we cannot currently allow the setting of a messageListener on a new consumer.
+ * The only caveate is due to QPID-577 there is likely to be temporary message 'loss'. As they are stuck on the internal
+ * _synchronousQueue pending a synchronous receive.
+ *
+ */
+public class DeadlockTest extends TestCase
+{
+ private static final Logger _logger = LoggerFactory.getLogger(DeadlockTest.class);
+
+
+ public static final String QPID_BROKER_CONNECTION_PROPERTY = "QPIDBROKER";
+
+ private String topic1 = "TEST.DeadLock1.TMP";
+ private String topic2 = "TEST.DeadLock2.TMP";
+
+ private Session sess;
+
+ private Semaphore s = new Semaphore(0);
+ private final String LOCAL = "tcp://localhost:5670";
+ private final String VM = "vm://:1";
+
+ private String BROKER = VM;
+
+ String connectionString = System.getProperty(QPID_BROKER_CONNECTION_PROPERTY,
+ "amqp://guest:guest@/test?brokerlist='" + BROKER + "'");
+
+
+ public void setUp() throws AMQVMBrokerCreationException
+ {
+ if (BROKER.equals("vm://:1"))
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ }
+
+ public void tearDown() throws AMQVMBrokerCreationException
+ {
+ if (BROKER.equals("vm://:1"))
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+ }
+
+ public class EmptyMessageListener implements javax.jms.MessageListener
+ {
+ public void onMessage(Message message)
+ {
+ // do nothing
+ }
+ }
+
+ public void setSessionListener(String topic, javax.jms.MessageListener listener)
+ {
+ try
+ {
+ Topic jmsTopic = sess.createTopic(topic);
+ MessageConsumer subscriber = sess.createConsumer(jmsTopic);
+ subscriber.setMessageListener(listener);
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ fail("Caught JMSException");
+ }
+ }
+
+ public class TestMessageListener implements javax.jms.MessageListener
+ {
+ public Random r = new Random();
+
+ public void onMessage(Message message)
+ {
+ if (r.nextBoolean())
+ {
+ setSessionListener(topic2, new EmptyMessageListener());
+ }
+ }
+
+ }
+
+ public void testDeadlock() throws InterruptedException, URLSyntaxException, JMSException
+ {
+ // in order to trigger the deadlock we need to
+ // set a message listener from one thread
+ // whilst receiving a message on another thread and on that thread also setting (the same?) message listener
+ AMQConnectionFactory acf = new AMQConnectionFactory(connectionString);
+ Connection conn = acf.createConnection();
+ conn.start();
+ sess = conn.createSession(false, org.apache.qpid.jms.Session.NO_ACKNOWLEDGE);
+ setSessionListener(topic1, new TestMessageListener());
+
+
+ Thread th = new Thread()
+ {
+ public void run()
+ {
+ try
+ {
+ Topic jmsTopic = sess.createTopic(topic1);
+ MessageProducer producer = sess.createProducer(jmsTopic);
+ producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+ Random r = new Random();
+ long end = System.currentTimeMillis() + 2000;
+ while (end - System.currentTimeMillis() > 0)
+ {
+ if (r.nextBoolean())
+ {
+ _logger.info("***************** send message");
+ Message jmsMessage = sess.createTextMessage("");
+ producer.send(jmsMessage);
+ }
+ else
+ {
+ _logger.info("***************** set session listener");
+ setSessionListener(topic2, new EmptyMessageListener());
+ }
+ Thread.yield();
+ }
+ _logger.info("done sends");
+ s.release();
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace();
+ fail("Caught JMSException");
+ }
+ }
+ };
+ th.setDaemon(true);
+ th.setName("testDeadlock");
+ th.start();
+
+ boolean success = s.tryAcquire(1, 4, TimeUnit.SECONDS);
+
+ // if we failed, closing the connection will just hang the test case.
+ if (success)
+ {
+ conn.close();
+ }
+
+ if (!success)
+ {
+ fail("Deadlock ocurred");
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/failure/HeapExhaustion.java b/Final/java/systests/src/main/java/org/apache/qpid/server/failure/HeapExhaustion.java
new file mode 100644
index 0000000000..4117fe6720
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/failure/HeapExhaustion.java
@@ -0,0 +1,230 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.failure;
+
+import junit.framework.TestCase;
+import org.apache.qpid.testutil.QpidClientConnectionHelper;
+import org.apache.qpid.client.failover.FailoverException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.log4j.Logger;
+
+import javax.jms.JMSException;
+import javax.jms.DeliveryMode;
+import java.io.IOException;
+
+
+/** Test Case provided by client Non-functional Test NF101: heap exhaustion behaviour */
+public class HeapExhaustion extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(HeapExhaustion.class);
+
+ protected QpidClientConnectionHelper conn;
+ protected final String BROKER = "localhost";
+ protected final String vhost = "/test";
+ protected final String queue = "direct://amq.direct//queue";
+
+ protected String hundredK;
+ protected String megabyte;
+
+ protected String generatePayloadOfSize(Integer numBytes)
+ {
+ return new String(new byte[numBytes]);
+ }
+
+ protected void setUp() throws Exception
+ {
+ conn = new QpidClientConnectionHelper(BROKER);
+ conn.setVirtualHost(vhost);
+
+ conn.connect();
+ // clear queue
+ _logger.debug("setup: clearing test queue");
+ conn.consume(queue, 2000);
+
+ hundredK = generatePayloadOfSize(1024 * 100);
+ megabyte = generatePayloadOfSize(1024 * 1024);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ conn.disconnect();
+ }
+
+
+ /**
+ * PUT at maximum rate (although we commit after each PUT) until failure
+ *
+ * @throws Exception on error
+ */
+ public void testUntilFailureTransient() throws Exception
+ {
+ int copies = 0;
+ int total = 0;
+ String payload = hundredK;
+ int size = payload.getBytes().length;
+ while (true)
+ {
+ conn.put(queue, payload, 1, DeliveryMode.NON_PERSISTENT);
+ copies++;
+ total += size;
+ System.out.println("put copy " + copies + " OK for total bytes: " + total);
+ }
+ }
+
+ /**
+ * PUT at lower rate (5 per second) until failure
+ *
+ * @throws Exception on error
+ */
+ public void testUntilFailureWithDelaysTransient() throws Exception
+ {
+ int copies = 0;
+ int total = 0;
+ String payload = hundredK;
+ int size = payload.getBytes().length;
+ while (true)
+ {
+ conn.put(queue, payload, 1, DeliveryMode.NON_PERSISTENT);
+ copies++;
+ total += size;
+ System.out.println("put copy " + copies + " OK for total bytes: " + total);
+ Thread.sleep(200);
+ }
+ }
+
+ public static void noDelay()
+ {
+ HeapExhaustion he = new HeapExhaustion();
+
+ try
+ {
+ he.setUp();
+ }
+ catch (Exception e)
+ {
+ _logger.info("Unable to connect");
+ System.exit(0);
+ }
+
+ try
+ {
+ _logger.info("Running testUntilFailure");
+ try
+ {
+ he.testUntilFailureTransient();
+ }
+ catch (FailoverException fe)
+ {
+ _logger.error("Caught failover:" + fe);
+ }
+ _logger.info("Finishing Connection ");
+
+ try
+ {
+ he.tearDown();
+ }
+ catch (JMSException jmse)
+ {
+ if (((AMQException) jmse.getLinkedException()).getErrorCode() == AMQConstant.REQUEST_TIMEOUT)
+ {
+ _logger.info("Successful test of testUntilFailure");
+ }
+ else
+ {
+ _logger.error("Test Failed due to:" + jmse);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Test Failed due to:" + e);
+ }
+ }
+
+ public static void withDelay()
+ {
+ HeapExhaustion he = new HeapExhaustion();
+
+ try
+ {
+ he.setUp();
+ }
+ catch (Exception e)
+ {
+ _logger.info("Unable to connect");
+ System.exit(0);
+ }
+
+ try
+ {
+ _logger.info("Running testUntilFailure");
+ try
+ {
+ he.testUntilFailureWithDelaysTransient();
+ }
+ catch (FailoverException fe)
+ {
+ _logger.error("Caught failover:" + fe);
+ }
+ _logger.info("Finishing Connection ");
+
+ try
+ {
+ he.tearDown();
+ }
+ catch (JMSException jmse)
+ {
+ if (((AMQException) jmse.getLinkedException()).getErrorCode() == AMQConstant.REQUEST_TIMEOUT)
+ {
+ _logger.info("Successful test of testUntilFailure");
+ }
+ else
+ {
+ _logger.error("Test Failed due to:" + jmse);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Test Failed due to:" + e);
+ }
+ }
+
+ public static void main(String args[])
+ {
+ noDelay();
+
+
+ try
+ {
+ System.out.println("Restart failed broker now to retest broker with delays in send.");
+ System.in.read();
+ }
+ catch (IOException e)
+ {
+ _logger.info("Continuing");
+ }
+
+ withDelay();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
new file mode 100644
index 0000000000..04376a3067
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import 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.framing.AMQShortString;
+
+import javax.management.JMException;
+
+/**
+ * Test class to test MBean operations for AMQMinaProtocolSession.
+ */
+public class AMQProtocolSessionMBeanTest extends TestCase
+{
+ private MessageStore _messageStore = new SkeletonMessageStore();
+ private AMQMinaProtocolSession _protocolSession;
+ private AMQChannel _channel;
+ private AMQProtocolSessionMBean _mbean;
+
+ public void testChannels() throws Exception
+ {
+ // check the channel count is correct
+ int channelCount = _mbean.channels().size();
+ assertTrue(channelCount == 1);
+ 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);
+ channel.setDefaultQueue(queue);
+ _protocolSession.addChannel(channel);
+ channelCount = _mbean.channels().size();
+ assertTrue(channelCount == 2);
+
+ // general properties test
+ _mbean.setMaximumNumberOfChannels(1000L);
+ assertTrue(_mbean.getMaximumNumberOfChannels() == 1000L);
+
+ // check APIs
+ AMQChannel channel3 = new AMQChannel(_protocolSession,3, _messageStore, null);
+ channel3.setLocalTransactional();
+ _protocolSession.addChannel(channel3);
+ _mbean.rollbackTransactions(2);
+ _mbean.rollbackTransactions(3);
+ _mbean.commitTransactions(2);
+ _mbean.commitTransactions(3);
+
+ // This should throw exception, because the channel does't exist
+ try
+ {
+ _mbean.commitTransactions(4);
+ fail();
+ }
+ catch (JMException ex)
+ {
+ System.out.println("expected exception is thrown :" + ex.getMessage());
+ }
+
+ // check if closing of session works
+ _protocolSession.addChannel(new AMQChannel(_protocolSession,5, _messageStore, null));
+ _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));
+ fail();
+ }
+ catch(AMQException ex)
+ {
+ System.out.println("expected exception is thrown :" + ex.getMessage());
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ _protocolSession = new AMQMinaProtocolSession(new MockIoSession(),
+ appRegistry.getVirtualHostRegistry(),
+ new AMQCodecFactory(true),
+ null);
+ _protocolSession.setVirtualHost(appRegistry.getVirtualHostRegistry().getVirtualHost("test"));
+ _channel = new AMQChannel(_protocolSession,1, _messageStore, null);
+ _protocolSession.addChannel(_channel);
+ _mbean = (AMQProtocolSessionMBean)_protocolSession.getManagedObject();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java
new file mode 100644
index 0000000000..ae02c1c28c
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java
@@ -0,0 +1,78 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import 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
+{
+// private MessageStore _messageStore = new SkeletonMessageStore();
+
+ public void testChannels() throws Exception
+ {
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ AMQMinaProtocolSession _protocolSession = new AMQMinaProtocolSession(new MockIoSession(),
+ appRegistry.getVirtualHostRegistry(),
+ new AMQCodecFactory(true),
+ null);
+ _protocolSession.setVirtualHost(appRegistry.getVirtualHostRegistry().getVirtualHost("test"));
+
+ // check the channel count is correct
+ int channelCount = _protocolSession.getChannels().size();
+ assertEquals("Initial channel count wrong", 0, channelCount);
+
+ long maxChannels = 10L;
+ _protocolSession.setMaximumNumberOfChannels(maxChannels);
+ assertEquals("Number of channels not correctly set.", new Long(maxChannels), _protocolSession.getMaximumNumberOfChannels());
+
+
+ try
+ {
+ for (long currentChannel = 0L; currentChannel < maxChannels; currentChannel++)
+ {
+ _protocolSession.addChannel(new AMQChannel(_protocolSession, (int) currentChannel, null, null));
+ }
+ }
+ catch (AMQException e)
+ {
+ assertEquals("Wrong exception recevied.", e.getErrorCode(), AMQConstant.NOT_ALLOWED);
+ }
+ assertEquals("Maximum number of channels not set.", new Long(maxChannels), new Long(_protocolSession.getChannels().size()));
+ }
+
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MockIoSession.java b/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MockIoSession.java
new file mode 100644
index 0000000000..cf6366b513
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/protocol/MockIoSession.java
@@ -0,0 +1,297 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.mina.common.*;
+import org.apache.mina.common.support.DefaultCloseFuture;
+import org.apache.mina.common.support.DefaultWriteFuture;
+
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.util.Set;
+
+public class MockIoSession implements IoSession
+{
+ private AMQProtocolSession _protocolSession;
+
+ /**
+ * Stores the last response written
+ */
+ private Object _lastWrittenObject;
+
+ private boolean _closing;
+
+ public MockIoSession()
+ {
+ }
+
+ public Object getLastWrittenObject()
+ {
+ return _lastWrittenObject;
+ }
+
+ public IoService getService()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoServiceConfig getServiceConfig()
+ {
+ return null;
+ }
+
+ public IoHandler getHandler()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoSessionConfig getConfig()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoFilterChain getFilterChain()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public WriteFuture write(Object message)
+ {
+ WriteFuture wf = new DefaultWriteFuture(null);
+ _lastWrittenObject = message;
+ return wf;
+ }
+
+ public CloseFuture close()
+ {
+ _closing = true;
+ CloseFuture cf = new DefaultCloseFuture(null);
+ cf.setClosed();
+ return cf;
+ }
+
+ public Object getAttachment()
+ {
+ return _protocolSession;
+ }
+
+ public Object setAttachment(Object attachment)
+ {
+ Object current = _protocolSession;
+ _protocolSession = (AMQProtocolSession) attachment;
+ return current;
+ }
+
+ public Object getAttribute(String key)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object setAttribute(String key, Object value)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object setAttribute(String key)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object removeAttribute(String key)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean containsAttribute(String key)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Set getAttributeKeys()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public TransportType getTransportType()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isConnected()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isClosing()
+ {
+ return _closing;
+ }
+
+ public CloseFuture getCloseFuture()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getRemoteAddress()
+ {
+ return new InetSocketAddress("127.0.0.1", 1234); //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getLocalAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getServiceAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getIdleTime(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getIdleTimeInMillis(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setIdleTime(IdleStatus status, int idleTime)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getWriteTimeout()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getWriteTimeoutInMillis()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setWriteTimeout(int writeTimeout)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public TrafficMask getTrafficMask()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setTrafficMask(TrafficMask trafficMask)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void suspendRead()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void suspendWrite()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void resumeRead()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void resumeWrite()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getReadBytes()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getWrittenBytes()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getReadMessages()
+ {
+ return 0L;
+ }
+
+ public long getWrittenMessages()
+ {
+ return 0L;
+ }
+
+ public long getWrittenWriteRequests()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getScheduledWriteRequests()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getScheduledWriteBytes()
+ {
+ return 0; //TODO
+ }
+
+ public long getCreationTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastIoTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastReadTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastWriteTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isIdle(IdleStatus status)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getIdleCount(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastIdleTime(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
new file mode 100644
index 0000000000..29952a6d9b
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
@@ -0,0 +1,374 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 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;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.ack.UnacknowledgedMessage;
+import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+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;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Tests that acknowledgements are handled correctly.
+ */
+public class AckTest extends TestCase
+{
+ private static final Logger _log = Logger.getLogger(AckTest.class);
+
+ private SubscriptionImpl _subscription;
+
+ private MockProtocolSession _protocolSession;
+
+ private TestableMemoryMessageStore _messageStore;
+
+ private StoreContext _storeContext = new StoreContext();
+
+ private AMQChannel _channel;
+
+ private SubscriptionSet _subscriptionManager;
+
+ private AMQQueue _queue;
+
+ private static final AMQShortString DEFAULT_CONSUMER_TAG = new AMQShortString("conTag");
+
+ public AckTest() throws Exception
+ {
+ ApplicationRegistry.initialise(new NullApplicationRegistry());
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _messageStore = new TestableMemoryMessageStore();
+ _protocolSession = new MockProtocolSession(_messageStore);
+ _channel = new AMQChannel(_protocolSession,5, _messageStore, null/*dont need exchange registry*/);
+
+ _protocolSession.addChannel(_channel);
+ _subscriptionManager = new SubscriptionSet();
+ _queue = new AMQQueue(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), _subscriptionManager);
+ }
+
+ private void publishMessages(int count) throws AMQException
+ {
+ publishMessages(count, false);
+ }
+
+ private void publishMessages(int count, boolean persistent) throws AMQException
+ {
+ TransactionalContext txnContext = new NonTransactionalContext(_messageStore, _storeContext, null,
+ new LinkedList<RequiredDeliveryException>(),
+ new HashSet<Long>());
+ MessageHandleFactory factory = new MessageHandleFactory();
+ for (int i = 1; i <= count; i++)
+ {
+ // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0)
+ // TODO: Establish some way to determine the version for the test.
+ MessagePublishInfo publishBody = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return new AMQShortString("someExchange");
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return new AMQShortString("rk");
+ }
+ };
+ AMQMessage msg = new AMQMessage(_messageStore.getNewMessageId(), publishBody, txnContext);
+ if (persistent)
+ {
+ BasicContentHeaderProperties b = new BasicContentHeaderProperties();
+ //This is DeliveryMode.PERSISTENT
+ b.setDeliveryMode((byte) 2);
+ ContentHeaderBody cb = new ContentHeaderBody();
+ cb.properties = b;
+ msg.setContentHeaderBody(cb);
+ }
+ else
+ {
+ msg.setContentHeaderBody(new ContentHeaderBody());
+ }
+ // we increment the reference here since we are not delivering the messaging to any queues, which is where
+ // the reference is normally incremented. The test is easier to construct if we have direct access to the
+ // subscription
+ msg.incrementReference();
+ msg.routingComplete(_messageStore, _storeContext, factory);
+ // we manually send the message to the subscription
+ _subscription.send(msg, _queue);
+ }
+ }
+
+ /**
+ * Tests that the acknowledgements are correctly associated with a channel and
+ * order is preserved when acks are enabled
+ */
+ public void testAckChannelAssociationTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ final int msgCount = 10;
+ publishMessages(msgCount, true);
+
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == msgCount);
+ assertTrue(_messageStore.getMessageMetaDataMap().size() == msgCount);
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i);
+ i++;
+ UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.queue == _queue);
+ }
+
+ assertTrue(map.size() == msgCount);
+ assertTrue(_messageStore.getMessageMetaDataMap().size() == msgCount);
+ }
+
+ /**
+ * Tests that in no-ack mode no messages are retained
+ */
+ public void testNoAckMode() throws AMQException
+ {
+ // false arg means no acks expected
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, false);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+ assertTrue(_messageStore.getMessageMetaDataMap().size() == 0);
+ assertTrue(_messageStore.getContentBodyMap().size() == 0);
+ }
+
+ /**
+ * Tests that in no-ack mode no messages are retained
+ */
+ public void testPersistentNoAckMode() throws AMQException
+ {
+ // false arg means no acks expected
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, false);
+ final int msgCount = 10;
+ publishMessages(msgCount, true);
+
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+ assertTrue(_messageStore.getMessageMetaDataMap().size() == 0);
+ assertTrue(_messageStore.getContentBodyMap().size() == 0);
+ }
+
+
+ /**
+ * Tests that a single acknowledgement is handled correctly (i.e multiple flag not
+ * set case)
+ */
+ public void testSingleAckReceivedTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(5, false);
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == msgCount - 1);
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i);
+ UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.queue == _queue);
+ // 5 is the delivery tag of the message that *should* be removed
+ if (++i == 5)
+ {
+ ++i;
+ }
+ }
+ }
+
+ /**
+ * Tests that a single acknowledgement is handled correctly (i.e multiple flag not
+ * set case)
+ */
+ public void testMultiAckReceivedTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(5, true);
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 5);
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i + 5);
+ UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.queue == _queue);
+ ++i;
+ }
+ }
+
+ /**
+ * Tests that a multiple acknowledgement is handled correctly. When ack'ing all pending msgs.
+ */
+ public void testMultiAckAllReceivedTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(0, true);
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+
+ Set<Long> deliveryTagSet = map.getDeliveryTags();
+ int i = 1;
+ for (long deliveryTag : deliveryTagSet)
+ {
+ assertTrue(deliveryTag == i + 5);
+ UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ assertTrue(unackedMsg.queue == _queue);
+ ++i;
+ }
+ }
+
+ public void testPrefetchHighLow() throws AMQException
+ {
+ int lowMark = 5;
+ int highMark = 10;
+
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ _channel.setPrefetchLowMarkCount(lowMark);
+ _channel.setPrefetchHighMarkCount(highMark);
+
+ assertTrue(_channel.getPrefetchLowMarkCount() == lowMark);
+ assertTrue(_channel.getPrefetchHighMarkCount() == highMark);
+
+ publishMessages(highMark);
+
+ // at this point we should have sent out only highMark messages
+ // which have not bee received so will be queued up in the channel
+ // which should be suspended
+ assertTrue(_subscription.isSuspended());
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == highMark);
+
+ //acknowledge messages so we are just above lowMark
+ _channel.acknowledgeMessage(lowMark - 1, true);
+
+ //we should still be suspended
+ assertTrue(_subscription.isSuspended());
+ assertTrue(map.size() == lowMark + 1);
+
+ //acknowledge one more message
+ _channel.acknowledgeMessage(lowMark, true);
+
+ //and suspension should be lifted
+ assertTrue(!_subscription.isSuspended());
+
+ //pubilsh more msgs so we are just below the limit
+ publishMessages(lowMark - 1);
+
+ //we should not be suspended
+ assertTrue(!_subscription.isSuspended());
+
+ //acknowledge all messages
+ _channel.acknowledgeMessage(0, true);
+ try
+ {
+ Thread.sleep(3000);
+ }
+ catch (InterruptedException e)
+ {
+ _log.error("Error: " + e, e);
+ }
+ //map will be empty
+ assertTrue(map.size() == 0);
+ }
+
+ public void testPrefetch() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ _channel.setPrefetchCount(5);
+
+ assertTrue(_channel.getPrefetchCount() == 5);
+
+ final int msgCount = 5;
+ publishMessages(msgCount);
+
+ // at this point we should have sent out only 5 messages with a further 5 queued
+ // up in the channel which should now be suspended
+ assertTrue(_subscription.isSuspended());
+ UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 5);
+ _channel.acknowledgeMessage(5, true);
+ assertTrue(!_subscription.isSuspended());
+ try
+ {
+ Thread.sleep(3000);
+ }
+ catch (InterruptedException e)
+ {
+ _log.error("Error: " + e, e);
+ }
+ assertTrue(map.size() == 0);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(AckTest.class);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java
new file mode 100644
index 0000000000..068f37574d
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java
@@ -0,0 +1,265 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.framing.AMQShortString;
+import org.apache.qpid.server.handler.OnCurrentThreadExecutor;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import java.util.*;
+import java.util.concurrent.Executor;
+
+/**
+ * Tests delivery in the face of concurrent incoming _messages, subscription alterations
+ * and attempts to asynchronously process queued _messages.
+ */
+public class ConcurrencyTestDisabled extends MessageTestHelper
+{
+ private final Random random = new Random();
+
+ private final int numMessages = 1000;
+
+ private final List<SubscriptionTestHelper> _subscribers = new ArrayList<SubscriptionTestHelper>();
+ private final Set<Subscription> _active = new HashSet<Subscription>();
+ private final List<AMQMessage> _messages = new ArrayList<AMQMessage>();
+ private int next = 0;//index to next message to send
+ private final List<AMQMessage> _received = Collections.synchronizedList(new ArrayList<AMQMessage>());
+ private final Executor _executor = new OnCurrentThreadExecutor();
+ private final List<Thread> _threads = new ArrayList<Thread>();
+
+ private final SubscriptionSet _subscriptionMgr = new SubscriptionSet();
+ private final DeliveryManager _deliveryMgr;
+
+ private boolean isComplete;
+ private boolean failed;
+ private VirtualHost _virtualHost;
+
+ public ConcurrencyTestDisabled() throws Exception
+ {
+
+ IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
+ _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ _deliveryMgr = new ConcurrentSelectorDeliveryManager(_subscriptionMgr, new AMQQueue(new AMQShortString("myQ"), false, new AMQShortString("guest"), false,
+ _virtualHost));
+ }
+
+ public void testConcurrent1() throws InterruptedException, AMQException
+ {
+ initSubscriptions(10);
+ initMessages(numMessages);
+ initThreads(1, 4, 4, 4);
+ doRun();
+ check();
+ }
+
+ public void testConcurrent2() throws InterruptedException, AMQException
+ {
+ initSubscriptions(10);
+ initMessages(numMessages);
+ initThreads(4, 2, 2, 2);
+ doRun();
+ check();
+ }
+
+ void check()
+ {
+ assertFalse("Failed", failed);
+
+ _deliveryMgr.processAsync(_executor);
+
+ assertEquals("Did not recieve the correct number of messages", _messages.size(), _received.size());
+ for(int i = 0; i < _messages.size(); i++)
+ {
+ assertEquals("Wrong message at " + i, _messages.get(i), _received.get(i));
+ }
+ }
+
+ void initSubscriptions(int subscriptions)
+ {
+ for(int i = 0; i < subscriptions; i++)
+ {
+ _subscribers.add(new SubscriptionTestHelper("Subscriber" + i, _received));
+ }
+ }
+
+ void initMessages(int messages) throws AMQException
+ {
+ for(int i = 0; i < messages; i++)
+ {
+ _messages.add(message());
+ }
+ }
+
+ void initThreads(int senders, int subscribers, int suspenders, int processors)
+ {
+ addThreads(senders, senders == 1 ? new Sender() : new OrderedSender());
+ addThreads(subscribers, new Subscriber());
+ addThreads(suspenders, new Suspender());
+ addThreads(processors, new Processor());
+ }
+
+ void addThreads(int count, Runnable runner)
+ {
+ for(int i = 0; i < count; i++)
+ {
+ _threads.add(new Thread(runner, runner.toString()));
+ }
+ }
+
+ void doRun() throws InterruptedException
+ {
+ for(Thread t : _threads)
+ {
+ t.start();
+ }
+
+ for(Thread t : _threads)
+ {
+ t.join();
+ }
+ }
+
+ private void toggle(Subscription s)
+ {
+ synchronized (_active)
+ {
+ if (_active.contains(s))
+ {
+ _active.remove(s);
+ Subscription result = _subscriptionMgr.removeSubscriber(s);
+ assertTrue("Removed subscription " + result + " but trying to remove subscription " + s,
+ result != null && result.equals(s));
+ }
+ else
+ {
+ _active.add(s);
+ _subscriptionMgr.addSubscriber(s);
+ }
+ }
+ }
+
+ private AMQMessage nextMessage()
+ {
+ synchronized (_messages)
+ {
+ if (next < _messages.size())
+ {
+ return _messages.get(next++);
+ }
+ else
+ {
+ if (!_deliveryMgr.hasQueuedMessages()) {
+ isComplete = true;
+ }
+ return null;
+ }
+ }
+ }
+
+ private boolean randomBoolean()
+ {
+ return random.nextBoolean();
+ }
+
+ private SubscriptionTestHelper randomSubscriber()
+ {
+ return _subscribers.get(random.nextInt(_subscribers.size()));
+ }
+
+ private class Sender extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ AMQMessage msg = nextMessage();
+ if (msg != null)
+ {
+ _deliveryMgr.deliver(null, new AMQShortString(toString()), msg, false);
+ }
+ }
+ }
+
+ private class OrderedSender extends Sender
+ {
+ synchronized void doRun() throws Throwable
+ {
+ super.doRun();
+ }
+ }
+
+ private class Suspender extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ randomSubscriber().setSuspended(randomBoolean());
+ }
+ }
+
+ private class Subscriber extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ toggle(randomSubscriber());
+ }
+ }
+
+ private class Processor extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ _deliveryMgr.processAsync(_executor);
+ }
+ }
+
+ private abstract class Runner implements Runnable
+ {
+ public void run()
+ {
+ try
+ {
+ while (!stop())
+ {
+ doRun();
+ }
+ }
+ catch (Throwable t)
+ {
+ failed = true;
+ t.printStackTrace();
+ }
+ }
+
+ abstract void doRun() throws Throwable;
+
+ boolean stop()
+ {
+ return isComplete || failed;
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(ConcurrencyTestDisabled.class);
+ }
+
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java
new file mode 100644
index 0000000000..dc5a6d3cf6
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java
@@ -0,0 +1,177 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler.OnCurrentThreadExecutor;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+
+import junit.framework.TestSuite;
+
+abstract public class DeliveryManagerTest extends MessageTestHelper
+{
+ protected final SubscriptionSet _subscriptions = new SubscriptionSet();
+ protected DeliveryManager _mgr;
+ protected StoreContext _storeContext = new StoreContext();
+ private static final AMQShortString DEFAULT_QUEUE_NAME = new AMQShortString("Me");
+
+ public DeliveryManagerTest() throws Exception
+ {
+ }
+
+ public void testStartInQueueingMode() throws AMQException
+ {
+ AMQMessage[] messages = new AMQMessage[10];
+ for (int i = 0; i < messages.length; i++)
+ {
+ messages[i] = message();
+ }
+ int batch = messages.length / 2;
+
+ for (int i = 0; i < batch; i++)
+ {
+ _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
+ }
+
+ SubscriptionTestHelper s1 = new SubscriptionTestHelper("1");
+ SubscriptionTestHelper s2 = new SubscriptionTestHelper("2");
+ _subscriptions.addSubscriber(s1);
+ _subscriptions.addSubscriber(s2);
+
+ for (int i = batch; i < messages.length; i++)
+ {
+ _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
+ }
+
+ assertTrue(s1.getMessages().isEmpty());
+ assertTrue(s2.getMessages().isEmpty());
+
+ _mgr.processAsync(new OnCurrentThreadExecutor());
+
+ assertEquals(messages.length / 2, s1.getMessages().size());
+ assertEquals(messages.length / 2, s2.getMessages().size());
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ if (i % 2 == 0)
+ {
+ assertTrue(s1.getMessages().get(i / 2) == messages[i]);
+ }
+ else
+ {
+ assertTrue(s2.getMessages().get(i / 2) == messages[i]);
+ }
+ }
+ }
+
+ public void testStartInDirectMode() throws AMQException
+ {
+ AMQMessage[] messages = new AMQMessage[10];
+ for (int i = 0; i < messages.length; i++)
+ {
+ messages[i] = message();
+ }
+ int batch = messages.length / 2;
+
+ SubscriptionTestHelper s1 = new SubscriptionTestHelper("1");
+ _subscriptions.addSubscriber(s1);
+
+ for (int i = 0; i < batch; i++)
+ {
+ _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
+ }
+
+ assertEquals(batch, s1.getMessages().size());
+ for (int i = 0; i < batch; i++)
+ {
+ assertTrue(messages[i] == s1.getMessages().get(i));
+ }
+ s1.getMessages().clear();
+ assertEquals(0, s1.getMessages().size());
+
+ s1.setSuspended(true);
+ for (int i = batch; i < messages.length; i++)
+ {
+ _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
+ }
+
+ _mgr.processAsync(new OnCurrentThreadExecutor());
+ assertEquals(0, s1.getMessages().size());
+ s1.setSuspended(false);
+
+ _mgr.processAsync(new OnCurrentThreadExecutor());
+ assertEquals(messages.length - batch, s1.getMessages().size());
+
+ for (int i = batch; i < messages.length; i++)
+ {
+ assertTrue(messages[i] == s1.getMessages().get(i - batch));
+ }
+
+ }
+
+ public void testNoConsumers() throws AMQException
+ {
+ try
+ {
+ AMQMessage msg = message(true);
+ _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, msg, false);
+ msg.checkDeliveredToConsumer();
+ fail("expected exception did not occur");
+ }
+ catch (NoConsumersException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected NoConsumersException, got " + e);
+ }
+ }
+
+ public void testNoActiveConsumers() throws AMQException
+ {
+ try
+ {
+ SubscriptionTestHelper s = new SubscriptionTestHelper("A");
+ _subscriptions.addSubscriber(s);
+ s.setSuspended(true);
+ AMQMessage msg = message(true);
+ _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, msg, false);
+ msg.checkDeliveredToConsumer();
+ fail("expected exception did not occur");
+ }
+ catch (NoConsumersException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected NoConsumersException, got " + e);
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ TestSuite suite = new TestSuite();
+ return suite;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageReturnTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageReturnTest.java
new file mode 100644
index 0000000000..45bf73bd9d
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageReturnTest.java
@@ -0,0 +1,315 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQNoRouteException;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.store.MemoryMessageStore;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Test Case to ensure that messages are correctly returned.
+ * This includes checking:
+ * - The message is returned.
+ * - The broker doesn't leak memory.
+ * - The broker's state is correct after test.
+ */
+public class MessageReturnTest extends TestCase implements ExceptionListener
+{
+ private static final Logger _logger = Logger.getLogger(MessageReturnTest.class);
+
+
+ protected final String BROKER = "vm://:1";
+ protected final String VHOST = "test";
+ protected final String QUEUE = "MessageReturnTest";
+ protected final String BADQUEUE = "MessageReturnTest-bad-to-force-returns";
+
+
+ private Context _context;
+
+ private Connection _producerConnection;
+
+ private MessageProducer _producer;
+ private Session _clientSession, _producerSession;
+ private static final int MSG_COUNT = 50;
+
+ private Message[] _messages = new Message[MSG_COUNT];
+
+ private CountDownLatch _returns = new CountDownLatch(1);
+ private int _receivedCount = 0;
+ private int _initialContentBodyMapSize;
+ private int _initilaMessageMetaDataMapSize;
+
+ protected void setUp() throws Exception
+ {
+ if (BROKER.startsWith("vm://"))
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@TTL_TEST_ID/" + VHOST + "?brokerlist='" + BROKER + "'");
+ env.put("queue.queue", QUEUE);
+ env.put("queue.badQueue", QUEUE);
+
+ _context = factory.getInitialContext(env);
+
+ getBrokerInitialState();
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ if (_producerConnection != null)
+ {
+ _producerConnection.close();
+ }
+
+ if (BROKER.startsWith("vm://"))
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+ }
+
+ public void test() throws Exception
+ {
+ init();
+ //Send Msgs
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ _producer.send(nextMessage(msg));
+ }
+
+ try
+ {
+ // Wait for all returns to arrive any longer than 5secs and something has gone wrong.
+ _returns.await(5, TimeUnit.SECONDS);
+ }
+ catch (InterruptedException e)
+ {
+
+ }
+
+ //Close the connection.. .giving the broker time to clean up its state.
+ _producerConnection.close();
+
+ //Verify we get all the messages.
+ verifyAllMessagesRecevied();
+ //Verify Broker state
+ verifyBrokerState();
+ }
+
+ private void init() throws NamingException, JMSException
+ {
+ _receivedCount = 0;
+ _messages = new Message[MSG_COUNT];
+ _returns = new CountDownLatch(1);
+
+ //Create Producer
+ _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _producerConnection.setExceptionListener(this);
+
+ _producerConnection.start();
+
+ _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _producer = _producerSession.createProducer((Queue) _context.lookup("badQueue"));
+ }
+
+ // todo: collect to a general testing class - duplicated in AMQQueueMBeanTest
+ private void getBrokerInitialState()
+ {
+ IApplicationRegistry registry = ApplicationRegistry.getInstance();
+
+ VirtualHost testVhost = registry.getVirtualHostRegistry().getVirtualHost(VHOST);
+
+ assertNotNull("Unable to get test Vhost", testVhost.getMessageStore());
+
+ TestableMemoryMessageStore store = new TestableMemoryMessageStore((MemoryMessageStore) testVhost.getMessageStore());
+
+ _initialContentBodyMapSize = store.getContentBodyMap() == null ? 0 : store.getContentBodyMap().size();
+ _initilaMessageMetaDataMapSize = store.getMessageMetaDataMap() == null ? 0 : store.getMessageMetaDataMap().size();
+
+ if (_initialContentBodyMapSize != 0)
+ {
+ _logger.warn("Store is dirty: ContentBodyMap has Size:" + _initialContentBodyMapSize);
+ System.out.println("Store is dirty: ContentBodyMap has Size:" + _initialContentBodyMapSize);
+ }
+
+ if (_initilaMessageMetaDataMapSize != 0)
+ {
+ _logger.warn("Store is dirty: MessageMetaDataMap has Size:" + _initilaMessageMetaDataMapSize);
+ System.out.println("Store is dirty: MessageMetaDataMap has Size:" + _initilaMessageMetaDataMapSize);
+ }
+
+ }
+
+ private void verifyBrokerState()
+ {
+ IApplicationRegistry registry = ApplicationRegistry.getInstance();
+
+ VirtualHost testVhost = registry.getVirtualHostRegistry().getVirtualHost(VHOST);
+
+ assertNotNull("Unable to get test Vhost", testVhost.getMessageStore());
+
+ TestableMemoryMessageStore store = new TestableMemoryMessageStore((MemoryMessageStore) testVhost.getMessageStore());
+
+
+ assertNotNull("ContentBodyMap should not be null", store.getContentBodyMap());
+
+ // If the CBM has content it may be due to the broker not yet purging.
+ // Closing the producer connection before testing should give the store time to clean up.
+ // Perform a quick sleep just in case
+ while (store.getContentBodyMap().size() > _initialContentBodyMapSize)
+ {
+ try
+ {
+ Thread.sleep(500);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ assertTrue("Expected the store content size not reached at test start it was :" + _initialContentBodyMapSize + " Now it is :" + store.getContentBodyMap().size(), _initialContentBodyMapSize >= store.getContentBodyMap().size());
+ assertNotNull("MessageMetaDataMap should not be null", store.getMessageMetaDataMap());
+ assertTrue("Expected the store MessageMetaData size not reached at test start it was :" + _initilaMessageMetaDataMapSize + " Now it is :" + store.getMessageMetaDataMap().size(), _initialContentBodyMapSize >= store.getMessageMetaDataMap().size());
+ }
+
+ private void verifyAllMessagesRecevied()
+ {
+
+ boolean[] msgIdRecevied = new boolean[MSG_COUNT];
+
+ int msgId = 0;
+
+ //Check received messages
+ for (Message msg : _messages)
+ {
+ assertNotNull("Missing message:" + msgId, msg);
+ assertFalse("Already received msg id " + msgId, msgIdRecevied[msgId]);
+ msgIdRecevied[msgId] = true;
+ msgId++;
+ }
+
+ //Check all recevied
+ for (msgId = 0; msgId < MSG_COUNT; msgId++)
+ {
+ assertTrue("Message " + msgId + " not received.", msgIdRecevied[msgId]);
+ }
+ }
+
+ /**
+ * We can't verify messageOrder here as the return threads are not synchronized so we have no way of
+ * guarranting the order.
+ */
+ private void verifyMessageOrder()
+ {
+ int msgId = 0;
+ for (Message msg : _messages)
+ {
+ assertNotNull("Missing message:" + msgId, msg);
+ try
+ {
+ assertEquals("Message not received in correct order", msgId, msg.getIntProperty("ID"));
+ }
+ catch (JMSException e)
+ {
+ fail("Unable to get messageID for msg:" + msg);
+ }
+
+ msgId++;
+ }
+ }
+
+ /**
+ * Get the next message putting the given count into the intProperties as ID.
+ *
+ * @param msgNo the message count to store as ID.
+ * @return
+ * @throws JMSException
+ */
+
+ private Message nextMessage(int msgNo) throws JMSException
+ {
+ Message send = _producerSession.createTextMessage("MessageReturnTest");
+ send.setIntProperty("ID", msgNo);
+ return send;
+ }
+
+
+ public void onException(JMSException jmsException)
+ {
+ // NOTE:
+ // This method MUST be thread-safe. Mulitple threads can call this at once.
+ synchronized (this)
+ {
+ if (jmsException.getLinkedException() instanceof AMQNoRouteException)
+ {
+ AMQNoRouteException amq = (AMQNoRouteException) jmsException.getLinkedException();
+
+ Message msg = (Message) amq.getUndeliveredMessage();
+
+ if (_receivedCount < MSG_COUNT)
+ {
+ assertNotNull("Reeceived Null message:" + _receivedCount, msg);
+ _messages[_receivedCount] = msg;
+ _receivedCount++;
+ }
+ else
+ {
+ fail("Received to many messages expected :" + MSG_COUNT + " received: " + _receivedCount + 1);
+ }
+
+ if (_receivedCount == MSG_COUNT)
+ {
+ _returns.countDown();
+ }
+ }
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
new file mode 100644
index 0000000000..88272023e8
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
@@ -0,0 +1,93 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+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;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.SkeletonMessageStore;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.TestApplicationRegistry;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import org.apache.qpid.server.txn.TransactionalContext;
+import org.apache.qpid.server.txn.NonTransactionalContext;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.AMQException;
+
+import junit.framework.TestCase;
+
+import java.util.LinkedList;
+import java.util.HashSet;
+
+class MessageTestHelper extends TestCase
+{
+ private final MessageStore _messageStore = new SkeletonMessageStore();
+
+ private final StoreContext _storeContext = new StoreContext();
+
+ private final TransactionalContext _txnContext = new NonTransactionalContext(_messageStore, _storeContext, null,
+ new LinkedList<RequiredDeliveryException>(),
+ new HashSet<Long>());
+
+ MessageTestHelper() throws Exception
+ {
+ ApplicationRegistry.initialise(new NullApplicationRegistry());
+ }
+
+ AMQMessage message() throws AMQException
+ {
+ return message(false);
+ }
+
+ AMQMessage message(final boolean immediate) throws AMQException
+ {
+ MessagePublishInfo publish = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean isImmediate()
+ {
+ return immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ return new AMQMessage(_messageStore.getNewMessageId(), publish, _txnContext,
+ new ContentHeaderBody());
+ }
+
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java
new file mode 100644
index 0000000000..0ad6502755
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java
@@ -0,0 +1,205 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.framing.AMQDataBlock;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.VersionSpecificRegistry;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.server.output.ProtocolOutputConverterRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.store.MessageStore;
+
+import javax.security.sasl.SaslServer;
+import java.util.HashMap;
+import java.util.Map;
+import java.security.Principal;
+
+/**
+ * A protocol session that can be used for testing purposes.
+ */
+public class MockProtocolSession implements AMQProtocolSession
+{
+ private MessageStore _messageStore;
+
+ private Map<Integer, AMQChannel> _channelMap = new HashMap<Integer, AMQChannel>();
+
+ public MockProtocolSession(MessageStore messageStore)
+ {
+ _messageStore = messageStore;
+ }
+
+ public void dataBlockReceived(AMQDataBlock message) throws Exception
+ {
+ }
+
+ public void writeFrame(AMQDataBlock frame)
+ {
+ }
+
+ public AMQShortString getContextKey()
+ {
+ return null;
+ }
+
+ public void setContextKey(AMQShortString contextKey)
+ {
+ }
+
+ public AMQChannel getChannel(int channelId)
+ {
+ AMQChannel channel = _channelMap.get(channelId);
+ if (channel == null)
+ {
+ throw new IllegalArgumentException("Invalid channel id: " + channelId);
+ }
+ else
+ {
+ return channel;
+ }
+ }
+
+ public void addChannel(AMQChannel channel)
+ {
+ if (channel == null)
+ {
+ throw new IllegalArgumentException("Channel must not be null");
+ }
+ else
+ {
+ _channelMap.put(channel.getChannelId(), channel);
+ }
+ }
+
+ public void closeChannel(int channelId) throws AMQException
+ {
+ }
+
+ public void closeChannelOk(int channelId)
+ {
+
+ }
+
+ public boolean channelAwaitingClosure(int channelId)
+ {
+ return false;
+ }
+
+ public void removeChannel(int channelId)
+ {
+ _channelMap.remove(channelId);
+ }
+
+ public void initHeartbeats(int delay)
+ {
+ }
+
+ public void closeSession() throws AMQException
+ {
+ }
+
+ public Object getKey()
+ {
+ return null;
+ }
+
+ public String getLocalFQDN()
+ {
+ return null;
+ }
+
+ public SaslServer getSaslServer()
+ {
+ return null;
+ }
+
+ public void setSaslServer(SaslServer saslServer)
+ {
+ }
+
+ public FieldTable getClientProperties()
+ {
+ return null;
+ }
+
+ public void setClientProperties(FieldTable clientProperties)
+ {
+ }
+
+ public Object getClientIdentifier()
+ {
+ return null;
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setVirtualHost(VirtualHost virtualHost)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void addSessionCloseTask(Task task)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeSessionCloseTask(Task task)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public ProtocolOutputConverter getProtocolOutputConverter()
+ {
+ return ProtocolOutputConverterRegistry.getConverter(this);
+ }
+
+ public void setAuthorizedID(Principal authorizedID)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Principal getAuthorizedID()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return 8; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public VersionSpecificRegistry getRegistry()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/PersistentTestManual.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/PersistentTestManual.java
new file mode 100644
index 0000000000..d01284cb8a
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/PersistentTestManual.java
@@ -0,0 +1,279 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQChannelClosedException;
+import org.apache.qpid.AMQConnectionClosedException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.util.CommandLineParser;
+
+import javax.jms.JMSException;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class PersistentTestManual
+{
+ private static final Logger _logger = Logger.getLogger(PersistentTestManual.class);
+
+ private static final String QUEUE = "direct://amq.direct//PersistentTest-Queue2?durable='true',exclusive='true'";
+
+ protected AMQConnection _connection;
+
+ protected Session _session;
+
+ protected Queue _queue;
+ private Properties properties;
+
+ private String _brokerDetails;
+ private String _username;
+ private String _password;
+ private String _virtualpath;
+
+ public PersistentTestManual(Properties overrides)
+ {
+ properties = new Properties(defaults);
+ properties.putAll(overrides);
+
+ _brokerDetails = properties.getProperty(BROKER_PROPNAME);
+ _username = properties.getProperty(USERNAME_PROPNAME);
+ _password = properties.getProperty(PASSWORD_PROPNAME);
+ _virtualpath = properties.getProperty(VIRTUAL_HOST_PROPNAME);
+
+ createConnection();
+ }
+
+ protected void createConnection()
+ {
+ try
+ {
+ _connection = new AMQConnection(_brokerDetails, _username, _password, "PersistentTest", _virtualpath);
+
+ _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _connection.start();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to create test class due to:" + e.getMessage(), e);
+ System.exit(0);
+ }
+ }
+
+ public void test() throws AMQException, URLSyntaxException
+ {
+
+ // Create the Durable Queue
+ try
+ {
+ _session.createConsumer(_session.createQueue(QUEUE)).close();
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Unable to create Queue due to:" + e.getMessage(), e);
+ System.exit(0);
+ }
+
+ try
+ {
+ if (testQueue())
+ {
+ // close connection
+ _connection.close();
+ // wait
+ System.out.println("Restart Broker Now");
+ try
+ {
+ System.in.read();
+ }
+ catch (IOException e)
+ {
+ //
+ }
+ finally
+ {
+ System.out.println("Continuing....");
+ }
+
+ // Test queue is still there.
+ AMQConnection connection =
+ new AMQConnection(_brokerDetails, _username, _password, "DifferentClientID", _virtualpath);
+
+ AMQSession session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ try
+ {
+ session.createConsumer(session.createQueue(QUEUE));
+ _logger.error("Create consumer succeeded."
+ + " This shouldn't be allowed as this means the queue didn't exist when it should");
+
+ connection.close();
+
+ exit();
+ }
+ catch (JMSException e)
+ {
+ try
+ {
+ connection.close();
+ }
+ catch (JMSException cce)
+ {
+ if (cce.getLinkedException() instanceof AMQConnectionClosedException)
+ {
+ _logger.error("Channel Close Bug still present QPID-432, should see an 'Error closing session'");
+ }
+ else
+ {
+ exit(cce);
+ }
+ }
+
+ if (e.getLinkedException() instanceof AMQChannelClosedException)
+ {
+ _logger.info("AMQChannelClosedException received as expected");
+ }
+ else
+ {
+ exit(e);
+ }
+ }
+ }
+ }
+ catch (JMSException e)
+ {
+ _logger.error("Unable to test Queue due to:" + e.getMessage(), e);
+ System.exit(0);
+ }
+ }
+
+ private void exit(JMSException e)
+ {
+ _logger.error("JMSException received:" + e.getMessage());
+ e.printStackTrace();
+ exit();
+ }
+
+ private void exit()
+ {
+ try
+ {
+ _connection.close();
+ }
+ catch (JMSException e)
+ {
+ //
+ }
+
+ System.exit(0);
+ }
+
+ private boolean testQueue() throws JMSException
+ {
+ String TEST_TEXT = "init";
+
+ // Create a new session to send producer
+ Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Queue q = session.createQueue(QUEUE);
+ MessageProducer producer = session.createProducer(q);
+
+ producer.send(session.createTextMessage(TEST_TEXT));
+
+ // create a new consumer on the original session
+ TextMessage m = (TextMessage) _session.createConsumer(q).receive();
+
+ if ((m != null) && m.getText().equals(TEST_TEXT))
+ {
+ return true;
+ }
+ else
+ {
+ _logger.error("Incorrect values returned from Queue Test:" + m);
+ System.exit(0);
+
+ return false;
+ }
+ }
+
+ /** Holds the name of the property to get the test broker url from. */
+ public static final String BROKER_PROPNAME = "broker";
+
+ /** Holds the default broker url for the test. */
+ public static final String BROKER_DEFAULT = "tcp://localhost:5672";
+
+ /** Holds the name of the property to get the test broker virtual path. */
+ public static final String VIRTUAL_HOST_PROPNAME = "virtualHost";
+
+ /** Holds the default virtual path for the test. */
+ public static final String VIRTUAL_HOST_DEFAULT = "";
+
+ /** Holds the name of the property to get the broker access username from. */
+ public static final String USERNAME_PROPNAME = "username";
+
+ /** Holds the default broker log on username. */
+ public static final String USERNAME_DEFAULT = "guest";
+
+ /** Holds the name of the property to get the broker access password from. */
+ public static final String PASSWORD_PROPNAME = "password";
+
+ /** Holds the default broker log on password. */
+ public static final String PASSWORD_DEFAULT = "guest";
+
+ /** Holds the default configuration properties. */
+ public static Properties defaults = new Properties();
+
+ static
+ {
+ defaults.setProperty(BROKER_PROPNAME, BROKER_DEFAULT);
+ defaults.setProperty(USERNAME_PROPNAME, USERNAME_DEFAULT);
+ defaults.setProperty(PASSWORD_PROPNAME, PASSWORD_DEFAULT);
+ defaults.setProperty(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT);
+ }
+
+ public static void main(String[] args)
+ {
+ PersistentTestManual test;
+
+ Properties options =
+ CommandLineParser.processCommandLine(args, new CommandLineParser(new String[][] {}), System.getProperties());
+
+ test = new PersistentTestManual(options);
+ try
+ {
+ test.test();
+ System.out.println("Test was successfull.");
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to test due to:" + e.getMessage(), e);
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/QueueDepthWithSelectorTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/QueueDepthWithSelectorTest.java
new file mode 100644
index 0000000000..7bbfdb5543
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/QueueDepthWithSelectorTest.java
@@ -0,0 +1,214 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.queue;
+
+import java.util.Hashtable;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+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.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+
+/**
+ * Test Case to ensure that messages are correctly returned.
+ * This includes checking:
+ * - The message is returned.
+ * - The broker doesn't leak memory.
+ * - The broker's state is correct after test.
+ */
+public class QueueDepthWithSelectorTest extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(QueueDepthWithSelectorTest.class);
+
+ protected final String BROKER = "vm://:1";
+ protected final String VHOST = "test";
+ protected final String QUEUE = this.getClass().getName();
+
+ private Context _context;
+
+ private Connection _clientConnection, _producerConnection;
+ private Session _clientSession, _producerSession;
+ private MessageProducer _producer;
+ private MessageConsumer _consumer;
+
+ private static final int MSG_COUNT = 50;
+
+ private Message[] _messages = new Message[MSG_COUNT];
+
+ protected void setUp() throws Exception
+ {
+ if (BROKER.startsWith("vm://"))
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@TTL_TEST_ID/" + VHOST + "?brokerlist='" + BROKER + "'");
+ env.put("queue.queue", QUEUE);
+
+ _context = factory.getInitialContext(env);
+
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ if (_producerConnection != null)
+ {
+ _producerConnection.close();
+ }
+
+ if (_clientConnection != null)
+ {
+ _clientConnection.close();
+ }
+
+ if (BROKER.startsWith("vm://"))
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+ }
+
+ public void test() throws Exception
+ {
+ init();
+ //Send messages
+ _logger.info("Starting to send messages");
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ _producer.send(nextMessage(msg));
+ }
+ _logger.info("Closing connection");
+ //Close the connection.. .giving the broker time to clean up its state.
+ _producerConnection.close();
+
+ //Verify we get all the messages.
+ _logger.info("Verifying messages");
+ verifyAllMessagesRecevied();
+
+ //Close the connection.. .giving the broker time to clean up its state.
+ _clientConnection.close();
+
+ //Verify Broker state
+ _logger.info("Verifying broker state");
+ verifyBrokerState();
+ }
+
+ private void init() throws NamingException, JMSException
+ {
+ _messages = new Message[MSG_COUNT];
+
+ //Create Producer
+ _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ _producerConnection.start();
+ _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _producer = _producerSession.createProducer((Queue) _context.lookup("queue"));
+
+ // Create consumer
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ _clientConnection.start();
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ _consumer = _clientSession.createConsumer((Queue) _context.lookup("queue"), "key = 23");
+ }
+
+ private void verifyBrokerState()
+ {
+ IApplicationRegistry registry = ApplicationRegistry.getInstance();
+
+ VirtualHost testVhost = registry.getVirtualHostRegistry().getVirtualHost(VHOST);
+ assertNotNull("Unable to get test Vhost", testVhost);
+ assertNotNull("Unable to get test queue registry", testVhost.getQueueRegistry());
+ AMQQueue q = testVhost.getQueueRegistry().getQueue(new AMQShortString(QUEUE));
+ assertNotNull("Unable to get test queue", q);
+ assertEquals("Queue count too big", 0, q.getMessageCount());
+ }
+
+ private void verifyAllMessagesRecevied() throws JMSException
+ {
+
+ boolean[] msgIdRecevied = new boolean[MSG_COUNT];
+
+
+ for (int i = 0; i < MSG_COUNT; i++)
+ {
+ _messages[i] = _consumer.receive(1000);
+ assertNotNull("should have received a message but didn't", _messages[i]);
+ }
+
+ //Check received messages
+ int msgId = 0;
+ for (Message msg : _messages)
+ {
+ assertNotNull("Message should not be null", msg);
+ assertEquals("msgId was wrong", msgId, msg.getIntProperty("ID"));
+ assertFalse("Already received msg id " + msgId, msgIdRecevied[msgId]);
+ msgIdRecevied[msgId] = true;
+ msgId++;
+ }
+
+ //Check all received
+ for (msgId = 0; msgId < MSG_COUNT; msgId++)
+ {
+ assertTrue("Message " + msgId + " not received.", msgIdRecevied[msgId]);
+ }
+ }
+
+ /**
+ * Get the next message putting the given count into the intProperties as ID.
+ *
+ * @param msgNo the message count to store as ID.
+ *
+ * @return
+ *
+ * @throws JMSException
+ */
+
+ private Message nextMessage(int msgNo) throws JMSException
+ {
+ Message send = _producerSession.createTextMessage("MessageReturnTest");
+ send.setIntProperty("ID", msgNo);
+ send.setIntProperty("key", 23);
+ return send;
+ }
+
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java
new file mode 100644
index 0000000000..d3ec3c11d4
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+
+public class SubscriptionManagerTest extends TestCase
+{
+ private final SubscriptionSet mgr = new SubscriptionSet();
+
+ public void testBasicSubscriptionManagement()
+ {
+ assertTrue(mgr.isEmpty());
+ assertFalse(mgr.hasActiveSubscribers());
+ SubscriptionTestHelper s1 = new SubscriptionTestHelper("S1");
+ mgr.addSubscriber(s1);
+ assertFalse(mgr.isEmpty());
+ assertTrue(mgr.hasActiveSubscribers());
+
+ SubscriptionTestHelper s2 = new SubscriptionTestHelper("S2");
+ mgr.addSubscriber(s2);
+
+ s2.setSuspended(true);
+ assertFalse(mgr.isEmpty());
+ assertTrue(mgr.hasActiveSubscribers());
+ assertTrue(s2.isSuspended());
+ assertFalse(s1.isSuspended());
+
+ s1.setSuspended(true);
+ assertFalse(mgr.hasActiveSubscribers());
+
+ mgr.removeSubscriber(new SubscriptionTestHelper("S1"));
+ assertFalse(mgr.isEmpty());
+ mgr.removeSubscriber(new SubscriptionTestHelper("S2"));
+ assertTrue(mgr.isEmpty());
+ }
+
+ public void testRoundRobin()
+ {
+ SubscriptionTestHelper a = new SubscriptionTestHelper("A");
+ SubscriptionTestHelper b = new SubscriptionTestHelper("B");
+ SubscriptionTestHelper c = new SubscriptionTestHelper("C");
+ SubscriptionTestHelper d = new SubscriptionTestHelper("D");
+ mgr.addSubscriber(a);
+ mgr.addSubscriber(b);
+ mgr.addSubscriber(c);
+ mgr.addSubscriber(d);
+
+ for (int i = 0; i < 3; i++)
+ {
+ assertEquals(a, mgr.nextSubscriber(null));
+ assertEquals(b, mgr.nextSubscriber(null));
+ assertEquals(c, mgr.nextSubscriber(null));
+ assertEquals(d, mgr.nextSubscriber(null));
+ }
+
+ c.setSuspended(true);
+
+ for (int i = 0; i < 3; i++)
+ {
+ assertEquals(a, mgr.nextSubscriber(null));
+ assertEquals(b, mgr.nextSubscriber(null));
+ assertEquals(d, mgr.nextSubscriber(null));
+ }
+
+ mgr.removeSubscriber(a);
+ d.setSuspended(true);
+ c.setSuspended(false);
+ Subscription e = new SubscriptionTestHelper("D");
+ mgr.addSubscriber(e);
+
+ for (int i = 0; i < 3; i++)
+ {
+ assertEquals(b, mgr.nextSubscriber(null));
+ assertEquals(c, mgr.nextSubscriber(null));
+ assertEquals(e, mgr.nextSubscriber(null));
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(SubscriptionManagerTest.class);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java
new file mode 100644
index 0000000000..bcf54693d3
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java
@@ -0,0 +1,144 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import junit.framework.TestCase;
+
+public class SubscriptionSetTest extends TestCase
+{
+ /**
+ * A SubscriptionSet that counts the number of items scanned.
+ */
+ static class TestSubscriptionSet extends SubscriptionSet
+ {
+ private int scanned = 0;
+
+ void resetScanned()
+ {
+ scanned = 0;
+ }
+
+ protected void subscriberScanned()
+ {
+ ++scanned;
+ }
+
+ int getScanned()
+ {
+ return scanned;
+ }
+ }
+
+ final SubscriptionTestHelper sub1 = new SubscriptionTestHelper("1");
+ final SubscriptionTestHelper sub2 = new SubscriptionTestHelper("2");
+ final SubscriptionTestHelper sub3 = new SubscriptionTestHelper("3");
+
+ final SubscriptionTestHelper suspendedSub1 = new SubscriptionTestHelper("sus1", true);
+ final SubscriptionTestHelper suspendedSub2 = new SubscriptionTestHelper("sus2", true);
+ final SubscriptionTestHelper suspendedSub3 = new SubscriptionTestHelper("sus3", true);
+
+ public void testNextMessage()
+ {
+ SubscriptionSet ss = new SubscriptionSet();
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(0, ss.getCurrentSubscriber());
+
+ ss.addSubscriber(sub1);
+ assertEquals(sub1, ss.nextSubscriber(null));
+ assertEquals(1, ss.getCurrentSubscriber());
+ assertEquals(sub1, ss.nextSubscriber(null));
+ assertEquals(1, ss.getCurrentSubscriber());
+
+ ss.addSubscriber(sub2);
+ ss.addSubscriber(sub3);
+
+ assertEquals(sub2, ss.nextSubscriber(null));
+ assertEquals(2, ss.getCurrentSubscriber());
+
+ assertEquals(sub3, ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+ }
+
+ public void testNextMessageWhenAllSuspended()
+ {
+ SubscriptionSet ss = createAllSuspendedSubscriptionSet();
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+ }
+
+ private TestSubscriptionSet createAllSuspendedSubscriptionSet()
+ {
+ TestSubscriptionSet ss = new TestSubscriptionSet();
+ ss.addSubscriber(suspendedSub1);
+ ss.addSubscriber(suspendedSub2);
+ ss.addSubscriber(suspendedSub3);
+ return ss;
+ }
+
+ public void testNextMessageAfterRemove()
+ {
+ SubscriptionSet ss = new SubscriptionSet();
+ ss.addSubscriber(suspendedSub1);
+ ss.addSubscriber(suspendedSub2);
+ ss.addSubscriber(sub3);
+ assertEquals(sub3, ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+
+ assertEquals(suspendedSub1, ss.removeSubscriber(suspendedSub1));
+
+ assertEquals(sub3, ss.nextSubscriber(null)); // Current implementation handles OutOfBoundsException here.
+ assertEquals(2, ss.getCurrentSubscriber());
+ }
+
+ public void testNextMessageOverScanning()
+ {
+ TestSubscriptionSet ss = new TestSubscriptionSet();
+ SubscriptionTestHelper sub = new SubscriptionTestHelper("test");
+ ss.addSubscriber(suspendedSub1);
+ ss.addSubscriber(sub);
+ ss.addSubscriber(suspendedSub3);
+ assertEquals(sub, ss.nextSubscriber(null));
+ assertEquals(2, ss.getCurrentSubscriber());
+ assertEquals(2, ss.getScanned());
+
+ ss.resetScanned();
+ sub.setSuspended(true);
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+ // Current implementation overscans by one item here.
+ assertEquals(ss.size() + 1, ss.getScanned());
+ }
+
+ public void testNextMessageOverscanWorstCase() {
+ TestSubscriptionSet ss = createAllSuspendedSubscriptionSet();
+ ss.nextSubscriber(null);
+ // Scans the subscriptions twice.
+ assertEquals(ss.size() * 2, ss.getScanned());
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(SubscriptionSetTest.class);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
new file mode 100644
index 0000000000..fe947ef3bc
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
@@ -0,0 +1,160 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQChannel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+
+public class SubscriptionTestHelper implements Subscription
+{
+ private final List<AMQMessage> messages;
+ private final Object key;
+ private boolean isSuspended;
+
+ public SubscriptionTestHelper(Object key)
+ {
+ this(key, new ArrayList<AMQMessage>());
+ }
+
+ public SubscriptionTestHelper(final Object key, final boolean isSuspended)
+ {
+ this(key);
+ setSuspended(isSuspended);
+ }
+
+ SubscriptionTestHelper(Object key, List<AMQMessage> messages)
+ {
+ this.key = key;
+ this.messages = messages;
+ }
+
+ List<AMQMessage> getMessages()
+ {
+ return messages;
+ }
+
+ public void send(AMQMessage msg, AMQQueue queue)
+ {
+ messages.add(msg);
+ }
+
+ public void setSuspended(boolean suspended)
+ {
+ isSuspended = suspended;
+ }
+
+ public boolean isSuspended()
+ {
+ return isSuspended;
+ }
+
+ public boolean wouldSuspend(AMQMessage msg)
+ {
+ return isSuspended;
+ }
+
+ public void addToResendQueue(AMQMessage msg)
+ {
+ //no-op
+ }
+
+ public Object getSendLock()
+ {
+ return new Object();
+ }
+
+ public AMQChannel getChannel()
+ {
+ return null;
+ }
+
+ public void queueDeleted(AMQQueue queue)
+ {
+ }
+
+ 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
+ }
+
+ public boolean isAutoClose()
+ {
+ return false;
+ }
+
+ public void close()
+ {
+ //no-op
+ }
+
+ public boolean isClosed()
+ {
+ return false;
+ }
+
+ public boolean isBrowser()
+ {
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return key.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return o instanceof SubscriptionTestHelper && ((SubscriptionTestHelper) o).key.equals(key);
+ }
+
+ public String toString()
+ {
+ return key.toString();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java
new file mode 100644
index 0000000000..06956ba52f
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java
@@ -0,0 +1,166 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+import junit.framework.Assert;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.log4j.Logger;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.Context;
+import java.util.Hashtable;
+
+
+/** Test Case provided by client Non-functional Test NF101: heap exhaustion behaviour */
+public class TimeToLiveTest extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(TimeToLiveTest.class);
+
+
+ protected final String BROKER = "vm://:1";
+ protected final String VHOST = "/test";
+ protected final String QUEUE = "TimeToLiveQueue";
+
+ 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;
+
+ protected void setUp() throws Exception
+ {
+ if (BROKER.startsWith("vm://"))
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@TTL_TEST_ID" + VHOST + "?brokerlist='" + BROKER + "'");
+ env.put("queue.queue", QUEUE);
+
+ _context = factory.getInitialContext(env);
+
+ Queue queue = (Queue) _context.lookup("queue");
+
+ //Create Client 1
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _consumer = _clientSession.createConsumer(queue);
+
+ //Create Producer
+ _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _producerConnection.start();
+
+ _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _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.setTimeToLive(TIME_TO_LIVE);
+
+ for (; msg < MSG_COUNT - 2; msg++)
+ {
+ _producer.send(nextMessage(String.valueOf(msg), false));
+ }
+
+ //Reset TTL
+ _producer.setTimeToLive(0L);
+ _producer.send(nextMessage(String.valueOf(msg), false));
+
+ try
+ {
+ // Sleep to ensure TTL reached
+ Thread.sleep(2000);
+ }
+ catch (InterruptedException e)
+ {
+
+ }
+
+ _clientConnection.start();
+
+ //Receive Message 0
+ Message received = _consumer.receive(100);
+ 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);
+ 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);
+ Assert.assertNull("More messages received", received);
+ }
+
+ private Message nextMessage(String msg, boolean first) throws JMSException
+ {
+ Message send = _producerSession.createTextMessage("Message " + msg);
+ send.setBooleanProperty("first", first);
+ send.setLongProperty("TTL", _producer.getTimeToLive());
+ return send;
+ }
+
+
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java b/Final/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java
new file mode 100644
index 0000000000..6ffa3e0e02
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MessageMetaData;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.exchange.Exchange;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A message store that does nothing. Designed to be used in tests that do not want to use any message store
+ * functionality.
+ */
+public class SkeletonMessageStore implements MessageStore
+{
+ private final AtomicLong _messageId = new AtomicLong(1);
+
+ public void configure(String base, Configuration config) throws Exception
+ {
+ }
+
+ public void configure(VirtualHost virtualHost, String base, Configuration config) throws Exception
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void close() throws Exception
+ {
+ }
+
+ public void removeMessage(StoreContext s, Long messageId)
+ {
+ }
+
+ public void createExchange(Exchange exchange) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeExchange(Exchange exchange) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void createQueue(AMQQueue queue) throws AMQException
+ {
+ }
+
+ public void beginTran(StoreContext s) throws AMQException
+ {
+ }
+
+ public boolean inTran(StoreContext sc)
+ {
+ return false;
+ }
+
+ public void commitTran(StoreContext storeContext) throws AMQException
+ {
+ }
+
+ public void abortTran(StoreContext storeContext) throws AMQException
+ {
+ }
+
+ public List<AMQQueue> createQueues() throws AMQException
+ {
+ return null;
+ }
+
+ public Long getNewMessageId()
+ {
+ return _messageId.getAndIncrement();
+ }
+
+ public void storeContentBodyChunk(StoreContext sc, Long messageId, int index, ContentChunk contentBody, boolean lastContentBody) throws AMQException
+ {
+
+ }
+
+ public void storeMessageMetaData(StoreContext sc, Long messageId, MessageMetaData messageMetaData) throws AMQException
+ {
+
+ }
+
+ public MessageMetaData getMessageMetaData(StoreContext s,Long messageId) throws AMQException
+ {
+ return null;
+ }
+
+ public ContentChunk getContentBodyChunk(StoreContext s,Long messageId, int index) throws AMQException
+ {
+ return null;
+ }
+
+ public void removeQueue(AMQShortString name) throws AMQException
+ {
+
+ }
+
+ public void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException
+ {
+
+ }
+
+ public void dequeueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException
+ {
+
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java b/Final/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
new file mode 100644
index 0000000000..ab6d9742e4
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
@@ -0,0 +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.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.MessageHandleFactory;
+import org.apache.qpid.server.txn.NonTransactionalContext;
+
+/**
+ * Tests that reference counting works correctly with AMQMessage and the message store
+ */
+public class TestReferenceCounting extends TestCase
+{
+ private TestableMemoryMessageStore _store;
+
+ private StoreContext _storeContext = new StoreContext();
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _store = new TestableMemoryMessageStore();
+ }
+
+ /**
+ * Check that when the reference count is decremented the message removes itself from the store
+ */
+ public void testMessageGetsRemoved() throws AMQException
+ {
+ createPersistentContentHeader();
+
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ AMQMessage message = new AMQMessage(_store.getNewMessageId(), info,
+ new NonTransactionalContext(_store, _storeContext, null, null, null),
+ createPersistentContentHeader());
+ message = message.takeReference();
+
+ // we call routing complete to set up the handle
+ message.routingComplete(_store, _storeContext, new MessageHandleFactory());
+ assertTrue(_store.getMessageMetaDataMap().size() == 1);
+ message.decrementReference(_storeContext);
+ assertTrue(_store.getMessageMetaDataMap().size() == 0);
+ }
+
+ private ContentHeaderBody createPersistentContentHeader()
+ {
+ ContentHeaderBody chb = new ContentHeaderBody();
+ BasicContentHeaderProperties bchp = new BasicContentHeaderProperties();
+ bchp.setDeliveryMode((byte)2);
+ chb.properties = bchp;
+ return chb;
+ }
+
+ public void testMessageRemains() throws AMQException
+ {
+
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return null;
+ }
+
+ public boolean isImmediate()
+ {
+ return false;
+ }
+
+ public boolean isMandatory()
+ {
+ return false;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return null;
+ }
+ };
+
+ AMQMessage message = new AMQMessage(_store.getNewMessageId(),
+ info,
+ new NonTransactionalContext(_store, _storeContext, null, null, null),
+ createPersistentContentHeader());
+
+ message = message.takeReference();
+ // we call routing complete to set up the handle
+ message.routingComplete(_store, _storeContext, new MessageHandleFactory());
+ assertTrue(_store.getMessageMetaDataMap().size() == 1);
+ message = message.takeReference();
+ message.decrementReference(_storeContext);
+ assertTrue(_store.getMessageMetaDataMap().size() == 1);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TestReferenceCounting.class);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/Final/java/systests/src/main/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java
new file mode 100644
index 0000000000..48d808142c
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/store/TestableMemoryMessageStore.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.server.store;
+
+import org.apache.qpid.server.queue.MessageMetaData;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.List;
+
+/**
+ * Adds some extra methods to the memory message store for testing purposes.
+ */
+public class TestableMemoryMessageStore extends MemoryMessageStore
+{
+
+ MemoryMessageStore _mms = null;
+
+ public TestableMemoryMessageStore(MemoryMessageStore mms)
+ {
+ _mms = mms;
+ }
+
+ public TestableMemoryMessageStore()
+ {
+ _metaDataMap = new ConcurrentHashMap<Long, MessageMetaData>();
+ _contentBodyMap = new ConcurrentHashMap<Long, List<ContentChunk>>();
+ }
+
+ public ConcurrentMap<Long, MessageMetaData> getMessageMetaDataMap()
+ {
+ if (_mms != null)
+ {
+ return _mms._metaDataMap;
+ }
+ else
+ {
+ return _metaDataMap;
+ }
+ }
+
+ public ConcurrentMap<Long, List<ContentChunk>> getContentBodyMap()
+ {
+ if (_mms != null)
+ {
+ return _mms._contentBodyMap;
+ }
+ else
+ {
+ return _contentBodyMap;
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java
new file mode 100644
index 0000000000..025f8d89f6
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java
@@ -0,0 +1,307 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.txn;
+
+import junit.framework.TestCase;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.store.StoreContext;
+
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+public class TxnBufferTest extends TestCase
+{
+ private final LinkedList<MockOp> ops = new LinkedList<MockOp>();
+
+ public void testCommit() throws AMQException
+ {
+ MockStore store = new MockStore();
+
+ TxnBuffer buffer = new TxnBuffer();
+ buffer.enlist(new MockOp().expectPrepare().expectCommit());
+ //check relative ordering
+ MockOp op = new MockOp().expectPrepare().expectPrepare().expectCommit().expectCommit();
+ buffer.enlist(op);
+ buffer.enlist(op);
+ buffer.enlist(new MockOp().expectPrepare().expectCommit());
+
+ buffer.commit(null);
+
+ validateOps();
+ store.validate();
+ }
+
+ public void testRollback() throws AMQException
+ {
+ MockStore store = new MockStore();
+
+ TxnBuffer buffer = new TxnBuffer();
+ buffer.enlist(new MockOp().expectRollback());
+ buffer.enlist(new MockOp().expectRollback());
+ buffer.enlist(new MockOp().expectRollback());
+
+ buffer.rollback(null);
+
+ validateOps();
+ store.validate();
+ }
+
+ public void testCommitWithFailureDuringPrepare() throws AMQException
+ {
+ MockStore store = new MockStore();
+ store.beginTran(null);
+
+ TxnBuffer buffer = new TxnBuffer();
+ buffer.enlist(new StoreMessageOperation(store));
+ buffer.enlist(new MockOp().expectPrepare().expectUndoPrepare());
+ buffer.enlist(new TxnTester(store));
+ buffer.enlist(new MockOp().expectPrepare().expectUndoPrepare());
+ buffer.enlist(new FailedPrepare());
+ buffer.enlist(new MockOp());
+
+ try
+ {
+ buffer.commit(null);
+
+ }
+ catch (NoSuchElementException e)
+ {
+
+ }
+
+ validateOps();
+ store.validate();
+ }
+
+ public void testCommitWithPersistance() throws AMQException
+ {
+ MockStore store = new MockStore();
+ store.beginTran(null);
+ store.expectCommit();
+
+ TxnBuffer buffer = new TxnBuffer();
+ buffer.enlist(new MockOp().expectPrepare().expectCommit());
+ buffer.enlist(new MockOp().expectPrepare().expectCommit());
+ buffer.enlist(new MockOp().expectPrepare().expectCommit());
+ buffer.enlist(new StoreMessageOperation(store));
+ buffer.enlist(new TxnTester(store));
+
+ buffer.commit(null);
+ validateOps();
+ store.validate();
+ }
+
+ private void validateOps()
+ {
+ for (MockOp op : ops)
+ {
+ op.validate();
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TxnBufferTest.class);
+ }
+
+ class MockOp implements TxnOp
+ {
+ final Object PREPARE = "PREPARE";
+ final Object COMMIT = "COMMIT";
+ final Object UNDO_PREPARE = "UNDO_PREPARE";
+ final Object ROLLBACK = "ROLLBACK";
+
+ private final LinkedList expected = new LinkedList();
+
+ MockOp()
+ {
+ ops.add(this);
+ }
+
+ public void prepare(StoreContext context)
+ {
+ assertEquals(expected.removeLast(), PREPARE);
+ }
+
+ public void commit(StoreContext context)
+ {
+ assertEquals(expected.removeLast(), COMMIT);
+ }
+
+ public void undoPrepare()
+ {
+ assertEquals(expected.removeLast(), UNDO_PREPARE);
+ }
+
+ public void rollback(StoreContext context)
+ {
+ assertEquals(expected.removeLast(), ROLLBACK);
+ }
+
+ private MockOp expect(Object optype)
+ {
+ expected.addFirst(optype);
+ return this;
+ }
+
+ MockOp expectPrepare()
+ {
+ return expect(PREPARE);
+ }
+
+ MockOp expectCommit()
+ {
+ return expect(COMMIT);
+ }
+
+ MockOp expectUndoPrepare()
+ {
+ return expect(UNDO_PREPARE);
+ }
+
+ MockOp expectRollback()
+ {
+ return expect(ROLLBACK);
+ }
+
+ void validate()
+ {
+ assertEquals("Expected ops were not all invoked", new LinkedList(), expected);
+ }
+
+ void clear()
+ {
+ expected.clear();
+ }
+ }
+
+ class MockStore extends TestableMemoryMessageStore
+ {
+ final Object BEGIN = "BEGIN";
+ final Object ABORT = "ABORT";
+ final Object COMMIT = "COMMIT";
+
+ private final LinkedList expected = new LinkedList();
+ private boolean inTran;
+
+ public void beginTran(StoreContext context) throws AMQException
+ {
+ inTran = true;
+ }
+
+ public void commitTran(StoreContext context) throws AMQException
+ {
+ assertEquals(expected.removeLast(), COMMIT);
+ inTran = false;
+ }
+
+ public void abortTran(StoreContext context) throws AMQException
+ {
+ assertEquals(expected.removeLast(), ABORT);
+ inTran = false;
+ }
+
+ public boolean inTran(StoreContext context)
+ {
+ return inTran;
+ }
+
+ private MockStore expect(Object optype)
+ {
+ expected.addFirst(optype);
+ return this;
+ }
+
+ MockStore expectBegin()
+ {
+ return expect(BEGIN);
+ }
+
+ MockStore expectCommit()
+ {
+ return expect(COMMIT);
+ }
+
+ MockStore expectAbort()
+ {
+ return expect(ABORT);
+ }
+
+ void clear()
+ {
+ expected.clear();
+ }
+
+ void validate()
+ {
+ assertEquals("Expected ops were not all invoked", new LinkedList(), expected);
+ }
+ }
+
+ class NullOp implements TxnOp
+ {
+ public void prepare(StoreContext context) throws AMQException
+ {
+ }
+ public void commit(StoreContext context)
+ {
+ }
+ public void undoPrepare()
+ {
+ }
+ public void rollback(StoreContext context)
+ {
+ }
+ }
+
+ class FailedPrepare extends NullOp
+ {
+ public void prepare() throws AMQException
+ {
+ throw new AMQException("Fail!");
+ }
+ }
+
+ class TxnTester extends NullOp
+ {
+ private final MessageStore store;
+
+ private final StoreContext context = new StoreContext();
+
+ TxnTester(MessageStore store)
+ {
+ this.store = store;
+ }
+
+ public void prepare() throws AMQException
+ {
+ assertTrue("Expected prepare to be performed under txn", store.inTran(context));
+ }
+
+ public void commit()
+ {
+ assertTrue("Expected commit not to be performed under txn", !store.inTran(context));
+ }
+ }
+
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnTest.java b/Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnTest.java
new file mode 100644
index 0000000000..2957dda869
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/txn/TxnTest.java
@@ -0,0 +1,188 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.txn;
+
+import junit.framework.TestCase;
+import junit.framework.Assert;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.log4j.Logger;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.ConnectionFactory;
+import javax.jms.Connection;
+import javax.jms.Message;
+import javax.jms.TextMessage;
+import javax.jms.MessageListener;
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.Context;
+import java.util.Hashtable;
+import java.util.concurrent.CountDownLatch;
+
+
+/** Test Case Qpid-617 */
+public class TxnTest extends TestCase implements MessageListener
+{
+ private static final Logger _logger = Logger.getLogger(TxnTest.class);
+
+
+ protected final String BROKER = "vm://:1";//"localhost";
+ protected final String VHOST = "/test";
+ protected final String QUEUE = "TxnTestQueue";
+
+
+ Context _context;
+ Queue _queue;
+
+ private Connection _clientConnection, _producerConnection;
+
+ private MessageConsumer _consumer;
+ MessageProducer _producer;
+ Session _clientSession, _producerSession;
+ private CountDownLatch commit = new CountDownLatch(1);
+
+ protected void setUp() throws Exception
+ {
+ if (BROKER.startsWith("vm://"))
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@TTL_TEST_ID" + VHOST + "?brokerlist='" + BROKER + "'");
+ env.put("queue.queue", QUEUE);
+
+ _context = factory.getInitialContext(env);
+
+ _queue = (Queue) _context.lookup("queue");
+
+ //Create Client 1
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientSession = _clientConnection.createSession(true, 0);
+
+ _consumer = _clientSession.createConsumer(_queue);
+
+ //Create Producer
+ _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _producerConnection.start();
+
+ _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ _producer = _producerSession.createProducer(_queue);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ if (_clientConnection != null)
+ {
+ _clientConnection.close();
+ }
+
+ if (_producerConnection != null)
+ {
+ _producerConnection.close();
+ }
+
+ super.tearDown();
+
+ if (BROKER.startsWith("vm://"))
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+ }
+
+
+ public void testMessageListener() throws JMSException
+ {
+ _consumer.setMessageListener(this);
+ _clientConnection.start();
+
+ //Set TTL
+ _producer.send(_producerSession.createTextMessage("TxtTestML"));
+
+
+ try
+ {
+ //Wait for message to arrive
+ commit.await();
+ }
+ catch (InterruptedException e)
+ {
+
+ }
+ _consumer.close();
+
+ _consumer = _clientSession.createConsumer(_queue);
+
+ //Receive Message
+ Message received = _consumer.receive(1000);
+ assertNull("More messages received", received);
+
+ _consumer.close();
+ }
+
+ public void onMessage(Message message)
+ {
+
+ try
+ {
+ assertEquals("Incorrect Message Received.", "TxtTestML", ((TextMessage) message).getText());
+
+ _clientSession.commit();
+ }
+ catch (JMSException e)
+ {
+ fail("Failed to commit");
+ }
+
+ commit.countDown();
+ }
+
+
+ public void testReceive() throws JMSException
+ {
+ _clientConnection.start();
+
+ //Set TTL
+ _producer.send(_producerSession.createTextMessage("TxtTestReceive"));
+
+ //Receive Message
+ Message received = _consumer.receive(1000);
+
+ assertEquals("Incorrect Message Received.", "TxtTestReceive", ((TextMessage) received).getText());
+ //Receive Message
+
+ received = _consumer.receive(1000);
+
+ assertNull("More messages received", received);
+
+ _consumer.close();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/util/AveragedRun.java b/Final/java/systests/src/main/java/org/apache/qpid/server/util/AveragedRun.java
new file mode 100644
index 0000000000..1d17985ab5
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/util/AveragedRun.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.server.util;
+
+import org.apache.qpid.server.util.TimedRun;
+
+import java.util.concurrent.Callable;
+import java.util.Collection;
+
+public class AveragedRun implements Callable<RunStats>
+{
+ private final RunStats stats = new RunStats();
+ private final TimedRun test;
+ private final int iterations;
+
+ public AveragedRun(TimedRun test, int iterations)
+ {
+ this.test = test;
+ this.iterations = iterations;
+ }
+
+ public RunStats call() throws Exception
+ {
+ for (int i = 0; i < iterations; i++)
+ {
+ stats.record(test.call());
+ }
+ return stats;
+ }
+
+ public void run() throws Exception
+ {
+ System.out.println(test + ": " + call());
+ }
+
+ public String toString()
+ {
+ return test.toString();
+ }
+
+ static void run(Collection<AveragedRun> tests) throws Exception
+ {
+ for(AveragedRun test : tests)
+ {
+ test.run();
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/util/RunStats.java b/Final/java/systests/src/main/java/org/apache/qpid/server/util/RunStats.java
new file mode 100644
index 0000000000..ec67fc68b3
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/util/RunStats.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.util;
+
+public class RunStats
+{
+ private long min = Long.MAX_VALUE;
+ private long max;
+ private long total;
+ private int count;
+
+ public void record(long time)
+ {
+ max = Math.max(time, max);
+ min = Math.min(time, min);
+ total += time;
+ count++;
+ }
+
+ public long getMin()
+ {
+ return min;
+ }
+
+ public long getMax()
+ {
+ return max;
+ }
+
+ public long getAverage()
+ {
+ return total / count;
+ }
+
+ public String toString()
+ {
+ return "avg=" + getAverage() + ", min=" + min + ", max=" + max;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/Final/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java
new file mode 100644
index 0000000000..bd7ed60d1d
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java
@@ -0,0 +1,149 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util;
+
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+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.store.MessageStore;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.MapConfiguration;
+
+import java.util.HashMap;
+import java.util.Collection;
+import java.util.Properties;
+
+public class TestApplicationRegistry extends ApplicationRegistry
+{
+ private QueueRegistry _queueRegistry;
+
+ private ExchangeRegistry _exchangeRegistry;
+
+ private ExchangeFactory _exchangeFactory;
+
+ private ManagedObjectRegistry _managedObjectRegistry;
+
+ private AccessManager _accessManager;
+
+ private PrincipalDatabaseManager _databaseManager;
+
+ private AuthenticationManager _authenticationManager;
+
+ private MessageStore _messageStore;
+ private VirtualHost _vHost;
+
+ public TestApplicationRegistry()
+ {
+ super(new MapConfiguration(new HashMap()));
+ }
+
+ public void initialise() throws Exception
+ {
+ Properties users = new Properties();
+
+ users.put("guest", "guest");
+
+ _databaseManager = new PropertiesPrincipalDatabaseManager("default", users);
+
+ _accessManager = new AllowAll();
+
+ _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null);
+
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ _managedObjectRegistry = appRegistry.getManagedObjectRegistry();
+ _vHost = appRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ _queueRegistry = _vHost.getQueueRegistry();
+ _exchangeFactory = _vHost.getExchangeFactory();
+ _exchangeRegistry = _vHost.getExchangeRegistry();
+
+ _messageStore = new TestableMemoryMessageStore();
+
+ _configuration.addProperty("heartbeat.delay", 10 * 60); // 10 minutes
+ }
+
+ public Configuration getConfiguration()
+ {
+ return _configuration;
+ }
+
+ public QueueRegistry getQueueRegistry()
+ {
+ return _queueRegistry;
+ }
+
+ public ExchangeRegistry getExchangeRegistry()
+ {
+ return _exchangeRegistry;
+ }
+
+ public ExchangeFactory getExchangeFactory()
+ {
+ return _exchangeFactory;
+ }
+
+ public ManagedObjectRegistry getManagedObjectRegistry()
+ {
+ return _managedObjectRegistry;
+ }
+
+ public PrincipalDatabaseManager getDatabaseManager()
+ {
+ return _databaseManager;
+ }
+
+ public AuthenticationManager getAuthenticationManager()
+ {
+ return _authenticationManager;
+ }
+
+ public Collection<String> getVirtualHostNames()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public VirtualHostRegistry getVirtualHostRegistry()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public AccessManager getAccessManager()
+ {
+ return _accessManager;
+ }
+
+ public MessageStore getMessageStore()
+ {
+ return _messageStore;
+ }
+}
+
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/server/util/TimedRun.java b/Final/java/systests/src/main/java/org/apache/qpid/server/util/TimedRun.java
new file mode 100644
index 0000000000..1291380311
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/server/util/TimedRun.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.util.concurrent.Callable;
+
+public abstract class TimedRun implements Callable<Long>
+{
+ private final String description;
+
+ public TimedRun(String description)
+ {
+ this.description = description;
+ }
+
+ public Long call() throws Exception
+ {
+ setup();
+ long start = System.currentTimeMillis();
+ run();
+ long stop = System.currentTimeMillis();
+ teardown();
+ return stop - start;
+ }
+
+ public String toString()
+ {
+ return description;
+ }
+
+ protected void setup() throws Exception{}
+ protected void teardown() throws Exception{}
+ protected abstract void run() throws Exception;
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/VMBrokerSetup.java b/Final/java/systests/src/main/java/org/apache/qpid/test/VMBrokerSetup.java
new file mode 100644
index 0000000000..e859fac4af
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/VMBrokerSetup.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+
+import org.apache.qpid.client.transport.TransportConnection;
+
+public class VMBrokerSetup extends TestSetup
+{
+ public VMBrokerSetup(Test t)
+ {
+ super(t);
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ catch (Exception e)
+ {
+ fail("Unable to create broker: " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception
+ {
+ TransportConnection.killVMBroker(1);
+ super.tearDown();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java b/Final/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java
new file mode 100644
index 0000000000..9629f87d46
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java
@@ -0,0 +1,128 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.extensions.TestSetup;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class VMTestCase extends TestCase
+{
+ protected long RECEIVE_TIMEOUT = 1000L; // 1 sec
+ protected long CLOSE_TIMEOUT = 10000L; // 10 secs
+
+ protected Context _context;
+ protected String _clientID;
+ protected String _virtualhost;
+ protected String _brokerlist;
+
+ protected final Map<String, String> _connections = new HashMap<String, String>();
+ protected final Map<String, String> _queues = new HashMap<String, String>();
+ protected final Map<String, String> _topics = new HashMap<String, String>();
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ try
+ {
+ TransportConnection.createVMBroker(1);
+ }
+ catch (Exception e)
+ {
+ fail("Unable to create broker: " + e);
+ }
+
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ if (_clientID == null)
+ {
+ _clientID = this.getClass().getName();
+ }
+
+ if (_virtualhost == null)
+ {
+ _virtualhost = "/test";
+ }
+
+ if (_brokerlist == null)
+ {
+ _brokerlist = "vm://:1";
+ }
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@" + _clientID + _virtualhost + "?brokerlist='"
+ + _brokerlist + "'");
+
+ for (Map.Entry<String, String> c : _connections.entrySet())
+ {
+ env.put("connectionfactory." + c.getKey(), c.getValue());
+ }
+
+ env.put("queue.queue", "queue");
+
+ for (Map.Entry<String, String> q : _queues.entrySet())
+ {
+ env.put("queue." + q.getKey(), q.getValue());
+ }
+
+ env.put("topic.topic", "topic");
+
+ for (Map.Entry<String, String> t : _topics.entrySet())
+ {
+ env.put("topic." + t.getKey(), t.getValue());
+ }
+
+ _context = factory.getInitialContext(env);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ TransportConnection.killVMBroker(1);
+ ApplicationRegistry.remove(1);
+
+ super.tearDown();
+ }
+
+ public void testDummyinVMTestCase()
+ {
+ // keep maven happy
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java b/Final/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java
new file mode 100644
index 0000000000..ec9df8f1b3
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Assertion.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Assertion.java
new file mode 100644
index 0000000000..75c1c97999
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Assertion.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+/**
+ * Assertion models an assertion on a test {@link Circuit}.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Indicate whether or not the assertion passes when applied.
+ * </table>
+ */
+public interface Assertion
+{
+ /**
+ * Applies the assertion.
+ *
+ * @return <tt>true</tt> if the assertion passes, <tt>false</tt> if it fails.
+ */
+ public boolean apply();
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/AssertionBase.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/AssertionBase.java
new file mode 100644
index 0000000000..3d83224513
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/AssertionBase.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.test.framework;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * AssertionBase is a base class for implenmenting assertions. It provides a mechanism to store error messages, and
+ * report all error messages when its {@link #toString()} method is called.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Collect error messages.
+ * </table>
+ */
+public abstract class AssertionBase implements Assertion
+{
+ /** Holds the error messages. */
+ List<String> errors = new LinkedList<String>();
+
+ /**
+ * Adds an error message to the assertion.
+ *
+ * @param error An error message to add to the assertion.
+ */
+ public void addError(String error)
+ {
+ errors.add(error);
+ }
+
+ /**
+ * Prints all of the error messages in the assertion into a string.
+ *
+ * @return All of the error messages in the assertion as a string.
+ */
+ public String toString()
+ {
+ String result = "";
+
+ for (String error : errors)
+ {
+ result += error;
+ }
+
+ return result;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Circuit.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Circuit.java
new file mode 100644
index 0000000000..d665acb62c
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Circuit.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+import java.util.List;
+
+/**
+ * A Circuit is the basic test unit against which test cases are to be written. A circuit consists of two 'ends', an
+ * instigating 'publisher' end and a more passive 'receivers' end.
+ *
+ * <p/>Once created, the life-cycle of a circuit may be controlled by {@link #start()}ing it, or {@link #close()}ing it.
+ * Once started, the circuit is ready to send messages over. Once closed the circuit can no longer be used.
+ *
+ * <p/>The state of the circuit may be taken with the {@link #check()} method, and asserted against by the
+ * {@link #applyAssertions(java.util.List)} method.
+ *
+ * <p/>There is a default test procedure which may be performed against the circuit. The outline of this procedure is:
+ *
+ * <p/><pre>
+ * Start the circuit.
+ * Send test messages.
+ * Request a status report.
+ * Assert conditions on the publishing end of the circuit.
+ * Assert conditions on the receiving end of the circuit.
+ * Close the circuit.
+ * Pass with no failed assertions or fail with a list of failed assertions.
+ * </pre>
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Supply the publishing and receiving ends of a test messaging circuit.
+ * <tr><td> Start the circuit running.
+ * <tr><td> Close the circuit down.
+ * <tr><td> Take a reading of the circuits state.
+ * <tr><td> Apply assertions against the circuits state.
+ * <tr><td> Send test messages over the circuit.
+ * <tr><td> Perform the default test procedue on the circuit.
+ * </table>
+ */
+public interface Circuit
+{
+ /**
+ * Gets the interface on the publishing end of the circuit.
+ *
+ * @return The publishing end of the circuit.
+ */
+ public Publisher getPublisher();
+
+ /**
+ * Gets the interface on the receiving end of the circuit.
+ *
+ * @return The receiving end of the circuit.
+ */
+ public Receiver getReceiver();
+
+ /**
+ * Connects and starts the circuit. After this method is called the circuit is ready to send messages.
+ */
+ public void start();
+
+ /**
+ * Checks the test circuit. The effect of this is to gather the circuits state, for both ends of the circuit,
+ * into a report, against which assertions may be checked.
+ */
+ public void check();
+
+ /**
+ * Closes the circuit. All associated resources are closed.
+ */
+ public void close();
+
+ /**
+ * Applied a list of assertions against the test circuit. The {@link #check()} method should be called before doing
+ * this, to ensure that the circuit has gathered its state into a report to assert against.
+ *
+ * @param assertions The list of assertions to apply to the circuit.
+ *
+ * @return Any assertions that failed.
+ */
+ public List<Assertion> applyAssertions(List<Assertion> assertions);
+
+ /**
+ * Runs the default test procedure against the circuit, and checks that all of the specified assertions hold.
+ *
+ * @param numMessages The number of messages to send using the default test procedure.
+ * @param assertions The list of assertions to apply.
+ *
+ * @return Any assertions that failed.
+ */
+ public List<Assertion> test(int numMessages, List<Assertion> assertions);
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEnd.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEnd.java
new file mode 100644
index 0000000000..e557620cf5
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEnd.java
@@ -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.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+import javax.jms.*;
+
+/**
+ * A CircuitEnd is a pair consisting of one message producer and one message consumer, that represents one end of a
+ * test circuit. It is a standard unit of connectivity allowing a full-duplex conversation to be held, provided both
+ * the consumer and producer are instantiated and configured.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide a message producer for sending messages.
+ * <tr><td> Provide a message consumer for receiving messages.
+ * </table>
+ *
+ * @todo Update the {@link org.apache.qpid.util.ConversationFactory} so that it accepts these as the basic conversation
+ * connection units.
+ */
+public interface CircuitEnd
+{
+ /**
+ * Gets the message producer at this circuit end point.
+ *
+ * @return The message producer at with this circuit end point.
+ */
+ public MessageProducer getProducer();
+
+ /**
+ * Gets the message consumer at this circuit end point.
+ *
+ * @return The message consumer at this circuit end point.
+ */
+ public MessageConsumer getConsumer();
+
+ /**
+ * Send the specified message over the producer at this end point.
+ *
+ * @param message The message to send.
+ *
+ * @throws JMSException Any JMS exception occuring during the send is allowed to fall through.
+ */
+ public void send(Message message) throws JMSException;
+
+ /**
+ * Gets the JMS Session associated with this circuit end point.
+ *
+ * @return The JMS Session associated with this circuit end point.
+ */
+ public Session getSession();
+
+ /**
+ * Closes the message producers and consumers and the sessions, associated with this circuit end point.
+ *
+ * @throws JMSException Any JMSExceptions occurring during the close are allowed to fall through.
+ */
+ public void close() throws JMSException;
+
+ /**
+ * Returns the message monitor for reporting on received messages on this circuit end.
+ *
+ * @return The message monitor for this circuit end.
+ */
+ public MessageMonitor getMessageMonitor();
+
+ /**
+ * Returns the exception monitor for reporting on exceptions received on this circuit end.
+ *
+ * @return The exception monitor for this circuit end.
+ */
+ public ExceptionMonitor getExceptionMonitor();
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java
new file mode 100644
index 0000000000..251b216c45
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java
@@ -0,0 +1,149 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+import javax.jms.*;
+
+/**
+ * A CircuitEndBase is a pair consisting of one message producer and one message consumer, that represents one end of a
+ * test circuit. It is a standard unit of connectivity allowing a full-duplex conversation to be held, provided both
+ * the consumer and producer are instantiated and configured.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide a message producer for sending messages.
+ * <tr><td> Provide a message consumer for receiving messages.
+ * </table>
+ */
+public class CircuitEndBase implements CircuitEnd
+{
+ /** Holds the single message producer. */
+ MessageProducer producer;
+
+ /** Holds the single message consumer. */
+ MessageConsumer consumer;
+
+ /** Holds the controlSession for the circuit end. */
+ Session session;
+
+ /** Holds the message monitor for the circuit end. */
+ MessageMonitor messageMonitor;
+
+ /** Holds the exception monitor for the circuit end. */
+ ExceptionMonitor exceptionMonitor;
+
+ /**
+ * Creates a circuit end point on the specified producer, consumer and controlSession.
+ *
+ * @param producer The message producer for the circuit end point.
+ * @param consumer The message consumer for the circuit end point.
+ * @param session The controlSession for the circuit end point.
+ */
+ public CircuitEndBase(MessageProducer producer, MessageConsumer consumer, Session session, MessageMonitor messageMonitor,
+ ExceptionMonitor exceptionMonitor)
+ {
+ this.producer = producer;
+ this.consumer = consumer;
+ this.session = session;
+
+ this.messageMonitor = messageMonitor;
+ this.exceptionMonitor = exceptionMonitor;
+ }
+
+ /**
+ * Gets the message producer at this circuit end point.
+ *
+ * @return The message producer at with this circuit end point.
+ */
+ public MessageProducer getProducer()
+ {
+ return producer;
+ }
+
+ /**
+ * Gets the message consumer at this circuit end point.
+ *
+ * @return The message consumer at this circuit end point.
+ */
+ public MessageConsumer getConsumer()
+ {
+ return consumer;
+ }
+
+ /**
+ * Send the specified message over the producer at this end point.
+ *
+ * @param message The message to send.
+ * @throws javax.jms.JMSException Any JMS exception occuring during the send is allowed to fall through.
+ */
+ public void send(Message message) throws JMSException
+ {
+ producer.send(message);
+ }
+
+ /**
+ * Gets the JMS Session associated with this circuit end point.
+ *
+ * @return The JMS Session associated with this circuit end point.
+ */
+ public Session getSession()
+ {
+ return session;
+ }
+
+ /**
+ * Closes the message producers and consumers and the sessions, associated with this circuit end point.
+ *
+ * @throws javax.jms.JMSException Any JMSExceptions occurring during the close are allowed to fall through.
+ */
+ public void close() throws JMSException
+ {
+ if (producer != null)
+ {
+ producer.close();
+ }
+
+ if (consumer != null)
+ {
+ consumer.close();
+ }
+ }
+
+ /**
+ * Returns the message monitor for reporting on received messages on this circuit end.
+ *
+ * @return The message monitor for this circuit end.
+ */
+ public MessageMonitor getMessageMonitor()
+ {
+ return messageMonitor;
+ }
+
+ /**
+ * Returns the exception monitor for reporting on exceptions received on this circuit end.
+ *
+ * @return The exception monitor for this circuit end.
+ */
+ public ExceptionMonitor getExceptionMonitor()
+ {
+ return exceptionMonitor;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/DropInTest.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/DropInTest.java
new file mode 100644
index 0000000000..12cf0d79d5
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/DropInTest.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.framework;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+/**
+ * A DropIn test is a test case that can accept late joining test clients into a running test. This can be usefull,
+ * for interactive experimentation.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Accept late joining test clients.
+ * </table>
+ */
+public interface DropInTest
+{
+ /**
+ * Should accept a late joining client into a running test case. The client will be enlisted with a control message
+ * with the 'CONTROL_TYPE' field set to the value 'LATEJOIN'. It should also provide values for the fields:
+ *
+ * <p/><table>
+ * <tr><td> CLIENT_NAME <td> A unique name for the new client.
+ * <tr><td> CLIENT_PRIVATE_CONTROL_KEY <td> The key for the route on which the client receives its control messages.
+ * </table>
+ *
+ * @param message The late joiners join message.
+ *
+ * @throws JMSException Any JMS Exception are allowed to fall through, indicating that the join failed.
+ */
+ public void lateJoin(Message message) throws JMSException;
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java
new file mode 100644
index 0000000000..9f1207202e
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java
@@ -0,0 +1,158 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+import org.apache.log4j.Logger;
+
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An exception monitor, listens for JMS exception on a connection or consumer. It record all exceptions that it receives
+ * and provides methods to test the number and type of exceptions received.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Record all exceptions received. <td> {@link ExceptionListener}
+ * </table>
+ */
+public class ExceptionMonitor implements ExceptionListener
+{
+ /** Used for debugging. */
+ private final Logger log = Logger.getLogger(ExceptionMonitor.class);
+
+ /** Holds the received exceptions. */
+ List<JMSException> exceptions = new ArrayList<JMSException>();
+
+ /**
+ * Receives incoming exceptions.
+ *
+ * @param e The exception to record.
+ */
+ public synchronized void onException(JMSException e)
+ {
+ log.debug("public void onException(JMSException e): called", e);
+
+ exceptions.add(e);
+ }
+
+ /**
+ * Checks that no exceptions have been received.
+ *
+ * @return <tt>true</tt> if no exceptions have been received, <tt>false</tt> otherwise.
+ */
+ public synchronized boolean assertNoExceptions()
+ {
+ return exceptions.isEmpty();
+ }
+
+ /**
+ * Checks that exactly one exception has been received.
+ *
+ * @return <tt>true</tt> if exactly one exception been received, <tt>false</tt> otherwise.
+ */
+ public synchronized boolean assertOneJMSException()
+ {
+ return exceptions.size() == 1;
+ }
+
+ /**
+ * Checks that exactly one exception, with a linked cause of the specified type, has been received.
+ *
+ * @return <tt>true</tt> if exactly one exception, with a linked cause of the specified type, been received,
+ * <tt>false</tt> otherwise.
+ */
+ public synchronized boolean assertOneJMSExceptionWithLinkedCause(Class aClass)
+ {
+ if (exceptions.size() == 1)
+ {
+ JMSException e = exceptions.get(0);
+
+ Exception linkedCause = e.getLinkedException();
+
+ if ((linkedCause != null) && aClass.isInstance(linkedCause))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Reports the number of exceptions held by this monitor.
+ *
+ * @return The number of exceptions held by this monitor.
+ */
+ public synchronized int size()
+ {
+ return exceptions.size();
+ }
+
+ /**
+ * Clears the record of received exceptions.
+ */
+ public synchronized void reset()
+ {
+ exceptions = new ArrayList<JMSException>();
+ }
+
+ /**
+ * Provides a dump of the stack traces of all exceptions that this exception monitor was notified of. Mainly
+ * use for debugging/test failure reporting purposes.
+ *
+ * @return A string containing a dump of the stack traces of all exceptions.
+ */
+ public synchronized String toString()
+ {
+ String result = "ExceptionMonitor: holds " + exceptions.size() + " exceptions.\n\n";
+
+ for (JMSException ex : exceptions)
+ {
+ result += getStackTrace(ex) + "\n";
+ }
+
+ return result;
+ }
+
+ /**
+ * Prints an exception stack trace into a string.
+ *
+ * @param t The throwable to get the stack trace from.
+ *
+ * @return A string containing the throwables stack trace.
+ */
+ public static String getStackTrace(Throwable t)
+ {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ t.printStackTrace(pw);
+ pw.flush();
+ sw.flush();
+
+ return sw.toString();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java
new file mode 100644
index 0000000000..77bb7a5c52
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java
@@ -0,0 +1,280 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl;
+import org.apache.qpid.test.framework.sequencers.CircuitFactory;
+import org.apache.qpid.util.ConversationFactory;
+
+import uk.co.thebadgerset.junit.extensions.AsymptoticTestCase;
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * FrameworkBaseCase provides a starting point for writing test cases against the test framework. Its main purpose is
+ * to provide some convenience methods for testing.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create and clean up in-vm brokers on every test case.
+ * <tr><td> Produce lists of assertions from assertion creation calls.
+ * <tr><td> Produce JUnit failures from assertion failures.
+ * <tr><td> Convert failed assertions to error messages.
+ * </table>
+ */
+public class FrameworkBaseCase extends AsymptoticTestCase
+{
+ /** Used for debugging purposes. */
+ private static final Logger log = Logger.getLogger(FrameworkBaseCase.class);
+
+ /** Holds the test sequencer to create and run test circuits with. */
+ protected CircuitFactory circuitFactory = new DefaultCircuitFactory();
+
+ /**
+ * Creates a new test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public FrameworkBaseCase(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Returns the test case sequencer that provides test circuit, and test sequence implementations. The sequencer
+ * that this base case returns by default is suitable for running a test circuit with both circuit ends colocated
+ * on the same JVM.
+ *
+ * @return The test case sequencer.
+ */
+ protected CircuitFactory getCircuitFactory()
+ {
+ return circuitFactory;
+ }
+
+ /**
+ * Overrides the default test circuit factory. Test decorators can use this to supply distributed test sequencers or
+ * other test circuit factory specializations.
+ *
+ * @param circuitFactory The new test circuit factory.
+ */
+ public void setCircuitFactory(CircuitFactory circuitFactory)
+ {
+ this.circuitFactory = circuitFactory;
+ }
+
+ /**
+ * Creates a list of assertions.
+ *
+ * @param asserts The assertions to compile in a list.
+ *
+ * @return A list of assertions.
+ */
+ protected List<Assertion> assertionList(Assertion... asserts)
+ {
+ List<Assertion> result = new ArrayList<Assertion>();
+
+ for (Assertion assertion : asserts)
+ {
+ result.add(assertion);
+ }
+
+ return result;
+ }
+
+ /**
+ * Generates a JUnit assertion exception (failure) if any assertions are passed into this method, also concatenating
+ * all of the error messages in the assertions together to form an error message to diagnose the test failure with.
+ *
+ * @param asserts The list of failed assertions.
+ */
+ protected void assertNoFailures(List<Assertion> asserts)
+ {
+ log.debug("protected void assertNoFailures(List<Assertion> asserts = " + asserts + "): called");
+
+ // Check if there are no assertion failures, and return without doing anything if so.
+ if ((asserts == null) || asserts.isEmpty())
+ {
+ return;
+ }
+
+ // Compile all of the assertion failure messages together.
+ String errorMessage = assertionsToString(asserts);
+
+ // Fail with the error message from all of the assertions.
+ fail(errorMessage);
+ }
+
+ /**
+ * Converts a list of failed assertions into an error message.
+ *
+ * @param asserts The failed assertions.
+ *
+ * @return The error message.
+ */
+ protected String assertionsToString(List<Assertion> asserts)
+ {
+ String errorMessage = "";
+
+ for (Assertion assertion : asserts)
+ {
+ errorMessage += assertion.toString() + "\n";
+ }
+
+ return errorMessage;
+ }
+
+ /**
+ * Ensures that the in-vm broker is created and initialized.
+ *
+ * @throws Exception Any exceptions allowed to fall through and fail the test.
+ */
+ protected void setUp() throws Exception
+ {
+ NDC.push(getName());
+
+ // Ensure that the in-vm broker is created.
+ TransportConnection.createVMBroker(1);
+ }
+
+ /**
+ * Ensures that the in-vm broker is cleaned up after each test run.
+ */
+ protected void tearDown()
+ {
+ try
+ {
+ // Ensure that the in-vm broker is cleaned up so that the next test starts afresh.
+ TransportConnection.killVMBroker(1);
+ ApplicationRegistry.remove(1);
+ }
+ finally
+ {
+ NDC.pop();
+ }
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as known to the test
+ * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test
+ * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case
+ * name "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ *
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return methodName;
+ }
+
+ /**
+ * DefaultCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted
+ * on the same JVM.
+ */
+ public class DefaultCircuitFactory implements CircuitFactory
+ {
+ /**
+ * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles,
+ * begining the test and gathering the test reports from the participants.
+ *
+ * @param testCircuit The test circuit.
+ * @param assertions The list of assertions to apply to the test circuit.
+ * @param testProperties The test case definition.
+ */
+ public void sequenceTest(Circuit testCircuit, List<Assertion> assertions, Properties testProperties)
+ {
+ assertNoFailures(testCircuit.test(1, assertions));
+ }
+
+ /**
+ * Creates a test circuit for the test, configered by the test parameters specified.
+ *
+ * @param testProperties The test parameters.
+ * @return A test circuit.
+ */
+ public Circuit createCircuit(ParsedProperties testProperties)
+ {
+ return LocalCircuitImpl.createCircuit(testProperties);
+ }
+
+ /**
+ * Sets the sender test client to coordinate the test with.
+ *
+ * @param sender The contact details of the sending client in the test.
+ */
+ public void setSender(TestClientDetails sender)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Sets the receiving test client to coordinate the test with.
+ *
+ * @param receiver The contact details of the sending client in the test.
+ */
+ public void setReceiver(TestClientDetails receiver)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Supplies the sending test client.
+ *
+ * @return The sending test client.
+ */
+ public TestClientDetails getSender()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Supplies the receiving test client.
+ *
+ * @return The receiving test client.
+ */
+ public List<TestClientDetails> getReceivers()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Accepts the conversation factory over which to hold the test coordinating conversation.
+ *
+ * @param conversationFactory The conversation factory to coordinate the test over.
+ */
+ public void setConversationFactory(ConversationFactory conversationFactory)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java
new file mode 100644
index 0000000000..2456ba1709
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java
@@ -0,0 +1,11 @@
+package org.apache.qpid.test.framework;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+public class FrameworkClientBaseCase
+{
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessageMonitor.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessageMonitor.java
new file mode 100644
index 0000000000..a2dba45616
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessageMonitor.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+import org.apache.log4j.Logger;
+
+import javax.jms.Message;
+import javax.jms.MessageListener;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * MessageMonitor is used to record information about messages received. This will provide methods to check various
+ * properties, such as the type, number and content of messages received in order to verify the correct behaviour of
+ * tests.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Count incoming messages.
+ * <tr><td> Record time ellapsed since the arrival of the first message.
+ * <tr><td> Reset all counts and timings.
+ * </table>
+ */
+public class MessageMonitor implements MessageListener
+{
+ /** Used for debugging. */
+ private final Logger log = Logger.getLogger(MessageMonitor.class);
+
+ /** Holds the count of messages received since the last query. */
+ protected AtomicInteger numMessages = new AtomicInteger();
+
+ /** Holds the time of arrival of the first message. */
+ protected Long firstMessageTime = null;
+
+ /**
+ * Handles received messages. Does Nothing.
+ *
+ * @param message The message. Ignored.
+ */
+ public void onMessage(Message message)
+ {
+ // log.debug("public void onMessage(Message message): called");
+
+ numMessages.getAndIncrement();
+ }
+
+ /**
+ * Gets the count of messages.
+ *
+ * @return The count of messages.
+ */
+ public int getNumMessage()
+ {
+ if (firstMessageTime == null)
+ {
+ firstMessageTime = System.nanoTime();
+ }
+
+ return numMessages.get();
+ }
+
+ /**
+ * Gets the time elapsed since the first message arrived, in nanos, or zero if no messages have arrived yet.
+ *
+ * @return The time elapsed since the first message arrived, in nanos, or zero if no messages have arrived yet.
+ */
+ public long getTime()
+ {
+ if (firstMessageTime != null)
+ {
+ return System.nanoTime() - firstMessageTime;
+ }
+ else
+ {
+ return 0L;
+ }
+ }
+
+ /**
+ * Resets the message count and timer to zero.
+ */
+ public void reset()
+ {
+ numMessages.set(0);
+ firstMessageTime = null;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java
new file mode 100644
index 0000000000..f6664a78d9
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.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.test.framework;
+
+import org.apache.qpid.jms.Session;
+
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+
+import java.util.Properties;
+
+/**
+ * MessagingTestConfigProperties defines a set of property names and default values for specifying a messaging topology,
+ * and test parameters for running a messaging test over that topology. A Properties object holding some of these
+ * properties, superimposed onto the defaults, is used to establish test topologies and control test behaviour.
+ *
+ * <p/>A complete list of the parameters, default values and comments on their usage is provided here:
+ *
+ * <p/><table><caption>Parameters</caption>
+ * <tr><th> Parameter <th> Default <th> Comments
+ * <tr><td> messageSize <td> 0 <td> Message size in bytes. Not including any headers.
+ * <tr><td> destinationName <td> ping <td> The root name to use to generate destination names to ping.
+ * <tr><td> persistent <td> false <td> Determines whether peristent delivery is used.
+ * <tr><td> transacted <td> false <td> Determines whether messages are sent/received in transactions.
+ * <tr><td> broker <td> tcp://localhost:5672 <td> Determines the broker to connect to.
+ * <tr><td> virtualHost <td> test <td> Determines the virtual host to send all ping over.
+ * <tr><td> rate <td> 0 <td> The maximum rate (in hertz) to send messages at. 0 means no limit.
+ * <tr><td> verbose <td> false <td> The verbose flag for debugging. Prints to console on every message.
+ * <tr><td> pubsub <td> false <td> Whether to ping topics or queues. Uses p2p by default.
+ * <tr><td> username <td> guest <td> The username to access the broker with.
+ * <tr><td> password <td> guest <td> The password to access the broker with.
+ * <tr><td> selector <td> null <td> Not used. Defines a message selector to filter pings with.
+ * <tr><td> destinationCount <td> 1 <td> The number of receivers listening to the pings.
+ * <tr><td> timeout <td> 30000 <td> In milliseconds. The timeout to stop waiting for replies.
+ * <tr><td> commitBatchSize <td> 1 <td> The number of messages per transaction in transactional mode.
+ * <tr><td> uniqueDests <td> true <td> Whether each receivers only listens to one ping destination or all.
+ * <tr><td> durableDests <td> false <td> Whether or not durable destinations are used.
+ * <tr><td> ackMode <td> AUTO_ACK <td> The message acknowledgement mode. Possible values are:
+ * 0 - SESSION_TRANSACTED
+ * 1 - AUTO_ACKNOWLEDGE
+ * 2 - CLIENT_ACKNOWLEDGE
+ * 3 - DUPS_OK_ACKNOWLEDGE
+ * 257 - NO_ACKNOWLEDGE
+ * 258 - PRE_ACKNOWLEDGE
+ * <tr><td> maxPending <td> 0 <td> The maximum size in bytes, of messages sent but not yet received.
+ * Limits the volume of messages currently buffered on the client
+ * or broker. Can help scale test clients by limiting amount of buffered
+ * data to avoid out of memory errors.
+ * </table>
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide the names and defaults of all test parameters.
+ * </table>
+ *
+ * @todo Put a type-safe wrapper around these properties, but continue to store the parameters as properties. This is
+ * simply to ensure that it is a simple matter to serialize/deserialize string/string pairs onto messages.
+ */
+public class MessagingTestConfigProperties extends ParsedProperties
+{
+ // ====================== Connection Properties ==================================
+
+ /** Holds the name of the default connection configuration. */
+ public static final String CONNECTION_NAME = "broker";
+
+ /** Holds the name of the property to get the initial context factory name from. */
+ public static final String INITIAL_CONTEXT_FACTORY_PROPNAME = "java.naming.factory.initial";
+
+ /** Defines the class to use as the initial context factory by default. */
+ public static final String INITIAL_CONTEXT_FACTORY_DEFAULT = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory";
+
+ /** Holds the name of the property to get the test broker url from. */
+ public static final String BROKER_PROPNAME = "qpid.test.broker";
+
+ /** Holds the default broker url for the test. */
+ public static final String BROKER_DEFAULT = "vm://:1";
+
+ /** Holds the name of the property to get the test broker virtual path. */
+ public static final String VIRTUAL_HOST_PROPNAME = "virtualHost";
+
+ /** Holds the default virtual path for the test. */
+ public static final String VIRTUAL_HOST_DEFAULT = "";
+
+ /** Holds the name of the property to get the broker access username from. */
+ public static final String USERNAME_PROPNAME = "username";
+
+ /** Holds the default broker log on username. */
+ public static final String USERNAME_DEFAULT = "guest";
+
+ /** Holds the name of the property to get the broker access password from. */
+ public static final String PASSWORD_PROPNAME = "password";
+
+ /** Holds the default broker log on password. */
+ public static final String PASSWORD_DEFAULT = "guest";
+
+ // ====================== Messaging Topology Properties ==========================
+
+ /** Holds the name of the property to get the bind publisher procuder flag from. */
+ public static final String PUBLISHER_PRODUCER_BIND_PROPNAME = "publisherProducerBind";
+
+ /** Holds the default value of the publisher producer flag. */
+ public static final boolean PUBLISHER_PRODUCER_BIND_DEFAULT = true;
+
+ /** Holds the name of the property to get the bind publisher procuder flag from. */
+ public static final String PUBLISHER_CONSUMER_BIND_PROPNAME = "publisherConsumerBind";
+
+ /** Holds the default value of the publisher consumer flag. */
+ public static final boolean PUBLISHER_CONSUMER_BIND_DEFAULT = false;
+
+ /** Holds the name of the property to get the bind receivers procuder flag from. */
+ public static final String RECEIVER_PRODUCER_BIND_PROPNAME = "receiverProducerBind";
+
+ /** Holds the default value of the receivers producer flag. */
+ public static final boolean RECEIVER_PRODUCER_BIND_DEFAULT = false;
+
+ /** Holds the name of the property to get the bind receivers procuder flag from. */
+ public static final String RECEIVER_CONSUMER_BIND_PROPNAME = "receiverConsumerBind";
+
+ /** Holds the default value of the receivers consumer flag. */
+ public static final boolean RECEIVER_CONSUMER_BIND_DEFAULT = true;
+
+ /** Holds the name of the property to get the publishers consumer active flag from. */
+ public static final String PUBLISHER_CONSUMER_ACTIVE_PROPNAME = "publisherConsumerActive";
+
+ /** Holds the default value of the publishers consumer active flag. */
+ public static final boolean PUBLISHER_CONSUMER_ACTIVE_DEFAULT = true;
+
+ /** Holds the name of the property to get the receivers consumer active flag from. */
+ public static final String RECEIVER_CONSUMER_ACTIVE_PROPNAME = "receiverConsumerActive";
+
+ /** Holds the default value of the receivers consumer active flag. */
+ public static final boolean RECEIVER_CONSUMER_ACTIVE_DEFAULT = true;
+
+ /** Holds the name of the property to get the destination name root from. */
+ public static final String SEND_DESTINATION_NAME_ROOT_PROPNAME = "sendDestinationRoot";
+
+ /** Holds the root of the name of the default destination to send to. */
+ public static final String SEND_DESTINATION_NAME_ROOT_DEFAULT = "sendTo";
+
+ /** Holds the name of the property to get the destination name root from. */
+ public static final String RECEIVE_DESTINATION_NAME_ROOT_PROPNAME = "receiveDestinationRoot";
+
+ /** Holds the root of the name of the default destination to send to. */
+ public static final String RECEIVE_DESTINATION_NAME_ROOT_DEFAULT = "receiveFrom";
+
+ /** Holds the name of the proeprty to get the destination count from. */
+ public static final String DESTINATION_COUNT_PROPNAME = "destinationCount";
+
+ /** Defines the default number of destinations to ping. */
+ public static final int DESTINATION_COUNT_DEFAULT = 1;
+
+ /** Holds the name of the property to get the p2p or pub/sub messaging mode from. */
+ public static final String PUBSUB_PROPNAME = "pubsub";
+
+ /** Holds the pub/sub mode default, true means ping a topic, false means ping a queue. */
+ public static final boolean PUBSUB_DEFAULT = false;
+
+ // ====================== JMS Options and Flags =================================
+
+ /** Holds the name of the property to get the test delivery mode from. */
+ public static final String PERSISTENT_MODE_PROPNAME = "persistent";
+
+ /** Holds the message delivery mode to use for the test. */
+ public static final boolean PERSISTENT_MODE_DEFAULT = false;
+
+ /** Holds the name of the property to get the test transactional mode from. */
+ public static final String TRANSACTED_PROPNAME = "transacted";
+
+ /** Holds the transactional mode to use for the test. */
+ public static final boolean TRANSACTED_DEFAULT = false;
+
+ /** Holds the name of the property to set the no local flag from. */
+ public static final String NO_LOCAL_PROPNAME = "noLocal";
+
+ /** Defines the default value of the no local flag to use when consuming messages. */
+ public static final boolean NO_LOCAL_DEFAULT = false;
+
+ /** Holds the name of the property to get the message acknowledgement mode from. */
+ public static final String ACK_MODE_PROPNAME = "ackMode";
+
+ /** Defines the default message acknowledgement mode. */
+ public static final int ACK_MODE_DEFAULT = Session.AUTO_ACKNOWLEDGE;
+
+ /** Holds the name of the property to get the durable subscriptions flag from, when doing pub/sub messaging. */
+ public static final String DURABLE_SUBSCRIPTION_PROPNAME = "durableSubscription";
+
+ /** Defines the default value of the durable subscriptions flag. */
+ public static final boolean DURABLE_SUBSCRIPTION_DEFAULT = false;
+
+ // ====================== Qpid Options and Flags ================================
+
+ /** Holds the name of the property to set the exclusive flag from. */
+ public static final String EXCLUSIVE_PROPNAME = "exclusive";
+
+ /** Defines the default value of the exclusive flag to use when consuming messages. */
+ public static final boolean EXCLUSIVE_DEFAULT = false;
+
+ /** Holds the name of the property to set the immediate flag from. */
+ public static final String IMMEDIATE_PROPNAME = "immediate";
+
+ /** Defines the default value of the immediate flag to use when sending messages. */
+ public static final boolean IMMEDIATE_DEFAULT = false;
+
+ /** Holds the name of the property to set the mandatory flag from. */
+ public static final String MANDATORY_PROPNAME = "mandatory";
+
+ /** Defines the default value of the mandatory flag to use when sending messages. */
+ public static final boolean MANDATORY_DEFAULT = false;
+
+ /** Holds the name of the property to get the durable destinations flag from. */
+ public static final String DURABLE_DESTS_PROPNAME = "durableDests";
+
+ /** Default value for the durable destinations flag. */
+ public static final boolean DURABLE_DESTS_DEFAULT = false;
+
+ /** Holds the name of the proeprty to set the prefetch size from. */
+ public static final String PREFETCH_PROPNAME = "prefetch";
+
+ /** Defines the default prefetch size to use when consuming messages. */
+ public static final int PREFETCH_DEFAULT = 100;
+
+ // ====================== Common Test Parameters ================================
+
+ /** Holds the name of the property to get the test message size from. */
+ public static final String MESSAGE_SIZE_PROPNAME = "messageSize";
+
+ /** Used to set up a default message size. */
+ public static final int MESSAGE_SIZE_DEAFULT = 0;
+
+ /** Holds the name of the property to get the message rate from. */
+ public static final String RATE_PROPNAME = "rate";
+
+ /** Defines the default rate (in pings per second) to send pings at. 0 means as fast as possible, no restriction. */
+ public static final int RATE_DEFAULT = 0;
+
+ /** Holds the name of the proeprty to get the. */
+ public static final String SELECTOR_PROPNAME = "selector";
+
+ /** Holds the default message selector. */
+ public static final String SELECTOR_DEFAULT = "";
+
+ /** Holds the name of the property to get the waiting timeout for response messages. */
+ public static final String TIMEOUT_PROPNAME = "timeout";
+
+ /** Default time to wait before assuming that a ping has timed out. */
+ public static final long TIMEOUT_DEFAULT = 30000;
+
+ /** Holds the name of the property to get the commit batch size from. */
+ public static final String TX_BATCH_SIZE_PROPNAME = "commitBatchSize";
+
+ /** Defines the default number of pings to send in each transaction when running transactionally. */
+ public static final int TX_BATCH_SIZE_DEFAULT = 1;
+
+ /** Holds the name of the property to set the maximum amount of pending message data for a producer to hold. */
+ public static final String MAX_PENDING_PROPNAME = "maxPending";
+
+ /** Defines the default maximum quantity of pending message data to allow producers to hold. */
+ public static final int MAX_PENDING_DEFAULT = 0;
+
+ /** Holds the name of the property to get the verbose mode proeprty from. */
+ public static final String VERBOSE_PROPNAME = "verbose";
+
+ /** Holds the default verbose mode. */
+ public static final boolean VERBOSE_DEFAULT = false;
+
+ /** Holds the default configuration properties. */
+ public static ParsedProperties defaults = new ParsedProperties();
+
+ static
+ {
+ defaults.setPropertyIfNull(INITIAL_CONTEXT_FACTORY_PROPNAME, INITIAL_CONTEXT_FACTORY_DEFAULT);
+ // defaults.setPropertyIfNull(CONNECTION_PROPNAME, CONNECTION_DEFAULT);
+ defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT);
+ defaults.setPropertyIfNull(PUBLISHER_PRODUCER_BIND_PROPNAME, PUBLISHER_PRODUCER_BIND_DEFAULT);
+ defaults.setPropertyIfNull(PUBLISHER_CONSUMER_BIND_PROPNAME, PUBLISHER_CONSUMER_BIND_DEFAULT);
+ defaults.setPropertyIfNull(RECEIVER_PRODUCER_BIND_PROPNAME, RECEIVER_PRODUCER_BIND_DEFAULT);
+ defaults.setPropertyIfNull(RECEIVER_CONSUMER_BIND_PROPNAME, RECEIVER_CONSUMER_BIND_DEFAULT);
+ defaults.setPropertyIfNull(PUBLISHER_CONSUMER_ACTIVE_PROPNAME, PUBLISHER_CONSUMER_ACTIVE_DEFAULT);
+ defaults.setPropertyIfNull(RECEIVER_CONSUMER_ACTIVE_PROPNAME, RECEIVER_CONSUMER_ACTIVE_DEFAULT);
+ defaults.setPropertyIfNull(SEND_DESTINATION_NAME_ROOT_PROPNAME, SEND_DESTINATION_NAME_ROOT_DEFAULT);
+ defaults.setPropertyIfNull(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME, RECEIVE_DESTINATION_NAME_ROOT_DEFAULT);
+ defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT);
+ defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT);
+ defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT);
+ defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT);
+ defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT);
+ defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT);
+ defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT);
+ defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT);
+ defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT);
+ defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT);
+ defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT);
+ defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT);
+ defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT);
+ defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT);
+ defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT);
+ defaults.setPropertyIfNull(DURABLE_SUBSCRIPTION_PROPNAME, DURABLE_SUBSCRIPTION_DEFAULT);
+ defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT);
+ defaults.setPropertyIfNull(PREFETCH_PROPNAME, PREFETCH_DEFAULT);
+ defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_DEFAULT);
+ defaults.setPropertyIfNull(EXCLUSIVE_PROPNAME, EXCLUSIVE_DEFAULT);
+ defaults.setPropertyIfNull(IMMEDIATE_PROPNAME, IMMEDIATE_DEFAULT);
+ defaults.setPropertyIfNull(MANDATORY_PROPNAME, MANDATORY_DEFAULT);
+ }
+
+ /**
+ * Creates a test configuration based on the defaults.
+ */
+ public MessagingTestConfigProperties()
+ {
+ super(defaults);
+ }
+
+ /**
+ * Creates a test configuration based on the supplied properties.
+ *
+ * @param properties The test configuration.
+ */
+ public MessagingTestConfigProperties(Properties properties)
+ {
+ super(properties);
+ }
+
+ public int getMessageSize()
+ {
+ return getPropertyAsInteger(MESSAGE_SIZE_PROPNAME);
+ }
+
+ public boolean getPublisherProducerBind()
+ {
+ return getPropertyAsBoolean(PUBLISHER_PRODUCER_BIND_PROPNAME);
+ }
+
+ public boolean getPublisherConsumerBind()
+ {
+ return getPropertyAsBoolean(PUBLISHER_CONSUMER_BIND_PROPNAME);
+ }
+
+ public boolean getReceiverProducerBind()
+ {
+ return getPropertyAsBoolean(RECEIVER_PRODUCER_BIND_PROPNAME);
+ }
+
+ public boolean getReceiverConsumerBind()
+ {
+ return getPropertyAsBoolean(RECEIVER_CONSUMER_BIND_PROPNAME);
+ }
+
+ public boolean getPublisherConsumerActive()
+ {
+ return getPropertyAsBoolean(PUBLISHER_CONSUMER_ACTIVE_PROPNAME);
+ }
+
+ public boolean getReceiverConsumerActive()
+ {
+ return getPropertyAsBoolean(RECEIVER_CONSUMER_ACTIVE_PROPNAME);
+ }
+
+ public String getSendDestinationNameRoot()
+ {
+ return getProperty(SEND_DESTINATION_NAME_ROOT_PROPNAME);
+ }
+
+ public String getReceiveDestinationNameRoot()
+ {
+ return getProperty(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME);
+ }
+
+ public boolean getPersistentMode()
+ {
+ return getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME);
+ }
+
+ public boolean getTransacted()
+ {
+ return getPropertyAsBoolean(TRANSACTED_PROPNAME);
+ }
+
+ public String getBroker()
+ {
+ return getProperty(BROKER_PROPNAME);
+ }
+
+ public String getVirtualHost()
+ {
+ return getProperty(VIRTUAL_HOST_PROPNAME);
+ }
+
+ public String getRate()
+ {
+ return getProperty(RATE_PROPNAME);
+ }
+
+ public boolean getPubsub()
+ {
+ return getPropertyAsBoolean(PUBSUB_PROPNAME);
+ }
+
+ public String getUsername()
+ {
+ return getProperty(USERNAME_PROPNAME);
+ }
+
+ public String getPassword()
+ {
+ return getProperty(PASSWORD_PROPNAME);
+ }
+
+ public int getDestinationCount()
+ {
+ return getPropertyAsInteger(DESTINATION_COUNT_PROPNAME);
+ }
+
+ public long getTimeout()
+ {
+ return getPropertyAsLong(TIMEOUT_PROPNAME);
+ }
+
+ public int getTxBatchSize()
+ {
+ return getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME);
+ }
+
+ public boolean getDurableDests()
+ {
+ return getPropertyAsBoolean(DURABLE_DESTS_PROPNAME);
+ }
+
+ public int getAckMode()
+ {
+ return getPropertyAsInteger(ACK_MODE_PROPNAME);
+ }
+
+ public boolean getDurableSubscription()
+ {
+ return getPropertyAsBoolean(DURABLE_SUBSCRIPTION_PROPNAME);
+ }
+
+ public int getMaxPending()
+ {
+ return getPropertyAsInteger(MAX_PENDING_PROPNAME);
+ }
+
+ public int getPrefecth()
+ {
+ return getPropertyAsInteger(PREFETCH_PROPNAME);
+ }
+
+ public boolean getNoLocal()
+ {
+ return getPropertyAsBoolean(NO_LOCAL_PROPNAME);
+ }
+
+ public boolean getExclusive()
+ {
+ return getPropertyAsBoolean(EXCLUSIVE_PROPNAME);
+ }
+
+ public boolean getImmediate()
+ {
+ return getPropertyAsBoolean(IMMEDIATE_PROPNAME);
+ }
+
+ public boolean getMandatory()
+ {
+ return getPropertyAsBoolean(MANDATORY_PROPNAME);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java
new file mode 100644
index 0000000000..df35579533
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+/**
+ * A Publisher is a {@link CircuitEnd} that represents the status of the publishing side of a test circuit. Its main
+ * purpose is to provide assertions that can be applied to test the behaviour of the publishers.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide assertion that the publishers received no exceptions.
+ * <tr><td> Provide assertion that the publishers received a no consumers error code on every message.
+ * <tr><td> Provide assertion that the publishers received a no route error code on every message.
+ * </table>
+ */
+public interface Publisher
+{
+ /**
+ * Provides an assertion that the publisher encountered no exceptions.
+ *
+ * @return An assertion that the publisher encountered no exceptions.
+ */
+ public Assertion noExceptionsAssertion();
+
+ /**
+ * Provides an assertion that the publisher got a no consumers exception on every message.
+ *
+ * @return An assertion that the publisher got a no consumers exception on every message.
+ */
+ public Assertion noConsumersAssertion();
+
+ /**
+ * Provides an assertion that the publisher got a no rout exception on every message.
+ *
+ * @return An assertion that the publisher got a no rout exception on every message.
+ */
+ public Assertion noRouteAssertion();
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java
new file mode 100644
index 0000000000..e01b272d5a
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+/**
+ * A Receiver is a {@link CircuitEnd} that represents the status of the receiving side of a test circuit. Its main
+ * purpose is to provide assertions that can be applied to check the behaviour of the receivers.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide assertion that the receivers received no exceptions.
+ * <tr><td> Provide assertion that the receivers received all test messages sent to it.
+ * </table>
+ */
+public interface Receiver
+{
+ /**
+ * Provides an assertion that the receivers encountered no exceptions.
+ *
+ * @return An assertion that the receivers encountered no exceptions.
+ */
+ public Assertion noExceptionsAssertion();
+
+ /**
+ * Provides an assertion that the receivers got all messages that were sent to it.
+ *
+ * @return An assertion that the receivers got all messages that were sent to it.
+ */
+ public Assertion allMessagesAssertion();
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestClientDetails.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestClientDetails.java
new file mode 100644
index 0000000000..1e5807cba8
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestClientDetails.java
@@ -0,0 +1,86 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+/**
+ * TestClientDetails is used to encapsulate information about an interop test client. It pairs together the unique
+ * name of the client, and the route on which it listens to its control messages.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Record test clients control addresses together with their names.
+ * </table>
+ */
+public class TestClientDetails
+{
+ /** The test clients name. */
+ public String clientName;
+
+ /* The test clients unique sequence number. Not currently used. */
+
+ /** The routing key of the test clients control topic. */
+ public String privateControlKey;
+
+ /**
+ * Two TestClientDetails are considered to be equal, iff they have the same client name.
+ *
+ * @param o The object to compare to.
+ *
+ * @return <tt>If the object to compare to is a TestClientDetails equal to this one, <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ if (!(o instanceof TestClientDetails))
+ {
+ return false;
+ }
+
+ final TestClientDetails testClientDetails = (TestClientDetails) o;
+
+ return !((clientName != null) ? (!clientName.equals(testClientDetails.clientName))
+ : (testClientDetails.clientName != null));
+ }
+
+ /**
+ * Computes a hash code compatible with the equals method; based on the client name alone.
+ *
+ * @return A hash code for this.
+ */
+ public int hashCode()
+ {
+ return ((clientName != null) ? clientName.hashCode() : 0);
+ }
+
+ /**
+ * Outputs the client name and address details. Mostly used for debugging purposes.
+ *
+ * @return The client name and address.
+ */
+ public String toString()
+ {
+ return "TestClientDetails: [ clientName = " + clientName + ", privateControlKey = " + privateControlKey + " ]";
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java
new file mode 100644
index 0000000000..569eac425c
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java
@@ -0,0 +1,189 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework;
+
+import org.apache.log4j.Logger;
+
+import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*;
+
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+
+import javax.jms.*;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * TestUtils provides static helper methods that are usefull for writing tests against QPid.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Create connections from test properties. <td> {@link MessagingTestConfigProperties}
+ * <tr><td> Inject a short pause in a test.
+ * </table>
+ */
+public class TestUtils
+{
+ /** Used for debugging. */
+ private static Logger log = Logger.getLogger(TestUtils.class);
+
+ private static byte[] MESSAGE_DATA_BYTES =
+ "Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- "
+ .getBytes();
+
+ /**
+ * Establishes a JMS connection using a set of properties and qpids built in JNDI implementation. This is a simple
+ * convenience method for code that does not anticipate handling connection failures. All exceptions that indicate
+ * that the connection has failed, are wrapped as rutime exceptions, presumably handled by a top level failure
+ * handler.
+ *
+ * <p/>This utility makes use of the following test parameters from {@link MessagingTestConfigProperties} to control
+ * the connection creation:
+ *
+ * <p/><table>
+ * <tr><td> {@link MessagingTestConfigProperties#USERNAME_PROPNAME} <td> The username.
+ * <tr><td> {@link MessagingTestConfigProperties#PASSWORD_PROPNAME} <td> The password.
+ * <tr><td> {@link MessagingTestConfigProperties#VIRTUAL_HOST_PROPNAME} <td> The virtual host name.
+ * <tr><td> {@link MessagingTestConfigProperties#BROKER_PROPNAME} <td> The broker URL.
+ * <tr><td> {@link MessagingTestConfigProperties#CONNECTION_NAME} <td> The broker name in the initial context.
+ *
+ * @param messagingProps Connection properties as defined in {@link MessagingTestConfigProperties}.
+ *
+ * @return A JMS conneciton.
+ */
+ public static Connection createConnection(ParsedProperties messagingProps)
+ {
+ log.debug("public static Connection createConnection(ParsedProperties messagingProps = " + messagingProps
+ + "): called");
+
+ try
+ {
+ // Extract the configured connection properties from the test configuration.
+ String conUsername = messagingProps.getProperty(USERNAME_PROPNAME);
+ String conPassword = messagingProps.getProperty(PASSWORD_PROPNAME);
+ String virtualHost = messagingProps.getProperty(VIRTUAL_HOST_PROPNAME);
+ String brokerUrl = messagingProps.getProperty(BROKER_PROPNAME);
+
+ // Create the broker connection url.
+ String connectionString =
+ "amqp://" + conUsername + ":" + conPassword + "@clientid/" + ((virtualHost != null) ? virtualHost : "")
+ + "?brokerlist='" + brokerUrl + "'";
+
+ // Create properties to create the initial context from, and inject the connection factory configuration
+ // for the defined connection name into it.
+ messagingProps.setProperty("connectionfactory." + CONNECTION_NAME, connectionString);
+
+ Context ctx = new InitialContext(messagingProps);
+
+ ConnectionFactory cf = (ConnectionFactory) ctx.lookup(CONNECTION_NAME);
+
+ return cf.createConnection();
+ }
+ catch (NamingException e)
+ {
+ throw new RuntimeException("Got JNDI NamingException whilst looking up the connection factory.", e);
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Could not establish connection due to JMSException.", e);
+ }
+ }
+
+ /**
+ * Creates a test message of the specified size, on the given JMS session.
+ *
+ * @param session The JMS session.
+ * @param size The size of the message in bytes.
+ *
+ * @return A bytes message, of the specified size, filled with dummy data.
+ *
+ *
+ */
+ public static Message createTestMessageOfSize(Session session, int size) throws JMSException
+ {
+ BytesMessage message = session.createBytesMessage();
+
+ if (size > 0)
+ {
+ int div = MESSAGE_DATA_BYTES.length / size;
+ int mod = MESSAGE_DATA_BYTES.length % size;
+
+ for (int i = 0; i < div; i++)
+ {
+ message.writeBytes(MESSAGE_DATA_BYTES);
+ }
+
+ if (mod != 0)
+ {
+ message.writeBytes(MESSAGE_DATA_BYTES, 0, mod);
+ }
+ }
+
+ return message;
+ }
+
+ /**
+ * Pauses for the specified length of time. In the event of failing to pause for at least that length of time
+ * due to interuption of the thread, a RutimeException is raised to indicate the failure. The interupted status
+ * of the thread is restores in that case. This method should only be used when it is expected that the pause
+ * will be succesfull, for example in test code that relies on inejecting a pause.
+ *
+ * @param t The minimum time to pause for in milliseconds.
+ */
+ public static void pause(long t)
+ {
+ try
+ {
+ Thread.sleep(t);
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the interrupted status
+ Thread.currentThread().interrupt();
+
+ throw new RuntimeException("Failed to generate the requested pause length.", e);
+ }
+ }
+
+ /**
+ * Sets properties of different types on a JMS Message.
+ *
+ * @param message The message to set properties on.
+ * @param properties The property name/value pairs to set.
+ *
+ * @throws javax.jms.JMSException All underlying JMSExceptions are allowed to fall through.
+ *
+ * @todo Move this helper method somewhere else. For example, TestUtils.
+ */
+ public static void setPropertiesOnMessage(Message message, Map<Object, Object> properties) throws JMSException
+ {
+ for (Map.Entry<Object, Object> entry : properties.entrySet())
+ {
+ String name = entry.getKey().toString();
+ Object value = entry.getValue();
+
+ message.setObjectProperty(name, value);
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java
new file mode 100644
index 0000000000..b04ab0f9b0
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.clocksynch;
+
+/**
+ * ClockSynchFailureException represents failure of a {@link ClockSynchronizer} to achieve synchronization. This could
+ * be because a reference signal is not available, or because a desired accurracy cannot be attained, for example.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to achieve synchronization.
+ * </table>
+ */
+public class ClockSynchFailureException extends Exception
+{
+ /**
+ * Creates a clock synch failure exception.
+ *
+ * @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method).
+ * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). (A <tt>null</tt>
+ * value is permitted, and indicates that the cause is nonexistent or unknown.)
+ */
+ public ClockSynchFailureException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java
new file mode 100644
index 0000000000..3a2655340e
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java
@@ -0,0 +1,123 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.clocksynch;
+
+import org.apache.log4j.Logger;
+
+import uk.co.thebadgerset.junit.extensions.ShutdownHookable;
+import uk.co.thebadgerset.junit.extensions.Throttle;
+
+/**
+ * ClockSynchThread is a convenient utility for running a thread that periodically synchronizes the clock against
+ * a reference. Supply it with a {@link ClockSynchronizer} and a {@link Throttle} and it will continually keep the
+ * clock up-to-date at a rate determined by the throttle.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Continually sychronize the clock at a throttled rate.
+ * </table>
+ */
+public class ClockSynchThread extends Thread implements ShutdownHookable
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(ClockSynchThread.class);
+
+ /** Holds the clock syncher for the synch thread. */
+ private ClockSynchronizer clockSyncher;
+
+ /** Holds the throttle to limit the synch rate. */
+ private Throttle throttle;
+
+ /** Flag to indicate that the periodic clock syncher should keep running. */
+ boolean doSynch = true;
+
+ /**
+ * Creates a clock synchronizer thread from a clock synchronizer and a throttle.
+ *
+ * @param syncher The clock synchronizer.
+ * @param throttle The throttle.
+ */
+ public ClockSynchThread(ClockSynchronizer syncher, Throttle throttle)
+ {
+ this.clockSyncher = syncher;
+ this.throttle = throttle;
+ }
+
+ /**
+ * Terminates the synchronization thread.
+ */
+ public void terminate()
+ {
+ doSynch = false;
+ }
+
+ /**
+ * Continually updates the clock, until {@link #terminate()} is called.
+ */
+ public void run()
+ {
+ while (doSynch)
+ {
+ // Perform a clock clockSynch.
+ try
+ {
+ // Wait controlled by the throttle before doing the next synch.
+ throttle.throttle();
+
+ clockSyncher.synch();
+ log.debug("Clock synched, delta = " + clockSyncher.getDelta() + ", epsilon = " + clockSyncher.getEpsilon()
+ + ".");
+ }
+ // Terminate the synch thread if the synchronization cannot be achieved.
+ catch (ClockSynchFailureException e)
+ {
+ log.debug("Cannot synchronize the clock (reference service may be down). Terminating the synch thread.");
+ doSynch = false;
+ }
+ }
+ }
+
+ /**
+ * Gets the clock synchronizer that is kept continually up to date.
+ *
+ * @return The clock synchronizer that is kept continually up to date.
+ */
+ public ClockSynchronizer getClockSyncher()
+ {
+ return clockSyncher;
+ }
+
+ /**
+ * Supplies a shutdown hook, that terminates the synching thread.
+ *
+ * @return The shut down hook.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ doSynch = false;
+ }
+ });
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java
new file mode 100644
index 0000000000..d0fd16d715
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java
@@ -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.
+ *
+ */
+package org.apache.qpid.test.framework.clocksynch;
+
+/**
+ * ClockSynchronizer provides an interface through which two nodes may synchronize their clocks. It is expected that one
+ * node will act as the reference clock, to which no delta need be applied, and the other node will act as the slave,
+ * and which must apply a delta to its local clock to get a clock synchronized with the reference.
+ *
+ * <p/>The slave side will initiate the computation of a clock delta by calling the {@link #synch} method. This method
+ * will not return until the delta has been computed, at which point there is a method to return its value, as well as
+ * an estimate of the likely error (usually one standard deviation), in the synchronization. For convenience there is a
+ * {@link #nanoTime} method to return the value of System.nanoTime() with the delta added in.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Trigger a clock synchronziation.
+ * <tr><td> Compute a clock delta to apply to the local clock.
+ * <tr><td> Estimate the error in the synchronzation.
+ * </table>
+ */
+public interface ClockSynchronizer
+{
+ /**
+ * The slave side should call this to copute a clock delta with the reference.
+ *
+ * @throws ClockSynchFailureException If synchronization cannot be achieved.
+ */
+ public void synch() throws ClockSynchFailureException;
+
+ /**
+ * Gets the clock delta in nano seconds.
+ *
+ * @return The clock delta in nano seconds.
+ */
+ public long getDelta();
+
+ /**
+ * Gets an estimate of the clock error in nan seconds.
+ *
+ * @return An estimate of the clock error in nan seconds.
+ */
+ public long getEpsilon();
+
+ /**
+ * Gets the local clock time with any computed delta added in.
+ *
+ * @return The local clock time with any computed delta added in.
+ */
+ public long nanoTime();
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/LocalClockSynchronizer.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/LocalClockSynchronizer.java
new file mode 100644
index 0000000000..4f34e1c047
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/LocalClockSynchronizer.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.test.framework.clocksynch;
+
+/**
+ * LocalClockSynchronizer is a fake {@link ClockSynchronizer} that simply calls System.nanoTime(). It exists so that
+ * the same tests can be run distributed or locally, taking timings against the ClockSynchronizer interface without
+ * being aware of how they are being run.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the local clock with no delta.
+ * </table>
+ */
+public class LocalClockSynchronizer implements ClockSynchronizer
+{
+ /**
+ * The slave side should call this to copute a clock delta with the reference.
+ *
+ * @throws org.apache.qpid.test.framework.clocksynch.ClockSynchFailureException
+ * If synchronization cannot be achieved.
+ */
+ public void synch() throws ClockSynchFailureException
+ { }
+
+ /**
+ * Gets the clock delta in nano seconds.
+ *
+ * @return The clock delta in nano seconds.
+ */
+ public long getDelta()
+ {
+ return 0L;
+ }
+
+ /**
+ * Gets an estimate of the clock error in nan seconds.
+ *
+ * @return An estimate of the clock error in nan seconds.
+ */
+ public long getEpsilon()
+ {
+ return 0L;
+ }
+
+ /**
+ * Gets the local clock time with any computed delta added in.
+ *
+ * @return The local clock time with any computed delta added in.
+ */
+ public long nanoTime()
+ {
+ return System.nanoTime();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java
new file mode 100644
index 0000000000..f6195aa553
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java
@@ -0,0 +1,166 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.clocksynch;
+
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+
+import uk.co.thebadgerset.junit.extensions.ShutdownHookable;
+
+/**
+ * UDPClockReference supplies a refernce clock signal (generated from System.nanoTime()).
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply a reference clock signal.
+ * </table>
+ *
+ * @todo Port hard coded. Make configurable.
+ *
+ * @todo Errors rethrown as runtimes, or silently terminate the service. Could add better error handling if needed.
+ */
+public class UDPClockReference implements Runnable, ShutdownHookable
+{
+ /** Used for debugging. */
+ // private static final Logger log = Logger.getLogger(UDPClockReference.class);
+
+ /** Defines the timeout to use when polling the socket for time requests. */
+ private static final int TIMEOUT = 200;
+
+ /** Defines the port to run the clock reference on. */
+ public static final int REFERENCE_PORT = 4445;
+
+ /** Holds the socket to receive clock reference requests on. */
+ protected DatagramSocket socket = null;
+
+ /** Flag used to indicate that the time server should keep running. Set to false to terminate. */
+ protected boolean publish = true;
+
+ /**
+ * Creates a clock reference service on the standard port.
+ */
+ public UDPClockReference()
+ {
+ try
+ {
+ socket = new DatagramSocket(REFERENCE_PORT);
+ socket.setSoTimeout(TIMEOUT);
+ }
+ catch (SocketException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Implements the run loop for this reference time server. This waits for incoming time requests, and replies to
+ * any, with a message with the local time stamp in it. Periodically (controlled by {@link #TIMEOUT}), the run
+ * loop will check if the {@link #publish} flag has been cleared, and terminate the reference time service if so.
+ */
+ public void run()
+ {
+ byte[] buf = new byte[256];
+ ByteBuffer bbuf = ByteBuffer.wrap(buf);
+
+ while (publish)
+ {
+ try
+ {
+ // Wait for a reference time request.
+ DatagramPacket packet = new DatagramPacket(buf, buf.length);
+ boolean timedOut = false;
+
+ try
+ {
+ socket.receive(packet);
+ }
+ catch (SocketTimeoutException e)
+ {
+ timedOut = true;
+ }
+
+ if (!timedOut)
+ {
+ // Work out from the received packet, where to reply to.
+ InetAddress address = packet.getAddress();
+ int port = packet.getPort();
+
+ // Respond to the time request by sending back the local clock as the reference time.
+ bbuf.putLong(System.nanoTime());
+ bbuf.flip();
+ packet = new DatagramPacket(bbuf.array(), bbuf.capacity(), address, port);
+
+ socket.send(packet);
+ }
+ }
+ catch (IOException e)
+ {
+ publish = false;
+ }
+ }
+
+ socket.close();
+ }
+
+ /**
+ * Supplies a shutdown hook.
+ *
+ * @return The shut down hook.
+ */
+ public Thread getShutdownHook()
+ {
+ return new Thread(new Runnable()
+ {
+ public void run()
+ {
+ publish = false;
+ }
+ });
+ }
+
+ /**
+ * For testing purposes. Runs a reference clock on the default port.
+ *
+ * @param args None.
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ // Create the clock reference service.
+ UDPClockReference clock = new UDPClockReference();
+
+ // Set up a shutdown hook for it.
+ Runtime.getRuntime().addShutdownHook(clock.getShutdownHook());
+
+ // Start the service.
+ clock.run();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java
new file mode 100644
index 0000000000..78f05767d3
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java
@@ -0,0 +1,464 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.clocksynch;
+
+import org.apache.log4j.Logger;
+
+import uk.co.thebadgerset.junit.extensions.util.CommandLineParser;
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * UDPClockSynchronizer is a {@link ClockSynchronizer} that sends pings as UDP datagrams, and uses the following simple
+ * algorithm to perform clock synchronization:
+ *
+ * <ol>
+ * <li>Slave initiates synchronization with a Reference clock.</li>
+ * <li>Slave stamps current local time on a "time request" message and sends to the Reference.</li>
+ * <li>Upon receipt by Reference, Reference stamps Reference-time and returns.</li>
+ * <li>Upon receipt by Slave, Slave subtracts current time from sent time and divides by two to compute latency. It
+ * subtracts current time from Reference time to determine Slave-Reference time delta and adds in the
+ * half-latency to get the correct clock delta.</li>
+ * <li>The first result is immediately used to update the clock since it will get the local clock into at least
+ * the right ballpark.</li>
+ * <li>The Slave repeats steps 2 through 4, 15 more times.</li>
+ * <li>The results of the packet receipts are accumulated and sorted in lowest-latency to highest-latency order. The
+ * median latency is determined by picking the mid-point sample from this ordered list.</li>
+ * <li>All samples outside 1 standard-deviation from the median are discarded and the remaining samples
+ * are averaged using an arithmetic mean.</li>
+ * </ol>
+ *
+ * <p/>The use of UDP datagrams, instead of TCP based communication eliminates the hidden delays that TCP can introduce,
+ * as it can transparently re-order or re-send packets, or introduce delays as packets are naggled.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Trigger a clock synchronziation.
+ * <tr><td> Compute a clock delta to apply to the local clock.
+ * <tr><td> Estimate the error in the synchronzation.
+ * </table>
+ */
+public class UDPClockSynchronizer implements ClockSynchronizer
+{
+ /** Used for debugging. */
+ // private static final Logger log = Logger.getLogger(UDPClockSynchronizer.class);
+
+ /** Defines the timeout to use when waiting for responses to time requests. */
+ private static final int TIMEOUT = 50;
+
+ /** The clock delta. */
+ private long delta = 0L;
+
+ /** Holds an estimate of the clock error relative to the reference clock. */
+ private long epsilon = 0L;
+
+ /** Holds the address of the reference clock. */
+ private InetAddress referenceAddress;
+
+ /** Holds the socket to communicate with the reference service over. */
+ private DatagramSocket socket;
+
+ /** Used to control the shutdown in the main test loop. */
+ private static boolean doSynch = true;
+
+ /**
+ * Creates a clock synchronizer against the specified address for the reference.
+ *
+ * @param address The address of the reference service.
+ */
+ public UDPClockSynchronizer(String address)
+ {
+ try
+ {
+ referenceAddress = InetAddress.getByName(address);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The slave side should call this to compute a clock delta with the reference.
+ *
+ * @throws ClockSynchFailureException If synchronization cannot be achieved, due to unavailability of the reference
+ * time service.
+ */
+ public void synch() throws ClockSynchFailureException
+ {
+ try
+ {
+ socket = new DatagramSocket();
+ socket.setSoTimeout(TIMEOUT);
+
+ // Synchronize on a single ping, to get the clock into the right ball-park.
+ synch(1);
+
+ // Synchronize on 15 pings.
+ synch(15);
+
+ // And again, for greater accuracy, on 31.
+ synch(31);
+
+ socket.close();
+ }
+ catch (SocketException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Updates the synchronization delta by performing the specified number of reference clock requests.
+ *
+ * @param n The number of reference clock request cycles to perform.
+ *
+ * @throws ClockSynchFailureException If synchronization cannot be achieved, due to unavailability of the reference
+ * time service.
+ */
+ protected void synch(int n) throws ClockSynchFailureException
+ {
+ // log.debug("protected void synch(int n = " + n + "): called");
+
+ // Create an array of deltas by performing n reference pings.
+ long[] delta = new long[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ delta[i] = ping();
+ }
+
+ // Reject any deltas that are larger than 1 s.d. above the median.
+ long median = median(delta);
+ long sd = standardDeviation(delta);
+
+ // log.debug("median = " + median);
+ // log.debug("sd = " + sd);
+
+ long[] tempDeltas = new long[n];
+ int count = 0;
+
+ for (int i = 0; i < n; i++)
+ {
+ if ((delta[i] <= (median + sd)) && (delta[i] >= (median - sd)))
+ {
+ tempDeltas[count] = delta[i];
+ count++;
+ }
+ else
+ {
+ // log.debug("Rejected: " + delta[i]);
+ }
+ }
+
+ System.arraycopy(tempDeltas, 0, delta, 0, count);
+
+ // Estimate the delta as the mean of the remaining deltas.
+ this.delta += mean(delta);
+
+ // Estimate the error as the standard deviation of the remaining deltas.
+ this.epsilon = standardDeviation(delta);
+
+ // log.debug("this.delta = " + this.delta);
+ // log.debug("this.epsilon = " + this.epsilon);
+ }
+
+ /**
+ * Performs a single reference clock request cycle and returns the estimated delta relative to the local clock.
+ * This is computed as the half-latency of the requst cycle, plus the reference clock, minus the local clock.
+ *
+ * @return The estimated clock delta.
+ *
+ * @throws ClockSynchFailureException If the reference service is not responding.
+ */
+ protected long ping() throws ClockSynchFailureException
+ {
+ // log.debug("protected long ping(): called");
+
+ try
+ {
+ byte[] buf = new byte[256];
+
+ boolean timedOut = false;
+ long start = 0L;
+ long refTime = 0L;
+ long localTime = 0L;
+ long latency = 0L;
+ int failCount = 0;
+
+ // Keep trying the ping until it gets a response, or 10 tries in a row all time out.
+ do
+ {
+ // Start timing the request latency.
+ start = nanoTime();
+
+ // Get the reference time.
+ DatagramPacket packet =
+ new DatagramPacket(buf, buf.length, referenceAddress, UDPClockReference.REFERENCE_PORT);
+ socket.send(packet);
+ packet = new DatagramPacket(buf, buf.length);
+
+ timedOut = false;
+
+ try
+ {
+ socket.receive(packet);
+ }
+ catch (SocketTimeoutException e)
+ {
+ timedOut = true;
+ failCount++;
+
+ continue;
+ }
+
+ ByteBuffer bbuf = ByteBuffer.wrap(packet.getData());
+ refTime = bbuf.getLong();
+
+ // Stop timing the request latency.
+ localTime = nanoTime();
+ latency = localTime - start;
+
+ // log.debug("refTime = " + refTime);
+ // log.debug("localTime = " + localTime);
+ // log.debug("start = " + start);
+ // log.debug("latency = " + latency);
+ // log.debug("delta = " + ((latency / 2) + (refTime - localTime)));
+
+ }
+ while (timedOut && (failCount < 10));
+
+ // Fail completely if the fail count is too high.
+ if (failCount >= 10)
+ {
+ throw new ClockSynchFailureException("Clock reference not responding.", null);
+ }
+
+ // Estimate delta as (ref clock + half-latency) - local clock.
+ return (latency / 2) + (refTime - localTime);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Gets the clock delta in nano seconds.
+ *
+ * @return The clock delta in nano seconds.
+ */
+ public long getDelta()
+ {
+ return delta;
+ }
+
+ /**
+ * Gets an estimate of the clock error in nan seconds.
+ *
+ * @return An estimate of the clock error in nan seconds.
+ */
+ public long getEpsilon()
+ {
+ return epsilon;
+ }
+
+ /**
+ * Gets the local clock time with any computed delta added in.
+ *
+ * @return The local clock time with any computed delta added in.
+ */
+ public long nanoTime()
+ {
+ return System.nanoTime() + delta;
+ }
+
+ /**
+ * Computes the median of a series of values.
+ *
+ * @param values The values.
+ *
+ * @return The median.
+ */
+ public static long median(long[] values)
+ {
+ // log.debug("public static long median(long[] values = " + Arrays.toString(values) + "): called");
+
+ long median;
+
+ // Order the list of values.
+ long[] orderedValues = new long[values.length];
+ System.arraycopy(values, 0, orderedValues, 0, values.length);
+ Arrays.sort(orderedValues);
+
+ // Check if the median is computed from a pair of middle value.
+ if ((orderedValues.length % 2) == 0)
+ {
+ int middle = orderedValues.length / 2;
+
+ median = (orderedValues[middle] + orderedValues[middle - 1]) / 2;
+ }
+ // The median is computed from a single middle value.
+ else
+ {
+ median = orderedValues[orderedValues.length / 2];
+ }
+
+ // log.debug("median = " + median);
+
+ return median;
+ }
+
+ /**
+ * Computes the mean of a series of values.
+ *
+ * @param values The values.
+ *
+ * @return The mean.
+ */
+ public static long mean(long[] values)
+ {
+ // log.debug("public static long mean(long[] values = " + Arrays.toString(values) + "): called");
+
+ long total = 0L;
+
+ for (long value : values)
+ {
+ total += value;
+ }
+
+ long mean = total / values.length;
+
+ // log.debug("mean = " + mean);
+
+ return mean;
+ }
+
+ /**
+ * Computes the variance of series of values.
+ *
+ * @param values The values.
+ *
+ * @return The variance of the values.
+ */
+ public static long variance(long[] values)
+ {
+ // log.debug("public static long variance(long[] values = " + Arrays.toString(values) + "): called");
+
+ long mean = mean(values);
+
+ long totalVariance = 0;
+
+ for (long value : values)
+ {
+ long diff = (value - mean);
+ totalVariance += diff * diff;
+ }
+
+ long variance = totalVariance / values.length;
+
+ // log.debug("variance = " + variance);
+
+ return variance;
+ }
+
+ /**
+ * Computes the standard deviation of a series of values.
+ *
+ * @param values The values.
+ *
+ * @return The standard deviation.
+ */
+ public static long standardDeviation(long[] values)
+ {
+ // log.debug("public static long standardDeviation(long[] values = " + Arrays.toString(values) + "): called");
+
+ long sd = Double.valueOf(Math.sqrt(variance(values))).longValue();
+
+ // log.debug("sd = " + sd);
+
+ return sd;
+ }
+
+ /**
+ * For testing purposes. Supply address of reference clock as arg 1.
+ *
+ * @param args Address of reference clock as arg 1.
+ */
+ public static void main(String[] args)
+ {
+ ParsedProperties options =
+ new ParsedProperties(CommandLineParser.processCommandLine(args,
+ new CommandLineParser(
+ new String[][]
+ {
+ { "1", "Address of clock reference service.", "address", "true" }
+ }), System.getProperties()));
+
+ String address = options.getProperty("1");
+
+ // Create a clock synchronizer.
+ UDPClockSynchronizer clockSyncher = new UDPClockSynchronizer(address);
+
+ // Set up a shutdown hook for it.
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable()
+ {
+ public void run()
+ {
+ doSynch = false;
+ }
+ }));
+
+ // Repeat the clock synching until the user kills the progam.
+ while (doSynch)
+ {
+ // Perform a clock clockSynch.
+ try
+ {
+ clockSyncher.synch();
+
+ // Print out the clock delta and estimate of the error.
+ System.out.println("Delta = " + clockSyncher.getDelta());
+ System.out.println("Epsilon = " + clockSyncher.getEpsilon());
+
+ try
+ {
+ Thread.sleep(250);
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the interrupted status and terminate the loop.
+ Thread.currentThread().interrupt();
+ doSynch = false;
+ }
+ }
+ // Terminate if the reference time service is unavailable.
+ catch (ClockSynchFailureException e)
+ {
+ doSynch = false;
+ }
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java
new file mode 100644
index 0000000000..628729cb51
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java
@@ -0,0 +1,469 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedcircuit;
+
+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 javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Session;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * DistributedCircuitImpl is a distributed implementation of the test {@link Circuit}. Many publishers and receivers
+ * accross multiple machines may be combined to form a single test circuit. The test circuit extracts reports from
+ * all of its publishers and receivers, and applies its assertions to these reports.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the publishing and receiving ends of a test messaging circuit.
+ * <tr><td> Start the circuit running.
+ * <tr><td> Close the circuit down.
+ * <tr><td> Take a reading of the circuits state.
+ * <tr><td> Apply assertions against the circuits state.
+ * <tr><td> Send test messages over the circuit.
+ * <tr><td> Perform the default test procedue on the circuit.
+ * </table>
+ *
+ * @todo There is a short pause after receiving sender reports before asking for receiver reports, because receivers may
+ * not have finished receiving all their test messages before the report request arrives. This is going to be a
+ * problem for taking test timings and needs to be eliminiated. Suggested solution: have receiver send back reports
+ * asynchronously, on test batch size boundaries, and do so automatically rather than having to have the report
+ * request sent to them. Number each test run, or otherwise uniquely identify it, when a receiver does not get
+ * any more messages on a test run for more than a timeout, it can assume the test is complete and send a final
+ * report. On the coordinator end a future will need to be created to wait for all final reports to come in, and
+ * to register results and timings for the test. This must work in such a way that a new test cycle can be started
+ * without waiting for the results of the old one to come in.
+ *
+ * @todo Add in setting of timing controller, from timing aware test cases.
+ */
+public class DistributedCircuitImpl implements Circuit, TimingControllerAware
+{
+ /** Used for debugging purposes. */
+ private static final Logger log = Logger.getLogger(DistributedCircuitImpl.class);
+
+ /** Holds the conversation factory over which to coordinate the test. */
+ protected ConversationFactory conversationFactory;
+
+ /** Holds the controlSession over which to hold the control conversation. */
+ protected Session controlSession;
+
+ /** Holds the sender nodes in the test circuit. */
+ protected List<TestClientDetails> senders;
+
+ /** Holds the receiver nodes in the test circuit. */
+ protected List<TestClientDetails> receivers;
+
+ /** Holds the sender control conversations. */
+ protected ConversationFactory.Conversation[] senderConversation;
+
+ /** Holds the receiver control conversations. */
+ protected ConversationFactory.Conversation[] receiverConversation;
+
+ /** Holds the control topics for the senders in the test circuit. */
+ protected Destination[] senderControlTopic;
+
+ /** Holds the control topics for the receivers in the test circuit. */
+ protected Destination[] receiverControlTopic;
+
+ /** Holds the number of messages to send per test run. */
+ protected int numMessages;
+
+ /**
+ * Holds the timing controller for the circuit. This is used to log test times asynchronously, when reciever nodes
+ * return their reports after senders have completed a test case.
+ */
+ TimingController timingController;
+
+ /**
+ * Creates a distributed test circuit on the specified senders and receivers.
+ *
+ * @param session The controlSession for all control conversations.
+ * @param senders The senders.
+ * @param receivers The receivers.
+ * @param senderConversation A control conversation with the senders.
+ * @param receiverConversation A control conversation with the receivers.
+ * @param senderControlTopic The senders control topic.
+ * @param receiverControlTopic The receivers control topic.
+ */
+ protected DistributedCircuitImpl(Session session, List<TestClientDetails> senders, List<TestClientDetails> receivers,
+ ConversationFactory.Conversation[] senderConversation, ConversationFactory.Conversation[] receiverConversation,
+ Destination[] senderControlTopic, Destination[] receiverControlTopic)
+ {
+ this.controlSession = session;
+ this.senders = senders;
+ this.receivers = receivers;
+ this.senderConversation = senderConversation;
+ this.receiverConversation = receiverConversation;
+ this.senderControlTopic = senderControlTopic;
+ this.receiverControlTopic = receiverControlTopic;
+ }
+
+ /**
+ * Creates a distributed test circuit from the specified test parameters, on the senders and receivers
+ * given.
+ *
+ * @param testProps The test parameters.
+ * @param senders The sender ends in the test circuit.
+ * @param receivers The receiver ends in the test circuit.
+ * @param conversationFactory A conversation factory for creating the control conversations with senders and receivers.
+ *
+ * @return A connected and ready to start, test circuit.
+ */
+ public static Circuit createCircuit(ParsedProperties testProps, List<TestClientDetails> senders,
+ List<TestClientDetails> receivers, ConversationFactory conversationFactory)
+ {
+ log.debug("public static Circuit createCircuit(ParsedProperties testProps, List<TestClientDetails> senders, "
+ + " List<TestClientDetails> receivers, ConversationFactory conversationFactory)");
+
+ try
+ {
+ Session session = conversationFactory.getSession();
+
+ // Create control conversations with each of the senders.
+ ConversationFactory.Conversation[] senderConversation = new ConversationFactory.Conversation[senders.size()];
+ Destination[] senderControlTopic = new Destination[senders.size()];
+
+ for (int i = 0; i < senders.size(); i++)
+ {
+ TestClientDetails sender = senders.get(i);
+
+ senderControlTopic[i] = session.createTopic(sender.privateControlKey);
+ senderConversation[i] = conversationFactory.startConversation();
+ }
+
+ log.debug("Sender conversations created.");
+
+ // Create control conversations with each of the receivers.
+ ConversationFactory.Conversation[] receiverConversation = new ConversationFactory.Conversation[receivers.size()];
+ Destination[] receiverControlTopic = new Destination[receivers.size()];
+
+ for (int i = 0; i < receivers.size(); i++)
+ {
+ TestClientDetails receiver = receivers.get(i);
+
+ receiverControlTopic[i] = session.createTopic(receiver.privateControlKey);
+ receiverConversation[i] = conversationFactory.startConversation();
+ }
+
+ log.debug("Receiver conversations created.");
+
+ // Assign the sender role to each of the sending test clients.
+ for (int i = 0; i < senders.size(); i++)
+ {
+ TestClientDetails sender = senders.get(i);
+
+ Message assignSender = conversationFactory.getSession().createMessage();
+ TestUtils.setPropertiesOnMessage(assignSender, testProps);
+ assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+ assignSender.setStringProperty("ROLE", "SENDER");
+
+ senderConversation[i].send(senderControlTopic[i], assignSender);
+ }
+
+ log.debug("Sender role assignments sent.");
+
+ // Assign the receivers role to each of the receiving test clients.
+ for (int i = 0; i < receivers.size(); i++)
+ {
+ TestClientDetails receiver = receivers.get(i);
+
+ Message assignReceiver = session.createMessage();
+ TestUtils.setPropertiesOnMessage(assignReceiver, testProps);
+ assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+ assignReceiver.setStringProperty("ROLE", "RECEIVER");
+
+ receiverConversation[i].send(receiverControlTopic[i], assignReceiver);
+ }
+
+ log.debug("Receiver role assignments sent.");
+
+ // Wait for the senders and receivers to confirm their roles.
+ for (int i = 0; i < senders.size(); i++)
+ {
+ senderConversation[i].receive();
+ }
+
+ log.debug("Got all sender role confirmations");
+
+ for (int i = 0; i < receivers.size(); i++)
+ {
+ receiverConversation[i].receive();
+ }
+
+ log.debug("Got all receiver role confirmations");
+
+ // Package everything up as a circuit.
+ return new DistributedCircuitImpl(session, senders, receivers, senderConversation, receiverConversation,
+ senderControlTopic, receiverControlTopic);
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("JMSException not handled.");
+ }
+ }
+
+ /**
+ * Used by tests cases that can supply a {@link uk.co.thebadgerset.junit.extensions.TimingController} to set the
+ * controller on an aware test.
+ *
+ * @param controller The timing controller.
+ */
+ public void setTimingController(TimingController controller)
+ {
+ this.timingController = controller;
+ }
+
+ /**
+ * Gets the interface on the publishing end of the circuit.
+ *
+ * @return The publishing end of the circuit.
+ */
+ public Publisher getPublisher()
+ {
+ throw new RuntimeException("Not Implemented.");
+ }
+
+ /**
+ * Gets the interface on the receiving end of the circuit.
+ *
+ * @return The receiving end of the circuit.
+ */
+ public Receiver getReceiver()
+ {
+ throw new RuntimeException("Not Implemented.");
+ }
+
+ /**
+ * Connects and starts the circuit. After this method is called the circuit is ready to send messages.
+ */
+ public void start()
+ {
+ log.debug("public void start(): called");
+
+ try
+ {
+ // Start the test on each of the senders.
+ Message start = controlSession.createMessage();
+ start.setStringProperty("CONTROL_TYPE", "START");
+ start.setIntProperty("MESSAGE_COUNT", numMessages);
+
+ for (int i = 0; i < senders.size(); i++)
+ {
+ senderConversation[i].send(senderControlTopic[i], start);
+ }
+
+ log.debug("All senders told to start their tests.");
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Unhandled JMSException.", e);
+ }
+ }
+
+ /**
+ * Checks the test circuit. The effect of this is to gather the circuits state, for both ends of the circuit,
+ * into a report, against which assertions may be checked.
+ *
+ * @todo Replace the asynch receiver report thread with a choice of direct or asynch executor, so that asynch
+ * or synch logging of test timings is optional. Also need to provide an onMessage method that is capable
+ * of receiving timing reports that receivers will generate during an ongoing test, on the test sample
+ * size boundaries. The message timing logging code should be factored out as a common method that can
+ * be called in response to the final report responses, or the onMessage method. Another alternative is
+ * to abandon the final report request altogether and just use the onMessage method? I think the two
+ * differ though, as the final report is used to apply assertions, and the ongoing report is just for
+ * periodic timing results... In which case, maybe there needs to be a way for the onMessage method
+ * to process just some of the incoming messages, and forward the rest on to the conversion helper, as
+ * a sort of pre-conversation helper filter? Make conversation expose its onMessage method (it should
+ * already) and allow another delivery thread to filter the incoming messages to the conversation.
+ */
+ public void check()
+ {
+ log.debug("public void check(): called");
+
+ try
+ {
+ // Wait for all the test senders to return their reports.
+ for (int i = 0; i < senders.size(); i++)
+ {
+ Message senderReport = senderConversation[i].receive();
+ log.debug("Sender " + senderReport.getStringProperty("CLIENT_NAME") + " reports message count: "
+ + senderReport.getIntProperty("MESSAGE_COUNT"));
+ log.debug("Sender " + senderReport.getStringProperty("CLIENT_NAME") + " reports message time: "
+ + senderReport.getLongProperty("TEST_TIME"));
+ }
+
+ log.debug("Got all sender test reports.");
+
+ // Apply sender assertions to pass/fail the tests.
+
+ // Inject a short pause to give the receivers time to finish receiving their test messages.
+ TestUtils.pause(500);
+
+ // Ask the receivers for their reports.
+ Message statusRequest = controlSession.createMessage();
+ statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST");
+
+ for (int i = 0; i < receivers.size(); i++)
+ {
+ receiverConversation[i].send(receiverControlTopic[i], statusRequest);
+ }
+
+ log.debug("All receiver test reports requested.");
+
+ // Wait for all receiver reports to come in, but do so asynchronously.
+ Runnable gatherAllReceiverReports =
+ new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ // Wait for all the receivers to send their reports.
+ for (int i = 0; i < receivers.size(); i++)
+ {
+ Message receiverReport = receiverConversation[i].receive();
+
+ String clientName = receiverReport.getStringProperty("CLIENT_NAME");
+ int messageCount = receiverReport.getIntProperty("MESSAGE_COUNT");
+ long testTime = receiverReport.getLongProperty("TEST_TIME");
+
+ log.debug("Receiver " + clientName + " reports message count: " + messageCount);
+ log.debug("Receiver " + receiverReport.getStringProperty("CLIENT_NAME")
+ + " reports message time: " + testTime);
+
+ // Apply receiver assertions to pass/fail the tests.
+
+ // Log the test timings on the asynchronous test timing controller.
+ /*try
+ {
+ timingController.completeTest(true, messageCount, testTime);
+ }
+ // The timing controll can throw InterruptedException is the current test is to be
+ // interrupted.
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }*/
+ }
+
+ log.debug("All receiver test reports received.");
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ Thread receiverReportsThread = new Thread(gatherAllReceiverReports);
+ receiverReportsThread.start();
+
+ // return new Message[] { senderReport, receiverReport };
+
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Unhandled JMSException.", e);
+ }
+ }
+
+ /**
+ * Closes the circuit. All associated resources are closed.
+ */
+ public void close()
+ {
+ log.debug("public void close(): called");
+
+ // End the current test on all senders and receivers.
+ }
+
+ /**
+ * Applies a list of assertions against the test circuit. The {@link #check()} method should be called before doing
+ * this, to ensure that the circuit has gathered its state into a report to assert against.
+ *
+ * @param assertions The list of assertions to apply.
+ *
+ * @return Any assertions that failed.
+ */
+ public List<Assertion> applyAssertions(List<Assertion> assertions)
+ {
+ log.debug("public List<Assertion> applyAssertions(List<Assertion> assertions = " + assertions + "): called");
+
+ List<Assertion> failures = new LinkedList<Assertion>();
+
+ for (Assertion assertion : assertions)
+ {
+ if (!assertion.apply())
+ {
+ failures.add(assertion);
+ }
+ }
+
+ return failures;
+ }
+
+ /**
+ * Runs the default test procedure against the circuit, and checks that all of the specified assertions hold.
+ *
+ * @param numMessages The number of messages to send using the default test procedure.
+ * @param assertions The list of assertions to apply.
+ *
+ * @return Any assertions that failed.
+ *
+ * @todo From check onwards needs to be handled as a future. The future must call back onto the test case to
+ * report results asynchronously.
+ */
+ public List<Assertion> test(int numMessages, List<Assertion> assertions)
+ {
+ log.debug("public List<Assertion> test(int numMessages = " + numMessages + ", List<Assertion> assertions = "
+ + assertions + "): called");
+
+ // Keep the number of messages to send per test run, where the send method can reference it.
+ this.numMessages = numMessages;
+
+ // Start the test running on all sender circuit ends.
+ start();
+
+ // Request status reports to be handed in.
+ check();
+
+ // Assert conditions on the publishing end of the circuit.
+ // Assert conditions on the receiving end of the circuit.
+ List<Assertion> failures = applyAssertions(assertions);
+
+ // Close the circuit ending the current test case.
+ close();
+
+ // Pass with no failed assertions or fail with a list of failed assertions.
+ return failures;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java
new file mode 100644
index 0000000000..9e3054964c
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedcircuit;
+
+import org.apache.qpid.test.framework.Assertion;
+import org.apache.qpid.test.framework.Publisher;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+public class DistributedPublisherImpl implements Publisher
+{
+ /**
+ * Provides an assertion that the publisher encountered no exceptions.
+ *
+ * @return An assertion that the publisher encountered no exceptions.
+ */
+ public Assertion noExceptionsAssertion()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Provides an assertion that the publisher got a no consumers exception on every message.
+ *
+ * @return An assertion that the publisher got a no consumers exception on every message.
+ */
+ public Assertion noConsumersAssertion()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Provides an assertion that the publisher got a no rout exception on every message.
+ *
+ * @return An assertion that the publisher got a no rout exception on every message.
+ */
+ public Assertion noRouteAssertion()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java
new file mode 100644
index 0000000000..4a77906448
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedcircuit;
+
+import org.apache.qpid.test.framework.Assertion;
+import org.apache.qpid.test.framework.Receiver;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+public class DistributedReceiverImpl implements Receiver
+{
+ /**
+ * Provides an assertion that the receivers encountered no exceptions.
+ *
+ * @return An assertion that the receivers encountered no exceptions.
+ */
+ public Assertion noExceptionsAssertion()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Provides an assertion that the receivers got all messages that were sent to it.
+ *
+ * @return An assertion that the receivers got all messages that were sent to it.
+ */
+ public Assertion allMessagesAssertion()
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java
new file mode 100644
index 0000000000..621d16ec9b
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java
@@ -0,0 +1,315 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedcircuit;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.*;
+import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest;
+import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl;
+
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+
+import javax.jms.*;
+
+/**
+ * A TestClientCircuitEnd is a {@link CircuitEnd} that may be controlled from a
+ * {@link org.apache.qpid.test.framework.distributedtesting.TestClient}, and that forms a single publishing or
+ * receiving end point in a distributed test {@link org.apache.qpid.test.framework.Circuit}.
+ *
+ * <p/>When operating in the SENDER role, this circuit end is capable of acting as part of the default circuit test
+ * procedure (described in the class comment for {@link org.apache.qpid.test.framework.Circuit}). That is, it will
+ * send the number of test messages required, using the test configuration parameters given in the test invite, and
+ * return a report on its activities to the circuit controller.
+ *
+ * <p/>When operation in the RECEIVER role, this circuit end acts as part of the default circuit test procedure. It will
+ * receive test messages, on the setup specified in the test configuration parameters, and keep count of the messages
+ * received, and time taken to receive them. When requested by the circuit controller to provide a report, it will
+ * return this report of its activities.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+public class TestClientCircuitEnd implements CircuitEnd, TestClientControlledTest
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(TestClientCircuitEnd.class);
+
+ /** Holds the test parameters. */
+ ParsedProperties testProps;
+
+ /** The number of test messages to send. */
+ private int numMessages;
+
+ /** The role to be played by the test. */
+ private Roles role;
+
+ /** The connection to send the test messages on. */
+ private Connection connection;
+
+ /** Holds the circuit end for this test. */
+ CircuitEnd circuitEnd;
+
+ /**
+ * Holds a message monitor for this circuit end, either the monitor on the consumer when in RECEIVER more, or
+ * a monitor updated on every message sent, when acting as a SENDER.
+ */
+ MessageMonitor messageMonitor;
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName()
+ {
+ return "DEFAULT_CIRCUIT_TEST";
+ }
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException
+ {
+ log.debug("public boolean acceptInvite(Message inviteMessage): called");
+
+ // Populate the test parameters from the invitation.
+ testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
+
+ for (Object key : testProps.keySet())
+ {
+ String propName = (String) key;
+
+ // If the test parameters is overridden by the invitation, use it instead.
+ String inviteValue = inviteMessage.getStringProperty(propName);
+
+ if (inviteValue != null)
+ {
+ testProps.setProperty(propName, inviteValue);
+ log.debug("Test invite supplied override to " + propName + " of " + inviteValue);
+ }
+
+ }
+
+ // Accept the invitation.
+ return true;
+ }
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
+ {
+ log.debug("public void assignRole(Roles role, Message assignRoleMessage): called");
+
+ // Take note of the role to be played.
+ this.role = role;
+
+ // Extract and retain the test parameters.
+ numMessages = 1; // assignRoleMessage.getIntProperty("NUM_MESSAGES");
+
+ // Connect using the test parameters.
+ connection = TestUtils.createConnection(testProps);
+
+ // Create a circuit end that matches the assigned role and test parameters.
+ switch (role)
+ {
+ // Check if the sender role is being assigned, and set up a message producer if so.
+ case SENDER:
+
+ // Set up the publisher.
+ circuitEnd = LocalCircuitImpl.createPublisherCircuitEnd(connection, testProps, 0L);
+
+ // Create a custom message monitor that will be updated on every message sent.
+ messageMonitor = new MessageMonitor();
+
+ break;
+
+ // Otherwise the receivers role is being assigned, so set this up to listen for messages.
+ case RECEIVER:
+
+ // Set up the receiver.
+ circuitEnd = LocalCircuitImpl.createReceiverCircuitEnd(connection, testProps, 0L);
+
+ // Use the message monitor from the consumer for stats.
+ messageMonitor = getMessageMonitor();
+
+ break;
+ }
+
+ // Reset all messaging stats for the report.
+ messageMonitor.reset();
+
+ connection.start();
+ }
+
+ /**
+ * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
+ *
+ * @param numMessages The number of test messages to send.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ *
+ * @todo Add round robin on destinations where multiple destinations being used.
+ *
+ * @todo Add rate limiting when rate limit specified on publishers.
+ *
+ * @todo Add Max pending message size protection. The receiver will have to send back some acks once in a while,
+ * to notify the publisher that its messages are being consumed. This makes the safety valve harder to
+ * implement than in the single VM case. For example, if the limit is 1000 messages, might want to get back
+ * an ack every 500, to notify the publisher that it can keep sending. What about pub/sub tests? Will it be
+ * necessary to wait for an ack from every receiver? This will have the effect of rate limiting to slow
+ * consumers too.
+ *
+ * @todo Add commits on every commit batch size boundary.
+ */
+ public void start(int numMessages) throws JMSException
+ {
+ log.debug("public void start(): called");
+
+ // If in the SENDER role, send the specified number of test messages to the circuit destinations.
+ if (role.equals(Roles.SENDER))
+ {
+ Message testMessage = getSession().createMessage();
+
+ for (int i = 0; i < numMessages; i++)
+ {
+ getProducer().send(testMessage);
+
+ // Increment the message count and timings.
+ messageMonitor.onMessage(testMessage);
+ }
+ }
+ }
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException
+ {
+ Message report = session.createMessage();
+ report.setStringProperty("CONTROL_TYPE", "REPORT");
+
+ // Add the count of messages sent/received to the report.
+ report.setIntProperty("MESSAGE_COUNT", messageMonitor.getNumMessage());
+
+ // Add the time to send/receive messages to the report.
+ report.setLongProperty("TEST_TIME", messageMonitor.getTime());
+
+ // Add any exceptions detected to the report.
+
+ return report;
+ }
+
+ /**
+ * Gets the message producer at this circuit end point.
+ *
+ * @return The message producer at with this circuit end point.
+ */
+ public MessageProducer getProducer()
+ {
+ return circuitEnd.getProducer();
+ }
+
+ /**
+ * Gets the message consumer at this circuit end point.
+ *
+ * @return The message consumer at this circuit end point.
+ */
+ public MessageConsumer getConsumer()
+ {
+ return circuitEnd.getConsumer();
+ }
+
+ /**
+ * Send the specified message over the producer at this end point.
+ *
+ * @param message The message to send.
+ *
+ * @throws JMSException Any JMS exception occuring during the send is allowed to fall through.
+ */
+ public void send(Message message) throws JMSException
+ {
+ // Send the message on the circuit ends producer.
+ circuitEnd.send(message);
+ }
+
+ /**
+ * Gets the JMS Session associated with this circuit end point.
+ *
+ * @return The JMS Session associated with this circuit end point.
+ */
+ public Session getSession()
+ {
+ return circuitEnd.getSession();
+ }
+
+ /**
+ * Closes the message producers and consumers and the sessions, associated with this circuit end point.
+ *
+ * @throws JMSException Any JMSExceptions occurring during the close are allowed to fall through.
+ */
+ public void close() throws JMSException
+ {
+ // Close the producer and consumer.
+ circuitEnd.close();
+ }
+
+ /**
+ * Returns the message monitor for reporting on received messages on this circuit end.
+ *
+ * @return The message monitor for this circuit end.
+ */
+ public MessageMonitor getMessageMonitor()
+ {
+ return circuitEnd.getMessageMonitor();
+ }
+
+ /**
+ * Returns the exception monitor for reporting on exceptions received on this circuit end.
+ *
+ * @return The exception monitor for this circuit end.
+ */
+ public ExceptionMonitor getExceptionMonitor()
+ {
+ return circuitEnd.getExceptionMonitor();
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java
new file mode 100644
index 0000000000..90a44e3b6b
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.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.test.framework.distributedtesting;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.NDC;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+import org.apache.qpid.test.framework.MessagingTestConfigProperties;
+import org.apache.qpid.test.framework.TestClientDetails;
+import org.apache.qpid.test.framework.TestUtils;
+import org.apache.qpid.test.framework.clocksynch.UDPClockReference;
+import org.apache.qpid.util.ConversationFactory;
+import org.apache.qpid.util.PrettyPrintingUtils;
+
+import uk.co.thebadgerset.junit.extensions.TKTestRunner;
+import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+import uk.co.thebadgerset.junit.extensions.util.CommandLineParser;
+import uk.co.thebadgerset.junit.extensions.util.MathUtils;
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+import uk.co.thebadgerset.junit.extensions.util.TestContextProperties;
+
+import javax.jms.*;
+
+import java.net.InetAddress;
+import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * <p/>Implements the coordinator client described in the interop testing specification
+ * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). This coordinator is built on
+ * top of the JUnit testing framework.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Find out what test clients are available. <td> {@link ConversationFactory}
+ * <tr><td> Decorate available tests to run all available clients. <td> {@link DistributedTestDecorator}
+ * <tr><td> Attach XML test result logger.
+ * <tr><td> Terminate the interop testing framework.
+ * </table>
+ *
+ * @todo Should accumulate failures over all tests, and return with success or fail code based on all results. May need
+ * to write a special TestResult to do this properly. At the moment only the last one used will be tested for
+ * errors, as the start method creates a fresh one for each test case run.
+ *
+ * @todo Remove hard coding of test cases and put on command line instead.
+ */
+public class Coordinator extends TKTestRunner
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(Coordinator.class);
+
+ /** Used for reporting to the console. */
+ private static final Logger console = Logger.getLogger("CONSOLE");
+
+ /** Defines the possible distributed test engines available to run coordinated test cases with. */
+ public enum TestEngine
+ {
+ /** Specifies the interop test engine. This tests all available clients in pairs. */
+ INTEROP,
+
+ /** Specifies the fanout test engine. This sets up one publisher role, and many reciever roles. */
+ FANOUT
+ }
+
+ /**
+ * Holds the test context properties that provides the default test parameters, plus command line overrides.
+ * This is initialized with the default test parameters, to which command line overrides may be applied.
+ */
+ protected static ParsedProperties testContextProperties =
+ TestContextProperties.getInstance(MessagingTestConfigProperties.defaults);
+
+ /** Holds the URL of the broker to coordinate the tests on. */
+ protected String brokerUrl;
+
+ /** Holds the virtual host to coordinate the tests on. If <tt>null</tt>, then the default virtual host is used. */
+ protected String virtualHost;
+
+ /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */
+ protected Set<TestClientDetails> enlistedClients = new HashSet<TestClientDetails>();
+
+ /** Holds the conversation helper for the control conversation. */
+ protected ConversationFactory conversationFactory;
+
+ /** Holds the connection that the coordinating messages are sent over. */
+ protected Connection connection;
+
+ /** Holds the path of the directory to output test results too, if one is defined. */
+ protected String reportDir;
+
+ /** Holds the coordinating test engine type to run the tests through. */
+ protected TestEngine engine;
+
+ /** Flag that indicates that all test clients should be terminated upon completion of the test cases. */
+ protected boolean terminate;
+
+ /**
+ * Creates an interop test coordinator on the specified broker and virtual host.
+ *
+ * @param repetitions The number of times to repeat the test, or test batch size.
+ * @param duration The length of time to run the tests for. -1 means no duration has been set.
+ * @param threads The concurrency levels to ramp up to.
+ * @param delay A delay in milliseconds between test runs.
+ * @param params The sets of 'size' parameters to pass to test.
+ * @param testCaseName The name of the test case to run.
+ * @param reportDir The directory to output the test results to.
+ * @param runName The name of the test run; used to name the output file.
+ * @param verbose Whether to print comments during test run.
+ * @param brokerUrl The URL of the broker to connect to.
+ * @param virtualHost The virtual host to run all tests on. Optional, may be <tt>null</tt>.
+ * @param engine The distributed test engine type to run the tests with.
+ * @param terminate <tt>true</tt> if test client nodes should be terminated at the end of the tests.
+ * @param csv <tt>true</tt> if the CSV results listener should be attached.
+ * @param xml <tt>true</tt> if the XML results listener should be attached.
+ */
+ public Coordinator(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName,
+ String reportDir, String runName, boolean verbose, String brokerUrl, String virtualHost, TestEngine engine,
+ boolean terminate, boolean csv, boolean xml)
+ {
+ super(repetitions, duration, threads, delay, params, testCaseName, reportDir, runName, csv, xml, verbose);
+
+ log.debug("public Coordinator(Integer repetitions = " + repetitions + " , Long duration = " + duration
+ + ", int[] threads = " + Arrays.toString(threads) + ", int delay = " + delay + ", int[] params = "
+ + Arrays.toString(params) + ", String testCaseName = " + testCaseName + ", String reportDir = " + reportDir
+ + ", String runName = " + runName + ", boolean verbose = " + verbose + ", String brokerUrl = " + brokerUrl
+ + ", String virtualHost =" + virtualHost + ", TestEngine engine = " + engine + ", boolean terminate = "
+ + terminate + ", boolean csv = " + csv + ", boolean xml = " + xml + "): called");
+
+ // Retain the connection parameters.
+ this.brokerUrl = brokerUrl;
+ this.virtualHost = virtualHost;
+ this.reportDir = reportDir;
+ this.engine = engine;
+ this.terminate = terminate;
+ }
+
+ /**
+ * The entry point for the interop test coordinator. This client accepts the following command line arguments:
+ *
+ * <p/><table>
+ * <tr><td> -b <td> The broker URL. <td> Mandatory.
+ * <tr><td> -h <td> The virtual host. <td> Optional.
+ * <tr><td> -o <td> The directory to output test results to. <td> Optional.
+ * <tr><td> -e <td> The type of test distribution engine to use. <td> Optional. One of: interop, fanout.
+ * <tr><td> ... <td> Free arguments. The distributed test cases to run.
+ * <td> Mandatory. At least one must be defined.
+ * <tr><td> name=value <td> Trailing argument define name/value pairs. Added to the test contenxt properties.
+ * <td> Optional.
+ * </table>
+ *
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args)
+ {
+ NDC.push("coordinator");
+ log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called");
+ console.info("Qpid Distributed Test Coordinator.");
+
+ // Override the default broker url to be localhost:5672.
+ testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672");
+
+ try
+ {
+ // Use the command line parser to evaluate the command line with standard handling behaviour (print errors
+ // and usage then exist if there are errors).
+ // Any options and trailing name=value pairs are also injected into the test context properties object,
+ // to override any defaults that may have been set up.
+ ParsedProperties options =
+ new ParsedProperties(CommandLineParser.processCommandLine(args,
+ new CommandLineParser(
+ new String[][]
+ {
+ { "b", "The broker URL.", "broker", "false" },
+ { "h", "The virtual host to use.", "virtual host", "false" },
+ { "o", "The name of the directory to output test timings to.", "dir", "false" },
+ {
+ "e", "The test execution engine to use. Default is interop.", "engine", "interop",
+ "^interop$|^fanout$", "true"
+ },
+ { "t", "Terminate test clients on completion of tests.", null, "false" },
+ { "-csv", "Output test results in CSV format.", null, "false" },
+ { "-xml", "Output test results in XML format.", null, "false" },
+ {
+ "-trefaddr", "To specify an alternative to hostname for time singal reference.",
+ "address", "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 },
+ { "v", "Verbose mode.", null, "false" },
+ { "n", "A name for this test run, used to name the output file.", "name", "true" }
+ }), testContextProperties));
+
+ // Extract the command line options.
+ String brokerUrl = options.getProperty("b");
+ String virtualHost = options.getProperty("h");
+ String reportDir = options.getProperty("o");
+ reportDir = (reportDir == null) ? "." : reportDir;
+ String testEngine = options.getProperty("e");
+ TestEngine engine = "fanout".equals(testEngine) ? TestEngine.FANOUT : TestEngine.INTEROP;
+ boolean terminate = options.getPropertyAsBoolean("t");
+ boolean csvResults = options.getPropertyAsBoolean("-csv");
+ boolean xmlResults = options.getPropertyAsBoolean("-xml");
+
+ String threadsString = options.getProperty("c");
+ Integer repetitions = options.getPropertyAsInteger("r");
+ String durationString = options.getProperty("d");
+ String paramsString = options.getProperty("s");
+ boolean verbose = options.getPropertyAsBoolean("v");
+ String testRunName = options.getProperty("n");
+
+ int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString);
+ int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString);
+ Long duration = (durationString == null) ? null : MathUtils.parseDuration(durationString);
+
+ // If broker or virtual host settings were specified as command line options, override the defaults in the
+ // test context properties with them.
+
+ // Collection all of the test cases to be run.
+ Collection<Class<? extends FrameworkBaseCase>> testCaseClasses =
+ new ArrayList<Class<? extends FrameworkBaseCase>>();
+
+ // Scan for available test cases using a classpath scanner.
+ // ClasspathScanner.getMatches(DistributedTestCase.class, "^Test.*", true);
+
+ // Hard code the test classes till the classpath scanner is fixed.
+ // Collections.addAll(testCaseClasses, InteropTestCase1DummyRun.class, InteropTestCase2BasicP2P.class,
+ // InteropTestCase3BasicPubSub.class);
+
+ // Parse all of the free arguments as test cases to run.
+ for (int i = 1; true; i++)
+ {
+ String nextFreeArg = options.getProperty(Integer.toString(i));
+
+ // Terminate the loop once all free arguments have been consumed.
+ if (nextFreeArg == null)
+ {
+ break;
+ }
+
+ try
+ {
+ Class nextClass = Class.forName(nextFreeArg);
+
+ if (FrameworkBaseCase.class.isAssignableFrom(nextClass))
+ {
+ testCaseClasses.add(nextClass);
+ console.info("Found distributed test case: " + nextFreeArg);
+ }
+ }
+ catch (ClassNotFoundException e)
+ {
+ console.info("Unable to instantiate the test case: " + nextFreeArg + ".");
+ }
+ }
+
+ // Check that some test classes were actually found.
+ if (testCaseClasses.isEmpty())
+ {
+ throw new RuntimeException(
+ "No test cases implementing FrameworkBaseCase were specified on the command line.");
+ }
+
+ // Extract the names of all the test classes, to pass to the start method.
+ int i = 0;
+ String[] testClassNames = new String[testCaseClasses.size()];
+
+ for (Class testClass : testCaseClasses)
+ {
+ testClassNames[i++] = testClass.getName();
+ }
+
+ // Create a coordinator and begin its test procedure.
+ Coordinator coordinator =
+ new Coordinator(repetitions, duration, threads, 0, params, null, reportDir, testRunName, verbose, brokerUrl,
+ virtualHost, engine, terminate, csvResults, xmlResults);
+
+ TestResult testResult = coordinator.start(testClassNames);
+
+ // Return different error codes, depending on whether or not there were test failures.
+ if (testResult.failureCount() > 0)
+ {
+ System.exit(FAILURE_EXIT);
+ }
+ else
+ {
+ System.exit(SUCCESS_EXIT);
+ }
+ }
+ catch (Exception e)
+ {
+ log.debug("Top level handler caught execption.", e);
+ console.info(e.getMessage());
+ System.exit(EXCEPTION_EXIT);
+ }
+ }
+
+ /**
+ * Starts all of the test classes to be run by this coordinator.
+ *
+ * @param testClassNames An array of all the coordinating test case implementations.
+ *
+ * @return A JUnit TestResult to run the tests with.
+ *
+ * @throws Exception Any underlying exceptions are allowed to fall through, and fail the test process.
+ */
+ public TestResult start(String[] testClassNames) throws Exception
+ {
+ log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames)
+ + ": called");
+
+ // Connect to the broker.
+ connection = TestUtils.createConnection(TestContextProperties.getInstance());
+ Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ Destination controlTopic = session.createTopic("iop.control");
+ Destination responseQueue = session.createQueue("coordinator");
+
+ conversationFactory = new ConversationFactory(connection, responseQueue, LinkedBlockingQueue.class);
+ ConversationFactory.Conversation conversation = conversationFactory.startConversation();
+
+ connection.start();
+
+ // Broadcast the compulsory invitation to find out what clients are available to test.
+ Message invite = session.createMessage();
+ invite.setStringProperty("CONTROL_TYPE", "INVITE");
+ invite.setJMSReplyTo(responseQueue);
+
+ conversation.send(controlTopic, invite);
+
+ // Wait for a short time, to give test clients an opportunity to reply to the invitation.
+ Collection<Message> enlists = conversation.receiveAll(0, 500);
+ enlistedClients = extractEnlists(enlists);
+
+ for (TestClientDetails client : enlistedClients)
+ {
+ log.debug("Got enlisted test client: " + client);
+ console.info("Test node " + client.clientName + " available.");
+ }
+
+ // Start the clock reference service running.
+ UDPClockReference clockReference = new UDPClockReference();
+ Thread clockRefThread = new Thread(clockReference);
+ registerShutdownHook(clockReference);
+ clockRefThread.start();
+
+ // Broadcast to all clients to synchronize their clocks against the coordinators clock reference.
+ Message clockSynchRequest = session.createMessage();
+ clockSynchRequest.setStringProperty("CONTROL_TYPE", "CLOCK_SYNCH");
+
+ String localAddress = InetAddress.getByName(InetAddress.getLocalHost().getHostName()).getHostAddress();
+ clockSynchRequest.setStringProperty("ADDRESS", localAddress);
+
+ conversation.send(controlTopic, clockSynchRequest);
+
+ // Run the test in the suite using JUnit.
+ TestResult result = null;
+
+ for (String testClassName : testClassNames)
+ {
+ // Record the current test class, so that the test results can be output to a file incorporating this name.
+ this.currentTestClassName = testClassName;
+
+ result = super.start(new String[] { testClassName });
+ }
+
+ // At this point in time, all tests have completed. Broadcast the shutdown message, if the termination option
+ // was set on the command line.
+ if (terminate)
+ {
+ Message terminate = session.createMessage();
+ terminate.setStringProperty("CONTROL_TYPE", "TERMINATE");
+
+ conversation.send(controlTopic, terminate);
+ }
+
+ return result;
+ }
+
+ /**
+ * For a collection of enlist messages, this method pulls out of the client details for the enlisting clients.
+ *
+ * @param enlists The enlist messages.
+ *
+ * @return A set of enlisting clients, extracted from the enlist messages.
+ *
+ * @throws JMSException Any underlying JMSException is allowed to fall through.
+ */
+ public static Set<TestClientDetails> extractEnlists(Collection<Message> enlists) throws JMSException
+ {
+ log.debug("public static Set<TestClientDetails> extractEnlists(Collection<Message> enlists = " + enlists
+ + "): called");
+
+ Set<TestClientDetails> enlistedClients = new HashSet<TestClientDetails>();
+
+ // Retain the list of all available clients.
+ for (Message enlist : enlists)
+ {
+ TestClientDetails clientDetails = new TestClientDetails();
+ clientDetails.clientName = enlist.getStringProperty("CLIENT_NAME");
+ clientDetails.privateControlKey = enlist.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY");
+
+ String replyType = enlist.getStringProperty("CONTROL_TYPE");
+
+ if ("ENLIST".equals(replyType))
+ {
+ enlistedClients.add(clientDetails);
+ }
+ else if ("DECLINE".equals(replyType))
+ {
+ log.debug("Test client " + clientDetails.clientName + " declined the invite.");
+ }
+ else
+ {
+ log.warn("Got an unknown reply type, " + replyType + ", to the invite.");
+ }
+ }
+
+ return enlistedClients;
+ }
+
+ /**
+ * 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 coordinators ability to invite test clients to participate in
+ * tests.
+ *
+ * @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 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 FrameworkBaseCase)
+ {
+ log.debug("nextTest is a FrameworkBaseCase");
+ }
+ }
+
+ targetTest = new WrappedSuiteTestDecorator(suite);
+ log.debug("Wrapped with a WrappedSuiteTestDecorator.");
+ }
+
+ // Wrap the tests in a suitable distributed test decorator, to perform the invite/test cycle.
+ targetTest = newTestDecorator(targetTest, enlistedClients, conversationFactory, connection);
+
+ // TestSuite suite = new TestSuite();
+ // suite.addTest(targetTest);
+
+ // Wrap the tests in a scaled test decorator to them them as a 'batch' in one thread.
+ // targetTest = new ScaledTestDecorator(targetTest, new int[] { 1 });
+
+ return super.doRun(targetTest, wait);
+ }
+
+ /**
+ * Creates a wrapped test decorator, that is capable of inviting enlisted clients to participate in a specified
+ * test. This is the test engine that sets up the roles and sequences a distributed test case.
+ *
+ * @param targetTest The test decorator to wrap.
+ * @param enlistedClients The enlisted clients available to run the test.
+ * @param conversationFactory The conversation factory used to build conversation helper over the specified connection.
+ * @param connection The connection to talk to the enlisted clients over.
+ *
+ * @return An invititing test decorator, that invites all the enlisted clients to participate in tests, in pairs.
+ */
+ protected DistributedTestDecorator newTestDecorator(WrappedSuiteTestDecorator targetTest,
+ Set<TestClientDetails> enlistedClients, ConversationFactory conversationFactory, Connection connection)
+ {
+ switch (engine)
+ {
+ case FANOUT:
+ return new FanOutTestDecorator(targetTest, enlistedClients, conversationFactory, connection);
+ case INTEROP:
+ default:
+ return new InteropTestDecorator(targetTest, enlistedClients, conversationFactory, connection);
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java
new file mode 100644
index 0000000000..c2f34b44fc
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java
@@ -0,0 +1,166 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedtesting;
+
+import junit.framework.TestResult;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+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 javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import java.util.*;
+
+/**
+ * DistributedTestDecorator is a base class for writing test decorators that invite test clients to participate in
+ * distributed test cases. It provides a helper method, {@link #signupClients}, that broadcasts an invitation and
+ * returns the set of test clients that are available to particiapte in the test.
+ *
+ * <p/>When used to wrap a {@link FrameworkBaseCase} test, it replaces the default {@link CircuitFactory} implementations
+ * with a suitable circuit factory for distributed tests. Concrete implementations can use this to configure the sending
+ * and receiving roles on the test.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Broadcast test invitations and collect enlists. <td> {@link ConversationFactory}.
+ * </table>
+ */
+public abstract class DistributedTestDecorator extends WrappedSuiteTestDecorator
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(DistributedTestDecorator.class);
+
+ /** Holds the contact information for all test clients that are available and that may take part in the test. */
+ Set<TestClientDetails> allClients;
+
+ /** Holds the conversation helper for the control level conversation for coordinating the test through. */
+ ConversationFactory conversationFactory;
+
+ /** Holds the connection that the control conversation is held over. */
+ Connection connection;
+
+ /** Holds the underlying test suite that this decorator wraps. */
+ WrappedSuiteTestDecorator testSuite;
+
+ /** Holds the control topic, on which test invitations are broadcast. */
+ protected Destination controlTopic;
+
+ /**
+ * Creates a wrapped suite test decorator from another one.
+ *
+ * @param suite The test suite.
+ * @param availableClients The list of all clients that responded to the compulsory invite.
+ * @param controlConversation The conversation helper for the control level, test coordination conversation.
+ * @param controlConnection The connection that the coordination messages are sent over.
+ */
+ public DistributedTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> availableClients,
+ ConversationFactory controlConversation, Connection controlConnection)
+ {
+ super(suite);
+
+ log.debug("public DistributedTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> allClients = "
+ + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called");
+
+ testSuite = suite;
+ allClients = availableClients;
+ conversationFactory = controlConversation;
+ connection = controlConnection;
+
+ // Set up the test control topic.
+ try
+ {
+ controlTopic = conversationFactory.getSession().createTopic("iop.control");
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Unable to create the coordinating control topic to broadcast test invites on.", e);
+ }
+ }
+
+ /**
+ * Should run all of the tests in the wrapped test suite.
+ *
+ * @param testResult The the results object to monitor the test results with.
+ */
+ public abstract void run(TestResult testResult);
+
+ /**
+ * Should provide the distributed test sequencer to pass to {@link org.apache.qpid.test.framework.FrameworkBaseCase}
+ * tests.
+ *
+ * @return A distributed test sequencer.
+ */
+ public abstract CircuitFactory getTestSequencer();
+
+ /**
+ * Broadcasts an invitation to participate in a coordinating test case to find out what clients are available to
+ * run the test case.
+ *
+ * @param coordTest The coordinating test case to broadcast an inviate for.
+ *
+ * @return A set of test clients that accepted the invitation.
+ */
+ protected Set<TestClientDetails> signupClients(FrameworkBaseCase coordTest)
+ {
+ // Broadcast the invitation to find out what clients are available to test.
+ Set<TestClientDetails> enlists;
+ try
+ {
+ Message invite = conversationFactory.getSession().createMessage();
+
+ ConversationFactory.Conversation conversation = conversationFactory.startConversation();
+
+ invite.setStringProperty("CONTROL_TYPE", "INVITE");
+ invite.setStringProperty("TEST_NAME", coordTest.getTestCaseNameForTestMethod(coordTest.getName()));
+
+ conversation.send(controlTopic, invite);
+
+ // Wait for a short time, to give test clients an opportunity to reply to the invitation.
+ Collection<Message> replies = conversation.receiveAll(allClients.size(), 500);
+ enlists = Coordinator.extractEnlists(replies);
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e);
+ }
+
+ return enlists;
+ }
+
+ /**
+ * Prints a string summarizing this test decorator, mainly for debugging purposes.
+ *
+ * @return String representation for debugging purposes.
+ */
+ public String toString()
+ {
+ return "DistributedTestDecorator: [ testSuite = " + testSuite + " ]";
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java
new file mode 100644
index 0000000000..995aa5e71d
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java
@@ -0,0 +1,245 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedtesting;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.DropInTest;
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+import org.apache.qpid.test.framework.TestClientDetails;
+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 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;
+
+/**
+ * FanOutTestDecorator is an {@link DistributedTestDecorator} that runs one test client in the sender role, and the remainder
+ * in the receivers role. It also has the capability to listen for new test cases joining the test beyond the initial start
+ * point. This feature can be usefull when experimenting with adding more load, in the form of more test clients, to assess
+ * its impact on a running test.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Execute coordinated test cases. <td> {@link FrameworkBaseCase}
+ * <tr><td> Accept test clients joining a running test.
+ * </table>
+ */
+public class FanOutTestDecorator extends DistributedTestDecorator implements MessageListener
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(FanOutTestDecorator.class);
+
+ /** Holds the currently running test case. */
+ FrameworkBaseCase currentTest = null;
+
+ /**
+ * Creates a wrapped suite test decorator from another one.
+ *
+ * @param suite The test suite.
+ * @param availableClients The list of all clients that responded to the compulsory invite.
+ * @param controlConversation The conversation helper for the control level, test coordination conversation.
+ * @param controlConnection The connection that the coordination messages are sent over.
+ */
+ public FanOutTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> availableClients,
+ ConversationFactory controlConversation, Connection controlConnection)
+ {
+ super(suite, availableClients, controlConversation, controlConnection);
+
+ log.debug("public DistributedTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> allClients = "
+ + availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called");
+
+ testSuite = suite;
+ allClients = availableClients;
+ conversationFactory = controlConversation;
+ connection = controlConnection;
+
+ // Sign available clients up to the test.
+ for (Test test : getAllUnderlyingTests())
+ {
+ FrameworkBaseCase coordTest = (FrameworkBaseCase) test;
+
+ // Get all of the clients able to participate in the test.
+ Set<TestClientDetails> enlists = signupClients(coordTest);
+
+ // Check that there were some clients available.
+ if (enlists.size() == 0)
+ {
+ throw new RuntimeException("No clients to test with");
+ }
+
+ // Create a distributed test circuit factory for the test.
+ CircuitFactory circuitFactory = getTestSequencer();
+
+ // Set up the first client in the sender role, and the remainder in the receivers role.
+ Iterator<TestClientDetails> clients = enlists.iterator();
+ circuitFactory.setSender(clients.next());
+
+ while (clients.hasNext())
+ {
+ // Set the sending and receiving client details on the test case.
+ circuitFactory.setReceiver(clients.next());
+ }
+
+ // Pass down the connection to hold the coordinating conversation over.
+ circuitFactory.setConversationFactory(conversationFactory);
+
+ // If the current test case is a drop-in test, set it up as the currently running test for late joiners to
+ // add in to. Otherwise the current test field is set to null, to indicate that late joiners are not allowed.
+ currentTest = (coordTest instanceof DropInTest) ? coordTest : null;
+
+ // Execute the test case.
+ coordTest.setCircuitFactory(circuitFactory);
+ }
+ }
+
+ /**
+ * Broadcasts a test invitation and accepts enlists from participating clients. The wrapped test cases are run
+ * with one test client in the sender role, and the remaining test clients in the receiving role.
+ *
+ * <p/>Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime
+ * exceptions, resulting in the non-completion of the test run.
+ *
+ * @param testResult The the results object to monitor the test results with.
+ *
+ * @todo Better error recovery for failure of the invite/enlist conversation could be added.
+ */
+ public void run(TestResult testResult)
+ {
+ log.debug("public void run(TestResult testResult): called");
+
+ // Listen for late joiners on the control topic.
+ try
+ {
+ conversationFactory.getSession().createConsumer(controlTopic).setMessageListener(this);
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Unable to set up the message listener on the control topic.", e);
+ }
+
+ // Run all of the test cases in the test suite.
+ /*for (Test test : getAllUnderlyingTests())
+ {
+ FrameworkBaseCase coordTest = (FrameworkBaseCase) test;
+
+ // Get all of the clients able to participate in the test.
+ Set<TestClientDetails> enlists = signupClients(coordTest);
+
+ // Check that there were some clients available.
+ if (enlists.size() == 0)
+ {
+ throw new RuntimeException("No clients to test with");
+ }
+
+ // Create a distributed test circuit factory for the test.
+ CircuitFactory circuitFactory = getTestSequencer();
+
+ // Set up the first client in the sender role, and the remainder in the receivers role.
+ Iterator<TestClientDetails> clients = enlists.iterator();
+ circuitFactory.setSender(clients.next());
+
+ while (clients.hasNext())
+ {
+ // Set the sending and receiving client details on the test case.
+ circuitFactory.setReceiver(clients.next());
+ }
+
+ // Pass down the connection to hold the coordinating conversation over.
+ circuitFactory.setConversationFactory(conversationFactory);
+
+ // If the current test case is a drop-in test, set it up as the currently running test for late joiners to
+ // add in to. Otherwise the current test field is set to null, to indicate that late joiners are not allowed.
+ currentTest = (coordTest instanceof DropInTest) ? coordTest : null;
+
+ // Execute the test case.
+ coordTest.setCircuitFactory(circuitFactory);
+ }*/
+
+ // Run all of the test cases in the test suite.
+ for (Test test : getAllUnderlyingTests())
+ {
+ FrameworkBaseCase coordTest = (FrameworkBaseCase) test;
+
+ coordTest.run(testResult);
+
+ currentTest = null;
+ }
+ }
+
+ /**
+ * Should provide the distributed test sequencer to pass to {@link org.apache.qpid.test.framework.FrameworkBaseCase}
+ * tests.
+ *
+ * @return A distributed test sequencer.
+ */
+ public CircuitFactory getTestSequencer()
+ {
+ return new FanOutCircuitFactory();
+ }
+
+ /**
+ * Listens to incoming messages on the control topic. If the messages are 'join' messages, signalling a new
+ * test client wishing to join the current test, then the new client will be added to the current test in the
+ * receivers role.
+ *
+ * @param message The incoming control message.
+ */
+ public void onMessage(Message message)
+ {
+ try
+ {
+ // Check if the message is from a test client attempting to join a running test, and join it to the current
+ // test case if so.
+ if (message.getStringProperty("CONTROL_TYPE").equals("JOIN") && (currentTest != null))
+ {
+ ((DropInTest) currentTest).lateJoin(message);
+ }
+ }
+ // There is not a lot can be done with this error, so it is deliberately ignored.
+ catch (JMSException e)
+ {
+ log.debug("Unable to process message:" + message);
+ }
+ }
+
+ /**
+ * Prints a string summarizing this test decorator, mainly for debugging purposes.
+ *
+ * @return String representation for debugging purposes.
+ */
+ public String toString()
+ {
+ return "FanOutTestDecorator: [ testSuite = " + testSuite + " ]";
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java
new file mode 100644
index 0000000000..918f0f9177
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java
@@ -0,0 +1,208 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedtesting;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+import org.apache.qpid.test.framework.TestClientDetails;
+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 javax.jms.Connection;
+
+import java.util.*;
+
+/**
+ * DistributedTestDecorator is a test decorator, written to implement the interop test specification. Given a list
+ * of enlisted test clients, that are available to run interop tests, this decorator invites them to participate
+ * in each test in the wrapped test suite. Amongst all the clients that respond to the invite, all pairs are formed,
+ * and each pairing (in both directions, but excluding the reflexive pairings) is split into a sender and receivers
+ * role and a test case run between them. Any enlisted combinations that do not accept a test invite are automatically
+ * failed.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Broadcast test invitations and collect enlists. <td> {@link org.apache.qpid.util.ConversationFactory}.
+ * <tr><td> Output test failures for clients unwilling to run the test case. <td> {@link Coordinator}
+ * <tr><td> Execute distributed test cases. <td> {@link FrameworkBaseCase}
+ * <tr><td> Fail non participating pairings. <td> {@link OptOutTestCase}
+ * </table>
+ */
+public class InteropTestDecorator extends DistributedTestDecorator
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(InteropTestDecorator.class);
+
+ /**
+ * Creates a wrapped suite test decorator from another one.
+ *
+ * @param suite The test suite.
+ * @param availableClients The list of all clients that responded to the compulsory invite.
+ * @param controlConversation The conversation helper for the control level, test coordination conversation.
+ * @param controlConnection The connection that the coordination messages are sent over.
+ */
+ public InteropTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> availableClients,
+ ConversationFactory controlConversation, Connection controlConnection)
+ {
+ super(suite, availableClients, controlConversation, controlConnection);
+ }
+
+ /**
+ * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is
+ * then repeated for every combination of test clients (provided the wrapped test case extends
+ * {@link FrameworkBaseCase}.
+ *
+ * <p/>Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime exceptions,
+ * resulting in the non-completion of the test run.
+ *
+ * @todo Better error recovery for failure of the invite/enlist conversation could be added.
+ *
+ * @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");
+
+ Collection<Test> tests = testSuite.getAllUnderlyingTests();
+
+ for (Test test : getAllUnderlyingTests())
+ {
+ FrameworkBaseCase coordTest = (FrameworkBaseCase) test;
+
+ // Broadcast the invitation to find out what clients are available to test.
+ Set<TestClientDetails> enlists = signupClients(coordTest);
+
+ // Compare the list of willing clients to the list of all available.
+ Set<TestClientDetails> optOuts = new HashSet<TestClientDetails>(allClients);
+ optOuts.removeAll(enlists);
+
+ // Output test failures for clients that will not particpate in the test.
+ Set<List<TestClientDetails>> failPairs = allPairs(optOuts, allClients);
+
+ for (List<TestClientDetails> failPair : failPairs)
+ {
+ // Create a distributed test circuit factory for the test.
+ CircuitFactory circuitFactory = getTestSequencer();
+
+ // Create an automatic failure test for the opted out test pair.
+ FrameworkBaseCase failTest = new OptOutTestCase("testOptOut");
+ circuitFactory.setSender(failPair.get(0));
+ circuitFactory.setReceiver(failPair.get(1));
+ failTest.setCircuitFactory(circuitFactory);
+
+ failTest.run(testResult);
+ }
+
+ // Loop over all combinations of clients, willing to run the test.
+ Set<List<TestClientDetails>> enlistedPairs = allPairs(enlists, enlists);
+
+ for (List<TestClientDetails> enlistedPair : enlistedPairs)
+ {
+ // Create a distributed test circuit factory for the test.
+ CircuitFactory circuitFactory = getTestSequencer();
+
+ // Set the sending and receiving client details on the test circuitFactory.
+ circuitFactory.setSender(enlistedPair.get(0));
+ circuitFactory.setReceiver(enlistedPair.get(1));
+
+ // Pass down the connection to hold the coordination conversation over.
+ circuitFactory.setConversationFactory(conversationFactory);
+
+ // Execute the test case.
+ coordTest.setCircuitFactory(circuitFactory);
+ coordTest.run(testResult);
+ }
+ }
+ }
+
+ /**
+ * Should provide the distributed test sequencer to pass to {@link org.apache.qpid.test.framework.FrameworkBaseCase}
+ * tests.
+ *
+ * @return A distributed test sequencer.
+ */
+ public CircuitFactory getTestSequencer()
+ {
+ return new InteropCircuitFactory();
+ }
+
+ /**
+ * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is
+ * important, that is the pair <l, r> is distinct from <r, l>; both pairs are generated. For any element, i, in
+ * both the left and right sets, the reflexive pair <i, i> is not generated.
+ *
+ * @param left The left set.
+ * @param right The right set.
+ * @param <E> The type of the content of the pairs.
+ *
+ * @return All pairs formed from the permutations of all elements of the left and right sets.
+ */
+ private <E> Set<List<E>> allPairs(Set<E> left, Set<E> right)
+ {
+ log.debug("private <E> Set<List<E>> allPairs(Set<E> left = " + left + ", Set<E> right = " + right + "): called");
+
+ Set<List<E>> results = new HashSet<List<E>>();
+
+ // Form all pairs from left to right.
+ // Form all pairs from right to left.
+ for (E le : left)
+ {
+ for (E re : right)
+ {
+ if (!le.equals(re))
+ {
+ results.add(new Pair<E>(le, re));
+ results.add(new Pair<E>(re, le));
+ }
+ }
+ }
+
+ log.debug("results = " + results);
+
+ return results;
+ }
+
+ /**
+ * A simple implementation of a pair, using a list.
+ */
+ private class Pair<T> extends ArrayList<T>
+ {
+ /**
+ * Creates a new pair of elements.
+ *
+ * @param first The first element.
+ * @param second The second element.
+ */
+ public Pair(T first, T second)
+ {
+ super();
+ super.add(first);
+ super.add(second);
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/OptOutTestCase.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/OptOutTestCase.java
new file mode 100644
index 0000000000..fcdac20ca2
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/OptOutTestCase.java
@@ -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.
+ *
+ */
+package org.apache.qpid.test.framework.distributedtesting;
+
+import org.apache.qpid.test.framework.sequencers.CircuitFactory;
+import org.apache.qpid.test.framework.FrameworkBaseCase;
+
+/**
+ * An OptOutTestCase is a test case that automatically fails. It is used when a list of test clients has been generated
+ * from a compulsory invite, but only some of those clients have responded to a specific test case invite. The clients
+ * that did not respond, may automatically be given a fail for some tests.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Fail the test with a suitable reason.
+ * </table>
+ */
+public class OptOutTestCase extends FrameworkBaseCase
+{
+ /**
+ * Creates a new coordinating test case with the specified name.
+ *
+ * @param name The test case name.
+ */
+ public OptOutTestCase(String name)
+ {
+ super(name);
+ }
+
+ /** Generates an appropriate test failure assertion. */
+ public void testOptOut()
+ {
+ CircuitFactory circuitFactory = getCircuitFactory();
+
+ fail("One of " + circuitFactory.getSender() + " and " + getCircuitFactory().getReceivers()
+ + " opted out of the test.");
+ }
+
+ /**
+ * Should provide a translation from the junit method name of a test to its test case name as defined in the
+ * interop testing specification. For example the method "testP2P" might map onto the interop test case name
+ * "TC2_BasicP2P".
+ *
+ * @param methodName The name of the JUnit test method.
+ * @return The name of the corresponding interop test case.
+ */
+ public String getTestCaseNameForTestMethod(String methodName)
+ {
+ return "OptOutTest";
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClientControlledTest.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClientControlledTest.java
new file mode 100644
index 0000000000..893a022b97
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClientControlledTest.java
@@ -0,0 +1,108 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.distributedtesting;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.Session;
+
+/**
+ * TestClientControlledTest provides an interface that classes implementing test cases to run on a {@link TestClient}
+ * node can use. Implementations must be Java beans, that is, to provide a default constructor and to implement the
+ * {@link #getName} method.
+ *
+ * <p/>The methods specified in this interface are called when the {@link TestClient} receives control instructions to
+ * apply to the test. There are control instructions to present the test case with the test invite, so that it may
+ * choose whether or not to participate in the test, assign the test to play the sender or receiver role, start the
+ * test and obtain the test status report.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Supply the name of the test case that this implements.
+ * <tr><td> Accept/Reject invites based on test parameters.
+ * <tr><td> Adapt to assigned roles.
+ * <tr><td> Perform test case actions.
+ * <tr><td> Generate test reports.
+ * </table>
+ */
+public interface TestClientControlledTest
+{
+ /** Defines the possible test case roles that an interop test case can take on. */
+ public enum Roles
+ {
+ /** Specifies the sender role. */
+ SENDER,
+
+ /** Specifies the receivers role. */
+ RECEIVER
+ }
+
+ /**
+ * Should provide the name of the test case that this class implements. The exact names are defined in the
+ * interop testing spec.
+ *
+ * @return The name of the test case that this implements.
+ */
+ public String getName();
+
+ /**
+ * Determines whether the test invite that matched this test case is acceptable.
+ *
+ * @param inviteMessage The invitation to accept or reject.
+ *
+ * @return <tt>true</tt> to accept the invitation, <tt>false</tt> to reject it.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public boolean acceptInvite(Message inviteMessage) throws JMSException;
+
+ /**
+ * Assigns the role to be played by this test case. The test parameters are fully specified in the
+ * assignment message. When this method return the test case will be ready to execute.
+ *
+ * @param role The role to be played; sender or receivers.
+ * @param assignRoleMessage The role assingment message, contains the full test parameters.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void assignRole(Roles role, Message assignRoleMessage) throws JMSException;
+
+ /**
+ * Performs the test case actions. Returning from here, indicates that the sending role has completed its test.
+ *
+ * @param numMessages The number of test messages to send.
+ *
+ * @throws JMSException Any JMSException resulting from reading the message are allowed to fall through.
+ */
+ public void start(int numMessages) throws JMSException;
+
+ /**
+ * Gets a report on the actions performed by the test case in its assigned role.
+ *
+ * @param session The controlSession to create the report message in.
+ *
+ * @return The report message.
+ *
+ * @throws JMSException Any JMSExceptions resulting from creating the report are allowed to fall through.
+ */
+ public Message getReport(Session session) throws JMSException;
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java
new file mode 100644
index 0000000000..014dd21292
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java
@@ -0,0 +1,399 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.listeners;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+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 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.
+ */
+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/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java
new file mode 100644
index 0000000000..010669a7c2
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java
@@ -0,0 +1,452 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.localcircuit;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.test.framework.*;
+
+import uk.co.thebadgerset.junit.extensions.util.ParsedProperties;
+
+import javax.jms.*;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * LocalCircuitImpl provides an implementation of the test circuit. This is a local only circuit implementation that
+ * supports a single producer/consumer on each end of the circuit, with both ends of the circuit on the same JVM.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Supply the publishing and receiving ends of a test messaging circuit.
+ * <td> {@link LocalPublisherImpl}, {@link LocalReceiverImpl}
+ * <tr><td> Start the circuit running.
+ * <tr><td> Close the circuit down.
+ * <tr><td> Take a reading of the circuits state.
+ * <tr><td> Apply assertions against the circuits state. <td> {@link Assertion}
+ * <tr><td> Send test messages over the circuit.
+ * <tr><td> Perform the default test procedure on the circuit.
+ * <tr><td> Provide access to connection and controlSession exception monitors <td> {@link ExceptionMonitor}
+ * </table>
+ */
+public class LocalCircuitImpl implements Circuit
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(LocalCircuitImpl.class);
+
+ /** Used to create unique destination names for each test. */
+ private static AtomicLong uniqueDestsId = new AtomicLong();
+
+ /** Holds the test configuration for the circuit. */
+ private ParsedProperties testProps;
+
+ /** Holds the publishing end of the circuit. */
+ private LocalPublisherImpl publisher;
+
+ /** Holds the receiving end of the circuit. */
+ private LocalReceiverImpl receiver;
+
+ /** Holds the connection for the publishing end of the circuit. */
+ private Connection connection;
+
+ /** Holds the exception listener for the connection on the publishing end of the circuit. */
+ private ExceptionMonitor connectionExceptionMonitor;
+
+ /** Holds the exception listener for the controlSession on the publishing end of the circuit. */
+ private ExceptionMonitor exceptionMonitor;
+
+ /**
+ * Creates a test circuit using the specified test parameters. The publisher, receivers, connection and
+ * connection monitor must already have been created, to assemble the circuit.
+ *
+ * @param testProps The test parameters.
+ * @param publisher The test publisher.
+ * @param receiver The test receivers.
+ * @param connection The connection.
+ * @param connectionExceptionMonitor The connection exception monitor.
+ */
+ protected LocalCircuitImpl(ParsedProperties testProps, LocalPublisherImpl publisher, LocalReceiverImpl receiver,
+ Connection connection, ExceptionMonitor connectionExceptionMonitor)
+ {
+ this.testProps = testProps;
+ this.publisher = publisher;
+ this.receiver = receiver;
+ this.connection = connection;
+ this.connectionExceptionMonitor = connectionExceptionMonitor;
+ this.exceptionMonitor = new ExceptionMonitor();
+
+ // Set this as the parent circuit on the publisher and receivers.
+ publisher.setCircuit(this);
+ receiver.setCircuit(this);
+ }
+
+ /**
+ * Creates a local test circuit from the specified test parameters.
+ *
+ * @param testProps The test parameters.
+ *
+ * @return A connected and ready to start, test circuit.
+ */
+ public static Circuit createCircuit(ParsedProperties testProps)
+ {
+ // Create a standard publisher/receivers test client pair on a shared connection, individual sessions.
+ try
+ {
+ // Cast the test properties into a typed interface for convenience.
+ MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
+
+ // Get a unique offset to append to destination names to make them unique to the connection.
+ long uniqueId = uniqueDestsId.incrementAndGet();
+
+ // Set up the connection.
+ Connection connection = TestUtils.createConnection(testProps);
+
+ // Add the connection exception listener to assert on exception conditions with.
+ // ExceptionMonitor exceptionMonitor = new ExceptionMonitor();
+ // connection.setExceptionListener(exceptionMonitor);
+
+ // Set up the publisher.
+ CircuitEndBase publisherEnd = createPublisherCircuitEnd(connection, props, uniqueId);
+
+ // Set up the receiver.
+ CircuitEndBase receiverEnd = createReceiverCircuitEnd(connection, props, uniqueId);
+
+ // Start listening for incoming messages.
+ connection.start();
+
+ // Package everything up.
+ LocalPublisherImpl publisher = new LocalPublisherImpl(publisherEnd);
+ LocalReceiverImpl receiver = new LocalReceiverImpl(receiverEnd);
+
+ return new LocalCircuitImpl(testProps, publisher, receiver, connection, publisher.getExceptionMonitor());
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e);
+ }
+ }
+
+ /**
+ * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters.
+ *
+ * @param connection The connection to build the circuit end on.
+ * @param testProps The test parameters to configure the circuit end construction.
+ * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique.
+ *
+ * @return A circuit end suitable for the publishing side of a test circuit.
+ *
+ * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation.
+ */
+ public static CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
+ throws JMSException
+ {
+ log.debug(
+ "public static CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = "
+ + uniqueId + "): called");
+
+ // Cast the test properties into a typed interface for convenience.
+ MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
+
+ Session session = connection.createSession(props.getTransacted(), props.getAckMode());
+
+ Destination destination =
+ props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId)
+ : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId);
+
+ MessageProducer producer =
+ props.getPublisherProducerBind()
+ ? ((props.getImmediate() | props.getMandatory())
+ ? ((AMQSession) session).createProducer(destination, props.getMandatory(), props.getImmediate())
+ : session.createProducer(destination)) : null;
+
+ MessageConsumer consumer =
+ props.getPublisherConsumerBind()
+ ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null;
+
+ MessageMonitor messageMonitor = new MessageMonitor();
+
+ if (consumer != null)
+ {
+ consumer.setMessageListener(messageMonitor);
+ }
+
+ ExceptionMonitor exceptionMonitor = new ExceptionMonitor();
+ connection.setExceptionListener(exceptionMonitor);
+
+ if (!props.getPublisherConsumerActive() && (consumer != null))
+ {
+ consumer.close();
+ }
+
+ return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor);
+ }
+
+ /**
+ * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters.
+ *
+ * @param connection The connection to build the circuit end on.
+ * @param testProps The test parameters to configure the circuit end construction.
+ * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique.
+ *
+ * @return A circuit end suitable for the receiving side of a test circuit.
+ *
+ * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation.
+ */
+ public static CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId)
+ throws JMSException
+ {
+ log.debug(
+ "public static CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = "
+ + uniqueId + "): called");
+
+ // Cast the test properties into a typed interface for convenience.
+ MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps);
+
+ Session session = connection.createSession(props.getTransacted(), props.getAckMode());
+
+ MessageProducer producer =
+ props.getReceiverProducerBind()
+ ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null;
+
+ Destination destination =
+ props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId)
+ : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId);
+
+ MessageConsumer consumer =
+ props.getReceiverConsumerBind()
+ ? ((props.getDurableSubscription() && props.getPubsub())
+ ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination))
+ : null;
+
+ MessageMonitor messageMonitor = new MessageMonitor();
+
+ if (consumer != null)
+ {
+ consumer.setMessageListener(messageMonitor);
+ }
+
+ if (!props.getReceiverConsumerActive() && (consumer != null))
+ {
+ consumer.close();
+ }
+
+ return new CircuitEndBase(producer, consumer, session, messageMonitor, null);
+ }
+
+ /**
+ * Gets the interface on the publishing end of the circuit.
+ *
+ * @return The publishing end of the circuit.
+ */
+ public Publisher getPublisher()
+ {
+ return publisher;
+ }
+
+ /**
+ * Gets the local publishing circuit end, for direct manipulation.
+ *
+ * @return The local publishing circuit end.
+ */
+ public CircuitEnd getLocalPublisherCircuitEnd()
+ {
+ return publisher;
+ }
+
+ /**
+ * Gets the interface on the receiving end of the circuit.
+ *
+ * @return The receiving end of the circuit.
+ */
+ public Receiver getReceiver()
+ {
+ return receiver;
+ }
+
+ /**
+ * Gets the local receiving circuit end, for direct manipulation.
+ *
+ * @return The local receiving circuit end.
+ */
+ public CircuitEnd getLocalReceiverCircuitEnd()
+ {
+ return receiver;
+ }
+
+ /**
+ * Checks the test circuit. The effect of this is to gather the circuits state, for both ends of the circuit,
+ * into a report, against which assertions may be checked.
+ */
+ public void check()
+ { }
+
+ /**
+ * Applied a list of assertions against the test circuit. The {@link #check()} method should be called before doing
+ * this, to ensure that the circuit has gathered its state into a report to assert against.
+ *
+ * @param assertions The list of assertions to apply.
+ * @return Any assertions that failed.
+ */
+ public List<Assertion> applyAssertions(List<Assertion> assertions)
+ {
+ List<Assertion> failures = new LinkedList<Assertion>();
+
+ for (Assertion assertion : assertions)
+ {
+ if (!assertion.apply())
+ {
+ failures.add(assertion);
+ }
+ }
+
+ return failures;
+ }
+
+ /**
+ * Connects and starts the circuit. After this method is called the circuit is ready to send messages.
+ */
+ public void start()
+ { }
+
+ /**
+ * Closes the circuit. All associated resources are closed.
+ */
+ public void close()
+ {
+ try
+ {
+ publisher.close();
+ receiver.close();
+ connection.close();
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Got JMSException during close:" + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Sends a message on the test circuit. The exact nature of the message sent is controlled by the test parameters.
+ */
+ protected void send()
+ {
+ boolean transactional = testProps.getPropertyAsBoolean(MessagingTestConfigProperties.TRANSACTED_PROPNAME);
+
+ // Send an immediate message through the publisher and ensure that it results in a JMSException.
+ try
+ {
+ CircuitEnd end = getLocalPublisherCircuitEnd();
+
+ end.send(createTestMessage(end));
+
+ if (transactional)
+ {
+ end.getSession().commit();
+ }
+ }
+ catch (JMSException e)
+ {
+ exceptionMonitor.onException(e);
+ }
+ }
+
+ /**
+ * Runs the default test procedure against the circuit, and checks that all of the specified assertions hold. The
+ * outline of the default test procedure is:
+ *
+ * <p/><pre>
+ * Start the circuit.
+ * Send test messages.
+ * Request a status report.
+ * Assert conditions on the publishing end of the circuit.
+ * Assert conditions on the receiving end of the circuit.
+ * Close the circuit.
+ * Pass with no failed assertions or fail with a list of failed assertions.
+ * </pre>
+ *
+ * @param numMessages The number of messages to send using the default test procedure.
+ * @param assertions The list of assertions to apply.
+ * @return Any assertions that failed.
+ */
+ public List<Assertion> test(int numMessages, List<Assertion> assertions)
+ {
+ // Start the test circuit.
+ start();
+
+ // Send the requested number of test messages.
+ for (int i = 0; i < numMessages; i++)
+ {
+ send();
+ }
+
+ // Inject a short pause to allow time for exceptions to come back asynchronously.
+ TestUtils.pause(500L);
+
+ // Request a status report.
+ check();
+
+ // Clean up the publisher/receivers/controlSession/connections.
+ close();
+
+ // Apply all of the requested assertions, keeping record of any that fail.
+ List<Assertion> failures = applyAssertions(assertions);
+
+ // Return any failed assertions to the caller.
+ return failures;
+ }
+
+ /**
+ * Creates a message with the properties defined as per the test parameters.
+ *
+ * @param client The circuit end to create the message on.
+ *
+ * @return The test message.
+ *
+ * @throws JMSException Any JMSException occurring during creation of the message is allowed to fall through.
+ */
+ private Message createTestMessage(CircuitEnd client) throws JMSException
+ {
+ return client.getSession().createTextMessage("Hello");
+ }
+
+ /**
+ * Gets the exception monitor for the publishing ends connection.
+ *
+ * @return The exception monitor for the publishing ends connection.
+ */
+ public ExceptionMonitor getConnectionExceptionMonitor()
+ {
+ return connectionExceptionMonitor;
+ }
+
+ /**
+ * Gets the exception monitor for the publishing ends controlSession.
+ *
+ * @return The exception monitor for the publishing ends controlSession.
+ */
+ public ExceptionMonitor getExceptionMonitor()
+ {
+ return exceptionMonitor;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java
new file mode 100644
index 0000000000..93a137dffa
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java
@@ -0,0 +1,180 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.localcircuit;
+
+import org.apache.qpid.client.AMQNoConsumersException;
+import org.apache.qpid.client.AMQNoRouteException;
+import org.apache.qpid.test.framework.*;
+
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+/**
+ * Provides an implementation of the {@link Publisher} interface and wraps a single message producer and consumer on
+ * a single controlSession, as a {@link CircuitEnd}. A local publisher also acts as a circuit end, because for a locally
+ * located circuit the assertions may be applied directly, there does not need to be any inter process messaging
+ * between the publisher and its single circuit end, in order to ascertain its status.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a message producer for sending messages.
+ * <tr><td> Provide a message consumer for receiving messages.
+ * <tr><td> Provide assertion that the publisher received no exceptions.
+ * <tr><td> Provide assertion that the publisher received a no consumers error code.
+ * <tr><td> Provide assertion that the publisher received a no route error code.
+ * </table>
+ */
+public class LocalPublisherImpl extends CircuitEndBase implements Publisher
+{
+ /** Holds a reference to the containing circuit. */
+ private LocalCircuitImpl circuit;
+
+ /**
+ * Creates a circuit end point on the specified producer, consumer and controlSession.
+ *
+ * @param producer The message producer for the circuit end point.
+ * @param consumer The message consumer for the circuit end point.
+ * @param session The controlSession for the circuit end point.
+ */
+ public LocalPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session,
+ MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor)
+ {
+ super(producer, consumer, session, messageMonitor, exceptionMonitor);
+ }
+
+ /**
+ * Creates a circuit end point from the producer, consumer and controlSession in a circuit end base implementation.
+ *
+ * @param end The circuit end base implementation to take producers and consumers from.
+ */
+ public LocalPublisherImpl(CircuitEndBase end)
+ {
+ super(end.getProducer(), end.getConsumer(), end.getSession(), end.getMessageMonitor(), end.getExceptionMonitor());
+ }
+
+ /**
+ * Provides an assertion that the publisher encountered no exceptions.
+ *
+ * @return An assertion that the publisher encountered no exceptions.
+ */
+ public Assertion noExceptionsAssertion()
+ {
+ return new AssertionBase()
+ {
+ public boolean apply()
+ {
+ boolean passed = true;
+ ExceptionMonitor sessionExceptionMonitor = circuit.getExceptionMonitor();
+ ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor();
+
+ if (!connectionExceptionMonitor.assertNoExceptions())
+ {
+ passed = false;
+
+ addError("Was expecting no exceptions.\n");
+ addError("Got the following exceptions on the connection, "
+ + circuit.getConnectionExceptionMonitor());
+ }
+
+ if (!sessionExceptionMonitor.assertNoExceptions())
+ {
+ passed = false;
+
+ addError("Was expecting no exceptions.\n");
+ addError("Got the following exceptions on the producer, " + circuit.getExceptionMonitor());
+ }
+
+ return passed;
+ }
+ };
+ }
+
+ /**
+ * Provides an assertion that the publisher got a no consumers exception on every message.
+ *
+ * @return An assertion that the publisher got a no consumers exception on every message.
+ */
+ public Assertion noConsumersAssertion()
+ {
+ return new AssertionBase()
+ {
+ public boolean apply()
+ {
+ boolean passed = true;
+ ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor();
+
+ if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoConsumersException.class))
+ {
+ passed = false;
+
+ addError("Was expecting linked exception type " + AMQNoConsumersException.class.getName()
+ + " on the connection.\n");
+ addError((connectionExceptionMonitor.size() > 0)
+ ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor)
+ : "Got no exceptions on the connection.");
+ }
+
+ return passed;
+ }
+ };
+ }
+
+ /**
+ * Provides an assertion that the publisher got a no rout exception on every message.
+ *
+ * @return An assertion that the publisher got a no rout exception on every message.
+ */
+ public Assertion noRouteAssertion()
+ {
+ return new AssertionBase()
+ {
+ public boolean apply()
+ {
+ boolean passed = true;
+ ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor();
+
+ if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class))
+ {
+ passed = false;
+
+ addError("Was expecting linked exception type " + AMQNoRouteException.class.getName()
+ + " on the connection.\n");
+ addError((connectionExceptionMonitor.size() > 0)
+ ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor)
+ : "Got no exceptions on the connection.");
+ }
+
+ return passed;
+ }
+ };
+ }
+
+ /**
+ * Sets the contianing circuit.
+ *
+ * @param circuit The containing circuit.
+ */
+ public void setCircuit(LocalCircuitImpl circuit)
+ {
+ this.circuit = circuit;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java
new file mode 100644
index 0000000000..1b5acf92de
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java
@@ -0,0 +1,100 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.localcircuit;
+
+import org.apache.qpid.test.framework.*;
+
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+/**
+ * Provides an implementation of the {@link Receiver} interface that wraps a single message producer and consumer on
+ * a single controlSession, as a {@link CircuitEnd}. A local receiver also acts as a circuit end, because for a locally
+ * located circuit the assertions may be applied directly, there does not need to be any inter process messaging
+ * between the publisher and its single circuit end, in order to ascertain its status.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a message producer for sending messages.
+ * <tr><td> Provide a message consumer for receiving messages.
+ * <tr><td> Provide assertion that the receivers received no exceptions.
+ * <tr><td> Provide assertion that the receivers received all test messages sent to it.
+ * </table>
+ */
+public class LocalReceiverImpl extends CircuitEndBase implements Receiver
+{
+ /** Holds a reference to the containing circuit. */
+ private LocalCircuitImpl circuit;
+
+ /**
+ * Creates a circuit end point on the specified producer, consumer and controlSession.
+ *
+ * @param producer The message producer for the circuit end point.
+ * @param consumer The message consumer for the circuit end point.
+ * @param session The controlSession for the circuit end point.
+ */
+ public LocalReceiverImpl(MessageProducer producer, MessageConsumer consumer, Session session,
+ MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor)
+ {
+ super(producer, consumer, session, messageMonitor, exceptionMonitor);
+ }
+
+ /**
+ * Creates a circuit end point from the producer, consumer and controlSession in a circuit end base implementation.
+ *
+ * @param end The circuit end base implementation to take producers and consumers from.
+ */
+ public LocalReceiverImpl(CircuitEndBase end)
+ {
+ super(end.getProducer(), end.getConsumer(), end.getSession(), end.getMessageMonitor(), end.getExceptionMonitor());
+ }
+
+ /**
+ * Provides an assertion that the receivers encountered no exceptions.
+ *
+ * @return An assertion that the receivers encountered no exceptions.
+ */
+ public Assertion noExceptionsAssertion()
+ {
+ return null;
+ }
+
+ /**
+ * Provides an assertion that the receivers got all messages that were sent to it.
+ *
+ * @return An assertion that the receivers got all messages that were sent to it.
+ */
+ public Assertion allMessagesAssertion()
+ {
+ return null;
+ }
+
+ /**
+ * Sets the contianing circuit.
+ *
+ * @param circuit The containing circuit.
+ */
+ public void setCircuit(LocalCircuitImpl circuit)
+ {
+ this.circuit = circuit;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/package.html b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/package.html
new file mode 100644
index 0000000000..f07a5118e7
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/package.html
@@ -0,0 +1,22 @@
+<html>
+<body>
+<p/>A framework for testing Qpid, built around a standard 'test circuit' design. The idea behind this framework is the
+use of a test circuit which is configured by a set of test parameters, that may be projected onto a topology of
+test nodes, with tests scripted to run over test circuits, making as few assumptions as possible about the underlying
+topology. The standardization of the design, whilst limiting in some respectes, allows a large variety of test
+scenarios to be written with minimal amounts of coding.
+
+<p/>The standard consruction block for a test, is a test circuit. This consists of a publisher, and a receiver. The
+publisher and receiver may reside on the same machine, or may be distributed. Will use a standard set of properties to
+define the desired circuit topology.
+
+<p/>Tests are always to be controlled from the publishing side only. The receiving end of the circuit is to be exposed
+to the test code through an interface, that abstracts as much as possible the receiving end of the test. The interface
+exposes a set of 'assertions' that may be applied to the receiving end of the test circuit.
+
+<p/>In the case where the receiving end of the circuit resides on the same JVM, the assertions will call the receivers
+code locally. Where the receiving end is distributed accross one or more machines, the assertions will be applied to a
+test report gethered from all of the receivers. Test code will be written to the assertions making as few assumptions
+as possible about the exact test topology.
+</body>
+</html> \ No newline at end of file
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java
new file mode 100644
index 0000000000..555071760e
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java
@@ -0,0 +1,128 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.sequencers;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.Circuit;
+import org.apache.qpid.test.framework.TestClientDetails;
+import org.apache.qpid.util.ConversationFactory;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+public abstract class BaseCircuitFactory implements CircuitFactory
+{
+ /** Used for debugging. */
+ private final Logger log = Logger.getLogger(BaseCircuitFactory.class);
+
+ /** Holds the contact details for the sending test client. */
+ protected TestClientDetails sender;
+
+ /** Holds the contact details for the receving test client. */
+ protected List<TestClientDetails> receivers = new LinkedList<TestClientDetails>();
+
+ /** Holds the conversation factory over which to coordinate the test. */
+ protected ConversationFactory conversationFactory;
+
+ /**
+ * Creates a test circuit for the test, configered by the test parameters specified.
+ *
+ * @param testProperties The test parameters.
+ * @return A test circuit.
+ */
+ public Circuit createCircuit(Properties testProperties)
+ {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ /**
+ * Sets the sender test client to coordinate the test with.
+ *
+ * @param sender The contact details of the sending client in the test.
+ */
+ public void setSender(TestClientDetails sender)
+ {
+ log.debug("public void setSender(TestClientDetails sender = " + sender + "): called");
+
+ this.sender = sender;
+ }
+
+ /**
+ * Sets the receiving test client to coordinate the test with.
+ *
+ * @param receiver The contact details of the sending client in the test.
+ */
+ public void setReceiver(TestClientDetails receiver)
+ {
+ log.debug("public void setReceiver(TestClientDetails receivers = " + receiver + "): called");
+
+ this.receivers.add(receiver);
+ }
+
+ /**
+ * Supplies the sending test client.
+ *
+ * @return The sending test client.
+ */
+ public TestClientDetails getSender()
+ {
+ return sender;
+ }
+
+ /**
+ * Supplies the receiving test client.
+ *
+ * @return The receiving test client.
+ */
+ public List<TestClientDetails> getReceivers()
+ {
+ return receivers;
+ }
+
+ /**
+ * Accepts the conversation factory over which to hold the test coordinating conversation.
+ *
+ * @param conversationFactory The conversation factory to coordinate the test over.
+ */
+ public void setConversationFactory(ConversationFactory conversationFactory)
+ {
+ this.conversationFactory = conversationFactory;
+ }
+
+ /**
+ * Provides the conversation factory for providing the distributed test sequencing conversations over the test
+ * connection.
+ *
+ * @return The conversation factory to create test sequencing conversations with.
+ */
+ public ConversationFactory getConversationFactory()
+ {
+ return conversationFactory;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java
new file mode 100644
index 0000000000..82caa20d79
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.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.framework.sequencers;
+
+import org.apache.qpid.test.framework.Assertion;
+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 java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * A TestCaseSequence is responsibile for creating test circuits appropriate to the context that a test case is
+ * running in, and providing an implementation of a standard test procedure over a test circuit.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Provide a standard test procedure over a test circuit.
+ * <tr><td> Construct test circuits appropriate to a tests context.
+ * </table>
+ *
+ * @todo The sequence test method is deprecated, in favour of using test circuits instead. This interface might be
+ * better renamed to somethign like CircuitFactory, also the split between this interface and
+ * DistributedTestSequencer could be removed and DistributedTestCase functionality merged into FrameworkBaseCase.
+ * This is so that any test case written on top of the framework base case can be distributed, without having
+ * to extend a different base test class.
+ */
+public interface CircuitFactory
+{
+ /**
+ * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles,
+ * begining the test, gathering the test reports from the participants, and checking for assertion failures against
+ * the test reports.
+ *
+ * @param testCircuit The test circuit.
+ * @param assertions The list of assertions to apply to the test circuit.
+ * @param testProperties The test case definition.
+ *
+ * @deprecated Use test circuits and Circuit.test instead.
+ */
+ public void sequenceTest(Circuit testCircuit, List<Assertion> assertions, Properties testProperties);
+
+ /**
+ * Creates a test circuit for the test, configered by the test parameters specified.
+ *
+ * @param testProperties The test parameters.
+ *
+ * @return A test circuit.
+ */
+ public Circuit createCircuit(ParsedProperties testProperties);
+
+ /**
+ * Sets the sender test client to coordinate the test with.
+ *
+ * @param sender The contact details of the sending client in the test.
+ */
+ public void setSender(TestClientDetails sender);
+
+ /**
+ * Sets the receiving test client to coordinate the test with.
+ *
+ * @param receiver The contact details of the sending client in the test.
+ */
+ public void setReceiver(TestClientDetails receiver);
+
+ /**
+ * Supplies the sending test client.
+ *
+ * @return The sending test client.
+ */
+ public TestClientDetails getSender();
+
+ /**
+ * Supplies the receiving test client.
+ *
+ * @return The receiving test client.
+ */
+ public List<TestClientDetails> getReceivers();
+
+ /**
+ * Accepts the conversation factory over which to hold the test coordinating conversation.
+ *
+ * @param conversationFactory The conversation factory to coordinate the test over.
+ */
+ public void setConversationFactory(ConversationFactory conversationFactory);
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java
new file mode 100644
index 0000000000..673b47df03
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java
@@ -0,0 +1,201 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.framework.sequencers;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.Assertion;
+import org.apache.qpid.test.framework.Circuit;
+import org.apache.qpid.test.framework.TestClientDetails;
+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 javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Session;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * FanOutCircuitFactory is a circuit factory that creates distributed test circuits. Given a set of participating
+ * test client nodes, it assigns one node to the SENDER role and the remainder to the RECEIVER role.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ *
+ * @todo Adapt this to be an n*m topology circuit factory. Need to add circuit topology definitions to the test
+ * parameters. Place n senders onto the available test clients, and m receivers. Where n or m is larger than
+ * the available nodes, start stacking multiple test clients on each node. There will also be an option that
+ * indicates whether nodes can play both roles, and how many nodes out of all available may be assigned to
+ * each role.
+ *
+ * @todo The createCircuit methods on this and InteropCircuitFactory are going to be identical. This is because the
+ * partitioning into senders and receivers is already done by the test decorators. Either eliminate these factories
+ * as unnesesary, or move the partitioning functionaility into the factories, in which case the test decorators
+ * can probably be merged or eliminated. There is confusion over the placement of responsibilities between the
+ * factories and the test decorators... although the test decorators may well do more than just circuit creation
+ * in the future. For example, there may have to be a special decorator for test repetition that does one circuit
+ * creation, but the runs many tests over it, in which case the handling of responsibilities becomes clearer.
+ */
+public class FanOutCircuitFactory extends BaseCircuitFactory
+{
+ /** Used for debugging. */
+ Logger log = Logger.getLogger(FanOutCircuitFactory.class);
+
+ /**
+ * Creates a test circuit for the test, configered by the test parameters specified.
+ *
+ * @param testProperties The test parameters.
+ * @return A test circuit.
+ */
+ public Circuit createCircuit(ParsedProperties testProperties)
+ {
+ log.debug("public Circuit createCircuit(ParsedProperties testProperties): called");
+
+ List<TestClientDetails> senders = new LinkedList<TestClientDetails>();
+ senders.add(getSender());
+ List<TestClientDetails> receivers = getReceivers();
+ ConversationFactory conversationFactory = getConversationFactory();
+
+ return DistributedCircuitImpl.createCircuit(testProperties, senders, receivers, conversationFactory);
+ }
+
+ /**
+ * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles,
+ * begining the test, gathering the test reports from the participants, and checking for assertion failures against
+ * the test reports.
+ *
+ * @param testCircuit The test circuit.
+ * @param assertions The list of assertions to apply to the test circuit.
+ * @param testProperties The test case definition.
+ *
+ * @deprecated Scheduled for removal once existing tests converted over to use test circuits.
+ */
+ public void sequenceTest(Circuit testCircuit, List<Assertion> assertions, Properties testProperties)
+ {
+ log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called");
+
+ TestClientDetails sender = getSender();
+ List<TestClientDetails> receivers = getReceivers();
+ ConversationFactory conversationFactory = getConversationFactory();
+
+ try
+ {
+ // Create a conversation on the sender clients private control route.
+ Session session = conversationFactory.getSession();
+ Destination senderControlTopic = session.createTopic(sender.privateControlKey);
+ ConversationFactory.Conversation senderConversation = conversationFactory.startConversation();
+
+ // Assign the sender role to the sending test client.
+ Message assignSender = conversationFactory.getSession().createMessage();
+ TestUtils.setPropertiesOnMessage(assignSender, testProperties);
+ assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+ assignSender.setStringProperty("ROLE", "SENDER");
+ assignSender.setStringProperty("CLIENT_NAME", "Sustained_SENDER");
+
+ senderConversation.send(senderControlTopic, assignSender);
+
+ // Wait for the sender to confirm its role.
+ senderConversation.receive();
+
+ // Assign the receivers roles.
+ for (TestClientDetails receiver : receivers)
+ {
+ assignReceiverRole(receiver, testProperties, true);
+ }
+
+ // Start the test on the sender.
+ Message start = session.createMessage();
+ start.setStringProperty("CONTROL_TYPE", "START");
+
+ senderConversation.send(senderControlTopic, start);
+
+ // Wait for the test sender to return its report.
+ Message senderReport = senderConversation.receive();
+ TestUtils.pause(500);
+
+ // Ask the receivers for their reports.
+ Message statusRequest = session.createMessage();
+ statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST");
+
+ // Gather the reports from all of the receiving clients.
+
+ // Return all of the test reports, the senders report first.
+ // return new Message[] { senderReport };
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("Unhandled JMSException.");
+ }
+ }
+
+ /**
+ * Assigns the receivers role to the specified test client that is to act as a receivers during the test. This method
+ * does not always wait for the receiving clients to confirm their role assignments. This is because this method
+ * may be called from an 'onMessage' method, when a client is joining the test at a later point in time, and it
+ * is not possible to do a synchronous receive during an 'onMessage' method. There is a flag to indicate whether
+ * or not to wait for role confirmations.
+ *
+ * @param receiver The test client to assign the receivers role to.
+ * @param testProperties The test parameters.
+ * @param confirm Indicates whether role confirmation should be waited for.
+ *
+ * @throws JMSException Any JMSExceptions occurring during the conversation are allowed to fall through.
+ *
+ * @deprecated Scheduled for removal once existing tests converted over to use test circuits.
+ */
+ protected void assignReceiverRole(TestClientDetails receiver, Properties testProperties, boolean confirm)
+ throws JMSException
+ {
+ log.info("assignReceiverRole(TestClientDetails receivers = " + receiver + ", Map<String, Object> testProperties = "
+ + testProperties + "): called");
+
+ ConversationFactory conversationFactory = getConversationFactory();
+
+ // Create a conversation with the receiving test client.
+ Session session = conversationFactory.getSession();
+ Destination receiverControlTopic = session.createTopic(receiver.privateControlKey);
+ ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation();
+
+ // Assign the receivers role to the receiving client.
+ Message assignReceiver = session.createMessage();
+ TestUtils.setPropertiesOnMessage(assignReceiver, testProperties);
+ assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+ assignReceiver.setStringProperty("ROLE", "RECEIVER");
+ assignReceiver.setStringProperty("CLIENT_NAME", receiver.clientName);
+
+ receiverConversation.send(receiverControlTopic, assignReceiver);
+
+ // Wait for the role confirmation to come back.
+ if (confirm)
+ {
+ receiverConversation.receive();
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java
new file mode 100644
index 0000000000..35bcf78b72
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.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.test.framework.sequencers;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.test.framework.Assertion;
+import org.apache.qpid.test.framework.Circuit;
+import org.apache.qpid.test.framework.TestClientDetails;
+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 javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.Session;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td>
+ * </table>
+ */
+public class InteropCircuitFactory extends BaseCircuitFactory
+{
+ /** Used for debugging. */
+ Logger log = Logger.getLogger(InteropCircuitFactory.class);
+
+ /**
+ * Creates a test circuit for the test, configered by the test parameters specified.
+ *
+ * @param testProperties The test parameters.
+ * @return A test circuit.
+ */
+ public Circuit createCircuit(ParsedProperties testProperties)
+ {
+ log.debug("public Circuit createCircuit(ParsedProperties testProperties): called");
+
+ List<TestClientDetails> senders = new LinkedList<TestClientDetails>();
+ senders.add(getSender());
+ List<TestClientDetails> receivers = getReceivers();
+ ConversationFactory conversationFactory = getConversationFactory();
+
+ return DistributedCircuitImpl.createCircuit(testProperties, senders, receivers, conversationFactory);
+ }
+
+ /**
+ * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles,
+ * begining the test, gathering the test reports from the participants, and checking for assertion failures against
+ * the test reports.
+ *
+ * @param testCircuit The test circuit.
+ * @param assertions The list of assertions to apply to the test circuit.
+ * @param testProperties The test case definition.
+ */
+ public void sequenceTest(Circuit testCircuit, List<Assertion> assertions, Properties testProperties)
+ {
+ log.debug("protected Message[] sequenceTest(Object... testProperties = " + testProperties + "): called");
+
+ TestClientDetails sender = getSender();
+ List<TestClientDetails> receivers = getReceivers();
+ ConversationFactory conversationFactory = getConversationFactory();
+
+ try
+ {
+ Session session = conversationFactory.getSession();
+ Destination senderControlTopic = session.createTopic(sender.privateControlKey);
+ Destination receiverControlTopic = session.createTopic(receivers.get(0).privateControlKey);
+
+ ConversationFactory.Conversation senderConversation = conversationFactory.startConversation();
+ ConversationFactory.Conversation receiverConversation = conversationFactory.startConversation();
+
+ Message assignSender = conversationFactory.getSession().createMessage();
+ TestUtils.setPropertiesOnMessage(assignSender, testProperties);
+ assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+ assignSender.setStringProperty("ROLE", "SENDER");
+
+ senderConversation.send(senderControlTopic, assignSender);
+
+ // Assign the receivers role the receiving client.
+ Message assignReceiver = session.createMessage();
+ TestUtils.setPropertiesOnMessage(assignReceiver, testProperties);
+ assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+ assignReceiver.setStringProperty("ROLE", "RECEIVER");
+
+ receiverConversation.send(receiverControlTopic, assignReceiver);
+
+ // Wait for the senders and receivers to confirm their roles.
+ senderConversation.receive();
+ receiverConversation.receive();
+
+ // Start the test.
+ Message start = session.createMessage();
+ start.setStringProperty("CONTROL_TYPE", "START");
+
+ senderConversation.send(senderControlTopic, start);
+
+ // Wait for the test sender to return its report.
+ Message senderReport = senderConversation.receive();
+ TestUtils.pause(500);
+
+ // Ask the receivers for its report.
+ Message statusRequest = session.createMessage();
+ statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST");
+
+ receiverConversation.send(receiverControlTopic, statusRequest);
+
+ // Wait for the receivers to send its report.
+ Message receiverReport = receiverConversation.receive();
+
+ // return new Message[] { senderReport, receiverReport };
+
+ // Apply assertions.
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException("JMSException not handled.");
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java b/Final/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java
new file mode 100644
index 0000000000..398e3e7800
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java
@@ -0,0 +1,296 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.testutil;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQConnectionFactory;
+import org.apache.qpid.client.AMQConnectionURL;
+import org.apache.qpid.client.JMSAMQException;
+import org.apache.qpid.url.URLSyntaxException;
+
+import javax.jms.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+/**
+ * @todo This was originally cut and paste from the client module leading to a duplicate class, then altered very
+ * slightly. To avoid the duplicate class the name was altered slightly to have 'Helper' on the end in order
+ * to distinguish it from the original. Delete this class and use the original instead, just upgrade it to
+ * provide the new features needed.
+ */
+public class QpidClientConnectionHelper implements ExceptionListener
+{
+
+ private static final Logger _logger = Logger.getLogger(QpidClientConnectionHelper.class);
+
+ private boolean transacted = true;
+ private int ackMode = Session.CLIENT_ACKNOWLEDGE;
+ private Connection connection;
+
+ private String virtualHost;
+ private String brokerlist;
+ private int prefetch;
+ protected Session session;
+ protected boolean connected;
+
+ public QpidClientConnectionHelper(String broker)
+ {
+ super();
+ setVirtualHost("/test");
+ setBrokerList(broker);
+ setPrefetch(5000);
+ }
+
+ public void connect() throws JMSException
+ {
+ if (!connected)
+ {
+ /*
+ * amqp://[user:pass@][clientid]/virtualhost?
+ * brokerlist='[transport://]host[:port][?option='value'[&option='value']];'
+ * [&failover='method[?option='value'[&option='value']]']
+ * [&option='value']"
+ */
+ String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'";
+ try
+ {
+ AMQConnectionFactory factory = new AMQConnectionFactory(new AMQConnectionURL(brokerUrl));
+ _logger.info("connecting to Qpid :" + brokerUrl);
+ connection = factory.createConnection();
+
+ // register exception listener
+ connection.setExceptionListener(this);
+
+ session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch);
+
+ _logger.info("starting connection");
+ connection.start();
+
+ connected = true;
+ }
+ catch (URLSyntaxException e)
+ {
+ throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ public void disconnect() throws JMSException
+ {
+ if (connected)
+ {
+ session.commit();
+ session.close();
+ connection.close();
+ connected = false;
+ _logger.info("disconnected");
+ }
+ }
+
+ public void disconnectWithoutCommit() throws JMSException
+ {
+ if (connected)
+ {
+ session.close();
+ connection.close();
+ connected = false;
+ _logger.info("disconnected without commit");
+ }
+ }
+
+ public String getBrokerList()
+ {
+ return brokerlist;
+ }
+
+ public void setBrokerList(String brokerlist)
+ {
+ this.brokerlist = brokerlist;
+ }
+
+ public String getVirtualHost()
+ {
+ return virtualHost;
+ }
+
+ public void setVirtualHost(String virtualHost)
+ {
+ this.virtualHost = virtualHost;
+ }
+
+ public void setPrefetch(int prefetch)
+ {
+ this.prefetch = prefetch;
+ }
+
+ /** override as necessary */
+ public void onException(JMSException exception)
+ {
+ _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage());
+ }
+
+ public boolean isConnected()
+ {
+ return connected;
+ }
+
+ public Session getSession()
+ {
+ return session;
+ }
+
+ /**
+ * Put a String as a text messages, repeat n times. A null payload will result in a null message.
+ *
+ * @param queueName The queue name to put to
+ * @param payload the content of the payload
+ * @param copies the number of messages to put
+ *
+ * @throws javax.jms.JMSException any exception that occurs
+ */
+ public void put(String queueName, String payload, int copies, int deliveryMode) throws JMSException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ _logger.info("putting to queue " + queueName);
+ Queue queue = session.createQueue(queueName);
+
+ final MessageProducer sender = session.createProducer(queue);
+
+ sender.setDeliveryMode(deliveryMode);
+
+ for (int i = 0; i < copies; i++)
+ {
+ Message m = session.createTextMessage(payload + i);
+ m.setIntProperty("index", i + 1);
+ sender.send(m);
+ }
+
+ session.commit();
+ sender.close();
+ _logger.info("put " + copies + " copies");
+ }
+
+ /**
+ * GET the top message on a queue. Consumes the message. Accepts timeout value.
+ *
+ * @param queueName The quename to get from
+ * @param readTimeout The timeout to use
+ *
+ * @return the content of the text message if any
+ *
+ * @throws javax.jms.JMSException any exception that occured
+ */
+ public Message getNextMessage(String queueName, long readTimeout) throws JMSException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ Queue queue = session.createQueue(queueName);
+
+ final MessageConsumer consumer = session.createConsumer(queue);
+
+ Message message = consumer.receive(readTimeout);
+ session.commit();
+ consumer.close();
+
+ Message result;
+
+ // all messages we consume should be TextMessages
+ if (message instanceof TextMessage)
+ {
+ result = ((TextMessage) message);
+ }
+ else if (null == message)
+ {
+ result = null;
+ }
+ else
+ {
+ _logger.info("warning: received non-text message");
+ result = message;
+ }
+
+ return result;
+ }
+
+ /**
+ * GET the top message on a queue. Consumes the message.
+ *
+ * @param queueName The Queuename to get from
+ *
+ * @return The string content of the text message, if any received
+ *
+ * @throws javax.jms.JMSException any exception that occurs
+ */
+ public Message getNextMessage(String queueName) throws JMSException
+ {
+ return getNextMessage(queueName, 0);
+ }
+
+ /**
+ * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer.
+ *
+ * @param queueName The Queue name to consume from
+ * @param readTimeout The timeout for each consume
+ *
+ * @throws javax.jms.JMSException Any exception that occurs during the consume
+ * @throws InterruptedException If the consume thread was interrupted during a consume.
+ */
+ public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ _logger.info("consuming queue " + queueName);
+ Queue queue = session.createQueue(queueName);
+
+ final MessageConsumer consumer = session.createConsumer(queue);
+ int messagesReceived = 0;
+
+ _logger.info("consuming...");
+ while ((consumer.receive(readTimeout)) != null)
+ {
+ messagesReceived++;
+ }
+
+ session.commit();
+ consumer.close();
+ _logger.info("consumed: " + messagesReceived);
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/util/ClasspathScanner.java b/Final/java/systests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
new file mode 100644
index 0000000000..bad49060ca
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
@@ -0,0 +1,234 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import java.io.File;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.log4j.Logger;
+
+/**
+ * An ClasspathScanner scans the classpath for classes that implement an interface or extend a base class and have names
+ * that match a regular expression.
+ *
+ * <p/>In order to test whether a class implements an interface or extends a class, the class must be loaded (unless
+ * the class files were to be scanned directly). Using this collector can cause problems when it scans the classpath,
+ * because loading classes will initialize their statics, which in turn may cause undesired side effects. For this
+ * reason, the collector should always be used with a regular expression, through which the class file names are
+ * filtered, and only those that pass this filter will be tested. For example, if you define tests in classes that
+ * end with the keyword "Test" then use the regular expression "Test$" to match this.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Find all classes matching type and name pattern on the classpath.
+ * </table>
+ *
+ * @todo Add logic to scan jars as well as directories.
+ */
+public class ClasspathScanner
+{
+ private static final Logger log = Logger.getLogger(ClasspathScanner.class);
+
+ /**
+ * Scans the classpath and returns all classes that extend a specified class and match a specified name.
+ * There is an flag that can be used to indicate that only Java Beans will be matched (that is, only those classes
+ * that have a default constructor).
+ *
+ * @param matchingClass The class or interface to match.
+ * @param matchingRegexp The regular expression to match against the class name.
+ * @param beanOnly Flag to indicate that onyl classes with default constructors should be matched.
+ *
+ * @return All the classes that match this collector.
+ */
+ public static <T> Collection<Class<? extends T>> getMatches(Class<T> matchingClass, String matchingRegexp,
+ boolean beanOnly)
+ {
+ log.debug("public static <T> Collection<Class<? extends T>> getMatches(Class<T> matchingClass = " + matchingClass
+ + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called");
+
+ // Build a compiled regular expression from the pattern to match.
+ Pattern matchPattern = Pattern.compile(matchingRegexp);
+
+ String classPath = System.getProperty("java.class.path");
+ Map<String, Class<? extends T>> result = new HashMap<String, Class<? extends T>>();
+
+ log.debug("classPath = " + classPath);
+
+ // Find matching classes starting from all roots in the classpath.
+ for (String path : splitClassPath(classPath))
+ {
+ gatherFiles(new File(path), "", result, matchPattern, matchingClass);
+ }
+
+ return result.values();
+ }
+
+ /**
+ * Finds all matching classes rooted at a given location in the file system. If location is a directory it
+ * is recursively examined.
+ *
+ * @param classRoot The root of the current point in the file system being examined.
+ * @param classFileName The name of the current file or directory to examine.
+ * @param result The accumulated mapping from class names to classes that match the scan.
+ *
+ * @todo Recursion ok as file system depth is not likely to exhaust the stack. Might be better to replace with
+ * iteration.
+ */
+ private static <T> void gatherFiles(File classRoot, String classFileName, Map<String, Class<? extends T>> result,
+ Pattern matchPattern, Class<? extends T> matchClass)
+ {
+ log.debug("private static <T> void gatherFiles(File classRoot = " + classRoot + ", String classFileName = "
+ + classFileName + ", Map<String, Class<? extends T>> result, Pattern matchPattern = " + matchPattern
+ + ", Class<? extends T> matchClass = " + matchClass + "): called");
+
+ File thisRoot = new File(classRoot, classFileName);
+
+ // If the current location is a file, check if it is a matching class.
+ if (thisRoot.isFile())
+ {
+ // Check that the file has a matching name.
+ if (matchesName(thisRoot.getName(), matchPattern))
+ {
+ String className = classNameFromFile(thisRoot.getName());
+
+ // Check that the class has matching type.
+ try
+ {
+ Class<?> candidateClass = Class.forName(className);
+
+ Class matchedClass = matchesClass(candidateClass, matchClass);
+
+ if (matchedClass != null)
+ {
+ result.put(className, matchedClass);
+ }
+ }
+ catch (ClassNotFoundException e)
+ {
+ // Ignore this. The matching class could not be loaded.
+ log.debug("Got ClassNotFoundException, ignoring.", e);
+ }
+ }
+
+ return;
+ }
+ // Otherwise the current location is a directory, so examine all of its contents.
+ else
+ {
+ String[] contents = thisRoot.list();
+
+ if (contents != null)
+ {
+ for (String content : contents)
+ {
+ gatherFiles(classRoot, classFileName + File.separatorChar + content, result, matchPattern, matchClass);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if the specified class file name corresponds to a class with name matching the specified regular expression.
+ *
+ * @param classFileName The class file name.
+ * @param matchPattern The regular expression pattern to match.
+ *
+ * @return <tt>true</tt> if the class name matches, <tt>false</tt> otherwise.
+ */
+ private static boolean matchesName(String classFileName, Pattern matchPattern)
+ {
+ String className = classNameFromFile(classFileName);
+ Matcher matcher = matchPattern.matcher(className);
+
+ return matcher.matches();
+ }
+
+ /**
+ * Checks if the specified class to compare extends the base class being scanned for.
+ *
+ * @param matchingClass The base class to match against.
+ * @param toMatch The class to match against the base class.
+ *
+ * @return The class to check, cast as an instance of the class to match if the class extends the base class, or
+ * <tt>null</tt> otherwise.
+ */
+ private static <T> Class<? extends T> matchesClass(Class<?> matchingClass, Class<? extends T> toMatch)
+ {
+ try
+ {
+ return matchingClass.asSubclass(toMatch);
+ }
+ catch (ClassCastException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Takes a classpath (which is a series of paths) and splits it into its component paths.
+ *
+ * @param classPath The classpath to split.
+ *
+ * @return A list of the component paths that make up the class path.
+ */
+ private static List<String> splitClassPath(String classPath)
+ {
+ List<String> result = new LinkedList<String>();
+ String separator = System.getProperty("path.separator");
+ StringTokenizer tokenizer = new StringTokenizer(classPath, separator);
+
+ while (tokenizer.hasMoreTokens())
+ {
+ result.add(tokenizer.nextToken());
+ }
+
+ return result;
+ }
+
+ /**
+ * Translates from the filename of a class to its fully qualified classname. Files are named using forward slash
+ * seperators and end in ".class", whereas fully qualified class names use "." sperators and no ".class" ending.
+ *
+ * @param classFileName The filename of the class to translate to a class name.
+ *
+ * @return The fully qualified class name.
+ */
+ private static String classNameFromFile(String classFileName)
+ {
+ log.debug("private static String classNameFromFile(String classFileName = " + classFileName + "): called");
+
+ // Remove the .class ending.
+ String s = classFileName.substring(0, classFileName.length() - ".class".length());
+
+ // Turn / seperators in . seperators.
+ String s2 = s.replace(File.separatorChar, '.');
+
+ // Knock off any leading . caused by a leading /.
+ if (s2.startsWith("."))
+ {
+ return s2.substring(1);
+ }
+
+ return s2;
+ }
+}
diff --git a/Final/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java b/Final/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java
new file mode 100644
index 0000000000..a325e7025e
--- /dev/null
+++ b/Final/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java
@@ -0,0 +1,479 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.util;
+
+import org.apache.log4j.Logger;
+
+import javax.jms.*;
+
+import java.util.*;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A conversation helper, uses a message correlation id pattern to match up sent and received messages as a conversation
+ * over JMS messaging. Incoming message traffic is divided up by correlation id. Each id has a queue (behaviour dependant
+ * on the queue implementation). Clients of this de-multiplexer can wait on messages, defined by message correlation ids.
+ *
+ * <p/>One use of this is as a conversation synchronizer where multiple threads are carrying out conversations over a
+ * multiplexed messaging route. This can be usefull, as JMS sessions are not multi-threaded. Setting up the conversation
+ * with synchronous queues will allow these threads to be written in a synchronous style, but with their execution order
+ * governed by the asynchronous message flow. For example, something like the following code could run a multi-threaded
+ * conversation (the conversation methods can be called many times in parallel):
+ *
+ * <p/><pre>
+ * class Initiator
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, null,
+ * java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * initiateConversation()
+ * {
+ * try {
+ * // Exchange greetings.
+ * conversation.send(sendDestination, conversation.getSession().createTextMessage("Hello."));
+ * Message greeting = conversation.receive();
+ *
+ * // Exchange goodbyes.
+ * conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ * Message goodbye = conversation.receive();
+ * } finally {
+ * conversation.end();
+ * }
+ * }
+ * }
+ *
+ * class Responder
+ * {
+ * ConversationHelper conversation = new ConversationHelper(connection, receiveDestination,
+ * java.util.concurrent.LinkedBlockingQueue.class);
+ *
+ * respondToConversation()
+ * {
+ * try {
+ * // Exchange greetings.
+ * Message greeting = conversation.receive();
+ * conversation.send(conversation.getSession().createTextMessage("Hello."));
+ *
+ * // Exchange goodbyes.
+ * Message goodbye = conversation.receive();
+ * conversation.send(conversation.getSession().createTextMessage("Goodbye."));
+ * } finally {
+ * conversation.end();
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * <p/>Conversation correlation id's are generated on a per thread basis.
+ *
+ * <p/>The same controlSession is shared amongst all conversations. Calls to send are therefore synchronized because JMS
+ * sessions are not multi-threaded.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><th> Associate messages to an ongoing conversation using correlation ids.
+ * <tr><td> Auto manage sessions for conversations.
+ * <tr><td> Store messages not in a conversation in dead letter box.
+ * </table>
+ */
+public class ConversationFactory
+{
+ /** Used for debugging. */
+ private static final Logger log = Logger.getLogger(ConversationFactory.class);
+
+ /** Holds a map from correlation id's to queues. */
+ private Map<Long, BlockingQueue<Message>> idsToQueues = new HashMap<Long, BlockingQueue<Message>>();
+
+ /** Holds the connection over which the conversation is conducted. */
+ private Connection connection;
+
+ /** Holds the controlSession over which the conversation is conduxted. */
+ private Session session;
+
+ /** The message consumer for incoming messages. */
+ MessageConsumer consumer;
+
+ /** The message producer for outgoing messages. */
+ MessageProducer producer;
+
+ /** The well-known or temporary destination to receive replies on. */
+ Destination receiveDestination;
+
+ /** Holds the queue implementation class for the reply queue. */
+ Class<? extends BlockingQueue> queueClass;
+
+ /** Used to hold any replies that are received outside of the context of a conversation. */
+ BlockingQueue<Message> deadLetterBox = new LinkedBlockingQueue<Message>();
+
+ /* Used to hold conversation state on a per thread basis. */
+ /*
+ ThreadLocal<Conversation> threadLocals =
+ new ThreadLocal<Conversation>()
+ {
+ protected Conversation initialValue()
+ {
+ Conversation settings = new Conversation();
+ settings.conversationId = conversationIdGenerator.getAndIncrement();
+
+ return settings;
+ }
+ };
+ */
+
+ /** Generates new coversation id's as needed. */
+ AtomicLong conversationIdGenerator = new AtomicLong();
+
+ /**
+ * Creates a conversation helper on the specified connection with the default sending destination, and listening
+ * to the specified receiving destination.
+ *
+ * @param connection The connection to build the conversation helper on.
+ * @param receiveDestination The destination to listen to for incoming messages. This may be null to use a temporary
+ * queue.
+ * @param queueClass The queue implementation class.
+ *
+ * @throws JMSException All underlying JMSExceptions are allowed to fall through.
+ */
+ public ConversationFactory(Connection connection, Destination receiveDestination,
+ Class<? extends BlockingQueue> queueClass) throws JMSException
+ {
+ log.debug("public ConversationFactory(Connection connection, Destination receiveDestination = " + receiveDestination
+ + ", Class<? extends BlockingQueue> queueClass = " + queueClass + "): called");
+
+ this.connection = connection;
+ this.queueClass = queueClass;
+
+ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ // Check if a well-known receive destination has been provided, or use a temporary queue if not.
+ this.receiveDestination = (receiveDestination != null) ? receiveDestination : session.createTemporaryQueue();
+
+ consumer = session.createConsumer(receiveDestination);
+ producer = session.createProducer(null);
+
+ consumer.setMessageListener(new Receiver());
+ }
+
+ /**
+ * Creates a new conversation context.
+ *
+ * @return A new conversation context.
+ */
+ public Conversation startConversation()
+ {
+ log.debug("public Conversation startConversation(): called");
+
+ Conversation conversation = new Conversation();
+ conversation.conversationId = conversationIdGenerator.getAndIncrement();
+
+ return conversation;
+ }
+
+ /**
+ * Ensures that the reply queue for a conversation exists.
+ *
+ * @param conversationId The conversation correlation id.
+ */
+ private void initQueueForId(long conversationId)
+ {
+ if (!idsToQueues.containsKey(conversationId))
+ {
+ idsToQueues.put(conversationId, ReflectionUtils.<BlockingQueue>newInstance(queueClass));
+ }
+ }
+
+ /**
+ * Clears the dead letter box, returning all messages that were in it.
+ *
+ * @return All messages in the dead letter box.
+ */
+ public Collection<Message> emptyDeadLetterBox()
+ {
+ log.debug("public Collection<Message> emptyDeadLetterBox(): called");
+
+ Collection<Message> result = new ArrayList<Message>();
+ deadLetterBox.drainTo(result);
+
+ return result;
+ }
+
+ /**
+ * Gets the controlSession over which the conversation is conducted.
+ *
+ * @return The controlSession over which the conversation is conducted.
+ */
+ public Session getSession()
+ {
+ // Conversation settings = threadLocals.get();
+
+ return session;
+ }
+
+ /**
+ * Used to hold a conversation context. This consists of a correlating id for the conversation, and a reply
+ * destination automatically updated to the last received reply-to destination.
+ */
+ public class Conversation
+ {
+ /** Holds the correlation id for the context. */
+ long conversationId;
+
+ /**
+ * Holds the send destination for the context. This will automatically be updated to the most recently received
+ * reply-to destination.
+ */
+ Destination sendDestination;
+
+ /**
+ * Sends a message to the default sending location. The correlation id of the message will be assigned by this
+ * method, overriding any previously set value.
+ *
+ * @param sendDestination The destination to send to. This may be null to use the last received reply-to
+ * destination.
+ * @param message The message to send.
+ *
+ * @throws JMSException All undelying JMSExceptions are allowed to fall through. This will also be thrown if no
+ * send destination is specified and there is no most recent reply-to destination available
+ * to use.
+ */
+ public void send(Destination sendDestination, Message message) throws JMSException
+ {
+ log.debug("public void send(Destination sendDestination = " + sendDestination + ", Message message = " + message
+ + "): called");
+
+ // Conversation settings = threadLocals.get();
+ // long conversationId = conversationId;
+ message.setJMSCorrelationID(Long.toString(conversationId));
+ message.setJMSReplyTo(receiveDestination);
+
+ // Ensure that the reply queue for this conversation exists.
+ initQueueForId(conversationId);
+
+ // Check if an overriding send to destination has been set or use the last reply-to if not.
+ Destination sendTo = null;
+
+ if (sendDestination != null)
+ {
+ sendTo = sendDestination;
+ }
+ else if (sendDestination != null)
+ {
+ sendTo = sendDestination;
+ }
+ else
+ {
+ throw new JMSException("The send destination was specified, and no most recent reply-to available to use.");
+ }
+
+ // Send the message.
+ synchronized (this)
+ {
+ producer.send(sendTo, message);
+ }
+ }
+
+ /**
+ * Gets the next message in an ongoing conversation. This method may block until such a message is received.
+ *
+ * @return The next incoming message in the conversation.
+ *
+ * @throws JMSException All undelying JMSExceptions are allowed to fall through. Thrown if the received message
+ * did not have its reply-to destination set up.
+ */
+ public Message receive() throws JMSException
+ {
+ log.debug("public Message receive(): called");
+
+ // Conversation settings = threadLocals.get();
+ // long conversationId = settings.conversationId;
+
+ // Ensure that the reply queue for this conversation exists.
+ initQueueForId(conversationId);
+
+ BlockingQueue<Message> queue = idsToQueues.get(conversationId);
+
+ try
+ {
+ Message result = queue.take();
+
+ // Keep the reply-to destination to send replies to.
+ sendDestination = result.getJMSReplyTo();
+
+ return result;
+ }
+ catch (InterruptedException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are
+ * received they will be returned. If a timeout is specified, then all messages up to the limit, received within
+ * that timespan will be returned. At least one of the message count or timeout should be set to a value of
+ * 1 or greater.
+ *
+ * @param num The number of messages to receive, or all if this is less than 1.
+ * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1.
+ *
+ * @return All messages received within the count limit and the timeout.
+ *
+ * @throws JMSException All undelying JMSExceptions are allowed to fall through.
+ */
+ public Collection<Message> receiveAll(int num, long timeout) throws JMSException
+ {
+ log.debug("public Collection<Message> receiveAll(int num = " + num + ", long timeout = " + timeout
+ + "): called");
+
+ // Check that a timeout or message count was set.
+ if ((num < 1) && (timeout < 1))
+ {
+ throw new IllegalArgumentException("At least one of message count (num) or timeout must be set.");
+ }
+
+ // Ensure that the reply queue for this conversation exists.
+ initQueueForId(conversationId);
+ BlockingQueue<Message> queue = idsToQueues.get(conversationId);
+
+ // Used to collect the received messages in.
+ Collection<Message> result = new ArrayList<Message>();
+
+ // Used to indicate when the timeout or message count has expired.
+ boolean receiveMore = true;
+
+ int messageCount = 0;
+
+ // Receive messages until the timeout or message count expires.
+ do
+ {
+ try
+ {
+ Message next = null;
+
+ // Try to receive the message with a timeout if one has been set.
+ if (timeout > 0)
+ {
+ next = queue.poll(timeout, TimeUnit.MILLISECONDS);
+
+ // Check if the timeout expired, and stop receiving if so.
+ if (next == null)
+ {
+ receiveMore = false;
+ }
+ }
+ // Receive the message without a timeout.
+ else
+ {
+ next = queue.take();
+ }
+
+ // Increment the message count if a message was received.
+ messageCount += (next != null) ? 1 : 0;
+
+ // Check if all the requested messages were received, and stop receiving if so.
+ if ((num > 0) && (messageCount >= num))
+ {
+ receiveMore = false;
+ }
+
+ // Keep the reply-to destination to send replies to.
+ sendDestination = (next != null) ? next.getJMSReplyTo() : sendDestination;
+
+ if (next != null)
+ {
+ result.add(next);
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // Restore the threads interrupted status.
+ Thread.currentThread().interrupt();
+
+ // Stop receiving but return the messages received so far.
+ receiveMore = false;
+ }
+ }
+ while (receiveMore);
+
+ return result;
+ }
+
+ /**
+ * Completes the conversation. Any correlation id's pertaining to the conversation are no longer valid, and any
+ * incoming messages using them will go to the dead letter box.
+ */
+ public void end()
+ {
+ log.debug("public void end(): called");
+
+ // Ensure that the thread local for the current thread is cleaned up.
+ // Conversation settings = threadLocals.get();
+ // long conversationId = settings.conversationId;
+ // threadLocals.remove();
+
+ // Ensure that its queue is removed from the queue map.
+ BlockingQueue<Message> queue = idsToQueues.remove(conversationId);
+
+ // Move any outstanding messages on the threads conversation id into the dead letter box.
+ queue.drainTo(deadLetterBox);
+ }
+ }
+
+ /**
+ * Implements the message listener for this conversation handler.
+ */
+ protected class Receiver implements MessageListener
+ {
+ /**
+ * Handles all incoming messages in the ongoing conversations. These messages are split up by correaltion id
+ * and placed into queues.
+ *
+ * @param message The incoming message.
+ */
+ public void onMessage(Message message)
+ {
+ log.debug("public void onMessage(Message message = " + message + "): called");
+
+ try
+ {
+ Long conversationId = Long.parseLong(message.getJMSCorrelationID());
+
+ // Find the converstaion queue to place the message on. If there is no conversation for the message id,
+ // the the dead letter box queue is used.
+ BlockingQueue<Message> queue = idsToQueues.get(conversationId);
+ queue = (queue == null) ? deadLetterBox : queue;
+
+ queue.put(message);
+ }
+ catch (JMSException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/Final/java/systests/src/main/java/systests.log4j b/Final/java/systests/src/main/java/systests.log4j
new file mode 100644
index 0000000000..6d596d1d19
--- /dev/null
+++ b/Final/java/systests/src/main/java/systests.log4j
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootLogger=${root.logging.level}
+
+
+log4j.logger.org.apache.qpid=${amqj.logging.level}, console
+log4j.additivity.org.apache.qpid=false
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+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/Final/java/systests/src/old_test/java/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java b/Final/java/systests/src/old_test/java/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java
new file mode 100644
index 0000000000..ff0d58ad69
--- /dev/null
+++ b/Final/java/systests/src/old_test/java/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java
@@ -0,0 +1,184 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.NoConsumersException;
+import org.apache.qpid.server.util.TimedRun;
+import org.apache.qpid.server.util.AveragedRun;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ContentBody;
+
+import java.util.List;
+
+/**
+ * Want to vary the number of regsitrations, messages and matches and measure
+ * the corresponding variance in execution time.
+ * <p/>
+ * Each registration will contain the 'All' header, even registrations will
+ * contain the 'Even' header and odd headers will contain the 'Odd' header.
+ * In additions each regsitration will have a unique value for the 'Specific'
+ * header as well.
+ * <p/>
+ * Messages can then be routed to all registrations, to even- or odd- registrations
+ * or to a specific registration.
+ *
+ */
+public class HeadersExchangePerformanceTest extends AbstractHeadersExchangeTest
+{
+ private static enum Mode {ALL, ODD_OR_EVEN, SPECIFIC}
+
+ private final TestQueue[] queues;
+ private final Mode mode;
+
+ public HeadersExchangePerformanceTest(Mode mode, int registrations) throws AMQException
+ {
+ this.mode = mode;
+ queues = new TestQueue[registrations];
+ for (int i = 0; i < queues.length; i++)
+ {
+ switch(mode)
+ {
+ case ALL:
+ queues[i] = bind(new FastQueue("Queue" + i), "All");
+ break;
+ case ODD_OR_EVEN:
+ queues[i] = bind(new FastQueue("Queue" + i), "All", oddOrEven(i));
+ break;
+ case SPECIFIC:
+ queues[i] = bind(new FastQueue("Queue" + i), "All", oddOrEven(i), "Specific"+ i);
+ break;
+ }
+ }
+ }
+
+ void sendToAll(int count) throws AMQException
+ {
+ send(count, "All=True");
+ }
+
+ void sendToOdd(int count) throws AMQException
+ {
+ send(count, "All=True", "Odd=True");
+ }
+
+ void sendToEven(int count) throws AMQException
+ {
+ send(count, "All=True", "Even=True");
+ }
+
+ void sendToAllSpecifically(int count) throws AMQException
+ {
+ for (int i = 0; i < queues.length; i++)
+ {
+ sendToSpecific(count, i);
+ }
+ }
+
+ void sendToSpecific(int count, int index) throws AMQException
+ {
+ send(count, "All=True", oddOrEven(index) + "=True", "Specific=" + index);
+ }
+
+ private void send(int count, String... headers) throws AMQException
+ {
+ for (int i = 0; i < count; i++)
+ {
+ route(new Message("Message" + i, headers));
+ }
+ }
+
+ private static String oddOrEven(int i)
+ {
+ return (i % 2 == 0 ? "Even" : "Odd");
+ }
+
+ static class FastQueue extends TestQueue
+ {
+
+ public FastQueue(String name) throws AMQException
+ {
+ super(name);
+ }
+
+ public void deliver(BasicPublishBody publishBody, ContentHeaderBody contentHeaderBody, List<ContentBody> contentBodies) throws NoConsumersException
+ {
+ //just discard as we are not testing routing functionality here
+ }
+ }
+
+ static class Test extends TimedRun
+ {
+ private final Mode mode;
+ private final int registrations;
+ private final int count;
+ private HeadersExchangePerformanceTest test;
+
+ Test(Mode mode, int registrations, int count)
+ {
+ super(mode + ", registrations=" + registrations + ", count=" + count);
+ this.mode = mode;
+ this.registrations = registrations;
+ this.count = count;
+ }
+
+ protected void setup() throws Exception
+ {
+ test = new HeadersExchangePerformanceTest(mode, registrations);
+ run(100); //do a warm up run before times start
+ }
+
+ protected void teardown() throws Exception
+ {
+ test = null;
+ System.gc();
+ }
+
+ protected void run() throws Exception
+ {
+ run(count);
+ }
+
+ private void run(int count) throws Exception
+ {
+ switch(mode)
+ {
+ case ALL:
+ test.sendToAll(count);
+ break;
+ default:
+ System.out.println("Test for " + mode + " not yet implemented.");
+ }
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ int registrations = Integer.parseInt(argv[0]);
+ int messages = Integer.parseInt(argv[1]);
+ int iterations = Integer.parseInt(argv[2]);
+ TimedRun test = new Test(Mode.ALL, registrations, messages);
+ AveragedRun tests = new AveragedRun(test, iterations);
+ System.out.println(tests.call());
+ }
+}
+
diff --git a/Final/java/systests/src/old_test/java/org/apache/qpid/server/protocol/TestProtocolInitiation.java b/Final/java/systests/src/old_test/java/org/apache/qpid/server/protocol/TestProtocolInitiation.java
new file mode 100644
index 0000000000..e76c164f64
--- /dev/null
+++ b/Final/java/systests/src/old_test/java/org/apache/qpid/server/protocol/TestProtocolInitiation.java
@@ -0,0 +1,266 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.codec.AMQDecoder;
+import org.apache.qpid.codec.AMQEncoder;
+import org.apache.qpid.framing.*;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+import org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput;
+
+import junit.framework.TestCase;
+
+/**
+ * This test suite tests the handling of protocol initiation frames and related issues.
+ */
+public class TestProtocolInitiation extends TestCase implements ProtocolVersionList
+{
+ private AMQPFastProtocolHandler _protocolHandler;
+
+ private MockIoSession _mockIoSession;
+
+ /**
+ * We need to use the object encoder mechanism so to allow us to retrieve the
+ * output (a bytebuffer) we define our own encoder output class. The encoder
+ * writes the encoded data to this class, from where we can retrieve it during
+ * the test run.
+ */
+ private class TestProtocolEncoderOutput implements ProtocolEncoderOutput
+ {
+ public ByteBuffer result;
+
+ public void write(ByteBuffer buf)
+ {
+ result = buf;
+ }
+
+ public void mergeAll()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public WriteFuture flush()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private class TestProtocolDecoderOutput implements ProtocolDecoderOutput
+ {
+ public Object result;
+
+ public void write(Object buf)
+ {
+ result = buf;
+ }
+
+ public void flush()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _mockIoSession = new MockIoSession();
+ _protocolHandler = new AMQPFastProtocolHandler(null, null);
+ }
+
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol classes
+ * @throws Exception
+ */
+ public void testDecoderValidateProtocolClass() throws Exception
+ {
+ try
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolClass = 2;
+ decodePI(pi);
+ fail("expected exception did not occur");
+ }
+ catch (AMQProtocolClassException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected AMQProtocolClassException, got " + e);
+ }
+ }
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol instance numbers
+ * @throws Exception
+ */
+ public void testDecoderValidatesProtocolInstance() throws Exception
+ {
+ try
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolInstance = 2;
+ decodePI(pi);
+ fail("expected exception did not occur");
+ }
+ catch (AMQProtocolInstanceException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected AMQProtocolInstanceException, got " + e);
+ }
+ }
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol major
+ * @throws Exception
+ */
+ public void testDecoderValidatesProtocolMajor() throws Exception
+ {
+ try
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolMajor = 2;
+ decodePI(pi);
+ fail("expected exception did not occur");
+ }
+ catch (AMQProtocolVersionException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected AMQProtocolVersionException, got " + e);
+ }
+ }
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol minor
+ * @throws Exception
+ */
+ public void testDecoderValidatesProtocolMinor() throws Exception
+ {
+ try
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolMinor = 99;
+ decodePI(pi);
+ fail("expected exception did not occur");
+ }
+ catch (AMQProtocolVersionException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected AMQProtocolVersionException, got " + e);
+ }
+ }
+
+ /**
+ * Tests that the AMQDecoder accepts a valid PI
+ * @throws Exception
+ */
+ public void testDecoderValidatesHeader() throws Exception
+ {
+ try
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.header = new char[] {'P', 'Q', 'M', 'A' };
+ decodePI(pi);
+ fail("expected exception did not occur");
+ }
+ catch (AMQProtocolHeaderException m)
+ {
+ // ok
+ }
+ catch (Exception e)
+ {
+ fail("expected AMQProtocolHeaderException, got " + e);
+ }
+ }
+
+ /**
+ * Test that a valid header is passed by the decoder.
+ * @throws Exception
+ */
+ public void testDecoderAcceptsValidHeader() throws Exception
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ decodePI(pi);
+ }
+
+ /**
+ * This test checks that an invalid protocol header results in the
+ * connection being closed.
+ */
+ public void testInvalidProtocolHeaderClosesConnection() throws Exception
+ {
+ AMQProtocolHeaderException pe = new AMQProtocolHeaderException("Test");
+ _protocolHandler.exceptionCaught(_mockIoSession, pe);
+ assertNotNull(_mockIoSession.getLastWrittenObject());
+ Object piResponse = _mockIoSession.getLastWrittenObject();
+ assertEquals(piResponse.getClass(), ProtocolInitiation.class);
+ ProtocolInitiation pi = (ProtocolInitiation) piResponse;
+ assertEquals("Protocol Initiation sent out was not the broker's expected header", pi,
+ createValidProtocolInitiation());
+ assertTrue("Session has not been closed", _mockIoSession.isClosing());
+ }
+
+ private ProtocolInitiation createValidProtocolInitiation()
+ {
+ /* 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. */
+ int i = pv.length - 1;
+ return new ProtocolInitiation(pv[i][PROTOCOL_MAJOR], pv[i][PROTOCOL_MINOR]);
+ }
+
+ /**
+ * Helper that encodes a protocol initiation and attempts to decode it
+ * @param pi
+ * @throws Exception
+ */
+ private void decodePI(ProtocolInitiation pi) throws Exception
+ {
+ // we need to do this test at the level of the decoder since we initially only expect PI frames
+ // so the protocol handler is not set up to know whether it should be expecting a PI frame or
+ // a different type of frame
+ AMQDecoder decoder = new AMQDecoder(true);
+ AMQEncoder encoder = new AMQEncoder();
+ TestProtocolEncoderOutput peo = new TestProtocolEncoderOutput();
+ encoder.encode(_mockIoSession, pi, peo);
+ TestProtocolDecoderOutput pdo = new TestProtocolDecoderOutput();
+ decoder.decode(_mockIoSession, peo.result, pdo);
+ ((ProtocolInitiation) pdo.result).checkVersion(this);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(TestProtocolInitiation.class);
+ }
+}
diff --git a/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java b/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java
new file mode 100644
index 0000000000..11c0026455
--- /dev/null
+++ b/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueueConcurrentPerfTest.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.server.queue;
+
+import org.apache.qpid.server.util.AveragedRun;
+import org.apache.qpid.server.util.ConcurrentTest;
+
+public class QueueConcurrentPerfTest extends QueuePerfTest
+{
+ QueueConcurrentPerfTest(Factory factory, int queueCount, int messages)
+ {
+ super(factory, queueCount, messages);
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Factory[] factories = new Factory[]{SYNCHRONIZED, CONCURRENT};
+ int iterations = 5;
+ String label = argv.length > 0 ? argv[0]: null;
+ System.out.println((label == null ? "" : "Label, ") + "Queue Type, No. of Queues, No. of Operations, Avg Time, Min Time, Max Time");
+ //vary number of queues:
+ for(Factory f : factories)
+ {
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 100, 10000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 1000, 10000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 10000, 10000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 1000, 1000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 1000, 100000), iterations), 5));
+ }
+ }
+}
diff --git a/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueuePerfTest.java b/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueuePerfTest.java
new file mode 100644
index 0000000000..5b3857396d
--- /dev/null
+++ b/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueuePerfTest.java
@@ -0,0 +1,258 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util.AveragedRun;
+import org.apache.qpid.server.util.TimedRun;
+import org.apache.qpid.server.util.RunStats;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class QueuePerfTest extends TimedRun
+{
+ private final Factory _factory;
+ private final int _queueCount;
+ private final int _messages;
+ private final String _msg = "";
+ private List<Queue<String>> _queues;
+
+ QueuePerfTest(Factory factory, int queueCount, int messages)
+ {
+ super(factory + ", " + queueCount + ", " + messages);
+ _factory = factory;
+ _queueCount = queueCount;
+ _messages = messages;
+ }
+
+ protected void setup() throws Exception
+ {
+ //init
+ int count = Integer.getInteger("prepopulate", 0);
+// System.err.println("Prepopulating with " + count + " items");
+ _queues = new ArrayList<Queue<String>>(_queueCount);
+ for (int i = 0; i < _queueCount; i++)
+ {
+ Queue<String> q = _factory.create();
+ for(int j = 0; j < count; ++j)
+ {
+ q.add("Item"+ j);
+ }
+ _queues.add(q);
+ }
+ System.gc();
+ }
+
+ protected void teardown() throws Exception
+ {
+ System.gc();
+ }
+
+ protected void run() throws Exception
+ {
+ //dispatch
+ for (int i = 0; i < _messages; i++)
+ {
+ for (Queue<String> q : _queues)
+ {
+ q.offer(_msg);
+ q.poll();
+ }
+ }
+ }
+
+ static interface Factory
+ {
+ Queue<String> create();
+ }
+
+ static Factory CONCURRENT = new Factory()
+ {
+ public Queue<String> create()
+ {
+ return new ConcurrentLinkedQueue<String>();
+ }
+
+ public String toString()
+ {
+ return "ConcurrentLinkedQueue";
+ }
+
+ };
+
+ static Factory SYNCHRONIZED = new Factory()
+ {
+ public Queue<String> create()
+ {
+ return new SynchronizedQueue<String>(new LinkedList<String>());
+ }
+
+
+ public String toString()
+ {
+ return "Synchronized LinkedList";
+ }
+ };
+
+ static Factory PLAIN = new Factory()
+ {
+ public Queue<String> create()
+ {
+ return new LinkedList<String>();
+ }
+
+ public String toString()
+ {
+ return "Plain LinkedList";
+ }
+ };
+
+ static class SynchronizedQueue<E> implements Queue<E>
+ {
+ private final Queue<E> queue;
+
+ SynchronizedQueue(Queue<E> queue)
+ {
+ this.queue = queue;
+ }
+
+ public synchronized E element()
+ {
+ return queue.element();
+ }
+
+ public synchronized boolean offer(E o)
+ {
+ return queue.offer(o);
+ }
+
+ public synchronized E peek()
+ {
+ return queue.peek();
+ }
+
+ public synchronized E poll()
+ {
+ return queue.poll();
+ }
+
+ public synchronized E remove()
+ {
+ return queue.remove();
+ }
+
+ public synchronized int size()
+ {
+ return queue.size();
+ }
+
+ public synchronized boolean isEmpty()
+ {
+ return queue.isEmpty();
+ }
+
+ public synchronized boolean contains(Object o)
+ {
+ return queue.contains(o);
+ }
+
+ public synchronized Iterator<E> iterator()
+ {
+ return queue.iterator();
+ }
+
+ public synchronized Object[] toArray()
+ {
+ return queue.toArray();
+ }
+
+ public synchronized <T>T[] toArray(T[] a)
+ {
+ return queue.toArray(a);
+ }
+
+ public synchronized boolean add(E o)
+ {
+ return queue.add(o);
+ }
+
+ public synchronized boolean remove(Object o)
+ {
+ return queue.remove(o);
+ }
+
+ public synchronized boolean containsAll(Collection<?> c)
+ {
+ return queue.containsAll(c);
+ }
+
+ public synchronized boolean addAll(Collection<? extends E> c)
+ {
+ return queue.addAll(c);
+ }
+
+ public synchronized boolean removeAll(Collection<?> c)
+ {
+ return queue.removeAll(c);
+ }
+
+ public synchronized boolean retainAll(Collection<?> c)
+ {
+ return queue.retainAll(c);
+ }
+
+ public synchronized void clear()
+ {
+ queue.clear();
+ }
+ }
+
+ static void run(String label, AveragedRun test) throws Exception
+ {
+ RunStats stats = test.call();
+ System.out.println((label == null ? "" : label + ", ") + test
+ + ", " + stats.getAverage() + ", " + stats.getMax() + ", " + stats.getMin());
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Factory[] factories = new Factory[]{PLAIN, SYNCHRONIZED, CONCURRENT};
+ int iterations = 5;
+ String label = argv.length > 0 ? argv[0]: null;
+ System.out.println((label == null ? "" : "Label, ") + "Queue Type, No. of Queues, No. of Operations, Avg Time, Min Time, Max Time");
+ //vary number of queues:
+
+ for(Factory f : factories)
+ {
+ run(label, new AveragedRun(new QueuePerfTest(f, 100, 10000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 1000, 10000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 10000, 10000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 1000, 1000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 1000, 100000), iterations));
+ }
+ }
+
+}
diff --git a/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/SendPerfTest.java b/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/SendPerfTest.java
new file mode 100644
index 0000000000..2c5712fd35
--- /dev/null
+++ b/Final/java/systests/src/old_test/java/org/apache/qpid/server/queue/SendPerfTest.java
@@ -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.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.txn.TransactionalContext;
+import org.apache.qpid.server.txn.NonTransactionalContext;
+import org.apache.qpid.server.exchange.AbstractExchange;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.handler.OnCurrentThreadExecutor;
+import org.apache.qpid.server.protocol.AMQMinaProtocolSession;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.MockIoSession;
+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.util.AveragedRun;
+import org.apache.qpid.server.util.TestApplicationRegistry;
+import org.apache.qpid.server.util.TimedRun;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.LinkedList;
+
+public class SendPerfTest extends TimedRun
+{
+ private int _messages = 1000;
+ private int _clients = 10;
+ private List<AMQQueue> _queues;
+
+ public SendPerfTest(int clients, int messages)
+ {
+ super("SendPerfTest, msgs=" + messages + ", clients=" + clients);
+ _messages = messages;
+ _clients = clients;
+ }
+
+ protected void setup() throws Exception
+ {
+ _queues = initQueues(_clients);
+ System.gc();
+ }
+
+ protected void teardown() throws Exception
+ {
+ System.gc();
+ }
+
+ protected void run() throws Exception
+ {
+ deliver(_messages, _queues);
+ }
+
+ //have a dummy AMQProtocolSession that does nothing on the writeFrame()
+ //set up x number of queues
+ //create necessary bits and pieces to deliver a message
+ //deliver y messages to each queue
+
+ public static void main(String[] argv) throws Exception
+ {
+ ApplicationRegistry.initialise(new TestApplicationRegistry());
+ int clients = Integer.parseInt(argv[0]);
+ int messages = Integer.parseInt(argv[1]);
+ int iterations = Integer.parseInt(argv[2]);
+ AveragedRun test = new AveragedRun(new SendPerfTest(clients, messages), iterations);
+ test.run();
+ }
+
+ /**
+ * Delivers messages to a number of queues.
+ * @param count the number of messages to deliver
+ * @param queues the list of queues
+ * @throws NoConsumersException
+ */
+ static void deliver(int count, List<AMQQueue> queues) throws AMQException
+ {
+ BasicPublishBody publish = new BasicPublishBody();
+ publish.exchange = new NullExchange().getName();
+ ContentHeaderBody header = new ContentHeaderBody();
+ List<ContentBody> body = new ArrayList<ContentBody>();
+ MessageStore messageStore = new SkeletonMessageStore();
+ // channel can be null since it is only used in ack processing which does not apply to this test
+ TransactionalContext txContext = new NonTransactionalContext(messageStore, null,
+ new LinkedList<RequiredDeliveryException>());
+ body.add(new ContentBody());
+ MessageHandleFactory factory = new MessageHandleFactory();
+ for (int i = 0; i < count; i++)
+ {
+ // this routes and delivers the message
+ AMQMessage msg = new AMQMessage(i, publish, txContext, header, queues, body, messageStore,
+ factory);
+ }
+ }
+
+ static List<AMQQueue> initQueues(int number) throws AMQException
+ {
+ Exchange exchange = new NullExchange();
+ List<AMQQueue> queues = new ArrayList<AMQQueue>(number);
+ for (int i = 0; i < number; i++)
+ {
+ AMQQueue q = createQueue("Queue" + (i + 1));
+ q.bind("routingKey", exchange);
+ try
+ {
+ q.registerProtocolSession(createSession(), 1, "1", false);
+ }
+ catch (Exception e)
+ {
+ throw new AMQException("Error creating protocol session: " + e, e);
+ }
+ queues.add(q);
+ }
+ return queues;
+ }
+
+ static AMQQueue createQueue(String name) throws AMQException
+ {
+ return new AMQQueue(name, false, null, false, ApplicationRegistry.getInstance().getQueueRegistry(),
+ new OnCurrentThreadExecutor());
+ }
+
+ static AMQProtocolSession createSession() throws Exception
+ {
+ IApplicationRegistry reg = ApplicationRegistry.getInstance();
+ AMQCodecFactory codecFactory = new AMQCodecFactory(true);
+ AMQMinaProtocolSession result = new AMQMinaProtocolSession(new MockIoSession(), reg.getQueueRegistry(), reg.getExchangeRegistry(), codecFactory);
+ result.addChannel(new AMQChannel(1, null, null));
+ return result;
+ }
+
+ static class NullExchange extends AbstractExchange
+ {
+ public String getName()
+ {
+ return "NullExchange";
+ }
+
+ protected ExchangeMBean createMBean()
+ {
+ return null;
+ }
+
+ public void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ }
+
+ public void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException
+ {
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ }
+ }
+}
diff --git a/Final/java/systests/src/old_test/java/org/apache/qpid/server/util/ConcurrentTest.java b/Final/java/systests/src/old_test/java/org/apache/qpid/server/util/ConcurrentTest.java
new file mode 100644
index 0000000000..1ae8d3205d
--- /dev/null
+++ b/Final/java/systests/src/old_test/java/org/apache/qpid/server/util/ConcurrentTest.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util;
+
+public class ConcurrentTest extends TimedRun
+{
+ private final TimedRun _test;
+ private final Thread[] _threads;
+
+ public ConcurrentTest(TimedRun test, int threads)
+ {
+ super(test.toString());
+ _test = test;
+ _threads = new Thread[threads];
+ }
+
+ protected void setup() throws Exception
+ {
+ _test.setup();
+ for(int i = 0; i < _threads.length; i++)
+ {
+ _threads[i] = new Thread(new Runner());
+ }
+ }
+
+ protected void teardown() throws Exception
+ {
+ _test.teardown();
+ }
+
+ protected void run() throws Exception
+ {
+ for(Thread t : _threads)
+ {
+ t.start();
+ }
+ for(Thread t : _threads)
+ {
+ t.join();
+ }
+ }
+
+ private class Runner implements Runnable
+ {
+ private Exception error;
+
+ public void run()
+ {
+ try
+ {
+ _test.run();
+ }
+ catch(Exception e)
+ {
+ error = e;
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/Final/java/systests/src/old_test/java/org/apache/qpid/test/unit/ack/DisconnectAndRedeliverTest.java b/Final/java/systests/src/old_test/java/org/apache/qpid/test/unit/ack/DisconnectAndRedeliverTest.java
new file mode 100644
index 0000000000..3e35e3c85b
--- /dev/null
+++ b/Final/java/systests/src/old_test/java/org/apache/qpid/test/unit/ack/DisconnectAndRedeliverTest.java
@@ -0,0 +1,215 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.test.unit.ack;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Logger;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.qpid.client.AMQConnection;
+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.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.util.TestApplicationRegistry;
+
+import javax.jms.*;
+
+public class DisconnectAndRedeliverTest extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(DisconnectAndRedeliverTest.class);
+
+ static
+ {
+ String workdir = System.getProperty("QPID_WORK");
+ if (workdir == null || workdir.equals(""))
+ {
+ String tempdir = System.getProperty("java.io.tmpdir");
+ System.out.println("QPID_WORK not set using tmp directory: " + tempdir);
+ System.setProperty("QPID_WORK", tempdir);
+ }
+ DOMConfigurator.configure("../broker/etc/log4j.xml");
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ TransportConnection.createVMBroker(1);
+ ApplicationRegistry.initialise(new TestApplicationRegistry(), 1);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ TransportConnection.killAllVMBrokers();
+ }
+
+ /**
+ * This tests that when there are unacknowledged messages on a channel they are requeued for delivery when
+ * the channel is closed.
+ *
+ * @throws Exception
+ */
+ public void testDisconnectRedeliversMessages() throws Exception
+ {
+ Connection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test");
+
+ TestableMemoryMessageStore store = (TestableMemoryMessageStore) ApplicationRegistry.getInstance().getMessageStore();
+
+ Session consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ AMQQueue queue = new AMQQueue("someQ", "someQ", false, false);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ //force synch to ensure the consumer has resulted in a bound queue
+ ((AMQSession) consumerSession).declareExchangeSynch("amq.direct", "direct");
+
+ Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "/test");
+
+
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+
+ _logger.info("Sending four messages");
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer.send(producerSession.createTextMessage("msg2"));
+ producer.send(producerSession.createTextMessage("msg3"));
+ producer.send(producerSession.createTextMessage("msg4"));
+
+ con2.close();
+
+ _logger.info("Starting connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive();
+ tm.acknowledge();
+ _logger.info("Received and acknowledged first message");
+ consumer.receive();
+ consumer.receive();
+ consumer.receive();
+ _logger.info("Received all four messages. About to disconnect and reconnect");
+
+ con.close();
+ con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test");
+ consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ consumer = consumerSession.createConsumer(queue);
+
+ _logger.info("Starting second consumer connection");
+ con.start();
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertEquals("msg2", tm.getText());
+
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertEquals("msg3", tm.getText());
+
+
+ tm = (TextMessage) consumer.receive(3000);
+ assertEquals("msg4", tm.getText());
+
+ _logger.info("Received redelivery of three messages. Acknowledging last message");
+ tm.acknowledge();
+
+ con.close();
+
+ con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test");
+ consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ consumer = consumerSession.createConsumer(queue);
+ _logger.info("Starting third consumer connection");
+ con.start();
+ tm = (TextMessage) consumer.receiveNoWait();
+ assertNull(tm);
+ _logger.info("No messages redelivered as is expected");
+ con.close();
+
+ con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test");
+ consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ consumer = consumerSession.createConsumer(queue);
+ _logger.info("Starting fourth consumer connection");
+ con.start();
+ tm = (TextMessage) consumer.receive(3000);
+ assertNull(tm);
+ _logger.info("No messages redelivered as is expected");
+ con.close();
+
+ _logger.info("Actually:" + store.getMessageMetaDataMap().size());
+ // assertTrue(store.getMessageMap().size() == 0);
+ }
+
+ /**
+ * Tests that unacknowledged messages are thrown away when the channel is closed and they cannot be
+ * requeued (due perhaps to the queue being deleted).
+ *
+ * @throws Exception
+ */
+ public void testDisconnectWithTransientQueueThrowsAwayMessages() throws Exception
+ {
+
+ Connection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test");
+ TestableMemoryMessageStore store = (TestableMemoryMessageStore) ApplicationRegistry.getInstance().getMessageStore();
+ Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ Queue queue = new AMQQueue("someQ", "someQ", false, true);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+ //force synch to ensure the consumer has resulted in a bound queue
+ ((AMQSession) consumerSession).declareExchangeSynch("amq.direct", "direct");
+
+ Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "/test");
+ Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(queue);
+
+ _logger.info("Sending four messages");
+ producer.send(producerSession.createTextMessage("msg1"));
+ producer.send(producerSession.createTextMessage("msg2"));
+ producer.send(producerSession.createTextMessage("msg3"));
+ producer.send(producerSession.createTextMessage("msg4"));
+
+ con2.close();
+
+ _logger.info("Starting connection");
+ con.start();
+ TextMessage tm = (TextMessage) consumer.receive();
+ tm.acknowledge();
+ _logger.info("Received and acknowledged first message");
+ consumer.receive();
+ consumer.receive();
+ consumer.receive();
+ _logger.info("Received all four messages. About to disconnect and reconnect");
+
+ con.close();
+ con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "/test");
+ consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ consumer = consumerSession.createConsumer(queue);
+
+ _logger.info("Starting second consumer connection");
+ con.start();
+
+ tm = (TextMessage) consumer.receiveNoWait();
+ assertNull(tm);
+ _logger.info("No messages redelivered as is expected");
+
+ _logger.info("Actually:" + store.getMessageMetaDataMap().size());
+ assertTrue(store.getMessageMetaDataMap().size() == 0);
+ con.close();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new junit.framework.TestSuite(DisconnectAndRedeliverTest.class);
+ }
+}